create-fesd-app 1.0.21 → 1.0.23
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/package.json +2 -2
- package/src/assets/css/base/_common.sass +3 -0
- package/src/assets/css/layouts/_cookiePolicy.sass +2 -3
- package/src/assets/css/layouts/_navbar.sass +1 -1
- package/src/assets/js/commons/cookies/cookieData.js +2 -3
- package/src/assets/js/commons/cookies/cookieElement.js +2 -2
- package/src/assets/js/commons/cookies/cookiePolicy.js +290 -229
- package/src/assets/js/commons/inits.js +2 -2
- package/src/layouts/_navbar.pug +3 -1
- package/src/layouts/_template.pug +5 -0
- package/src/layouts/components/_cookiePolicy.pug +16 -6
- package/src/pages/index.pug +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-fesd-app",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.23",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "vite",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@tailwindcss/postcss": "^4.1.10",
|
|
26
26
|
"@tailwindcss/vite": "^4.1.10",
|
|
27
|
-
"@xwadex/fesd": "0.0.
|
|
27
|
+
"@xwadex/fesd": "0.0.46",
|
|
28
28
|
"ansi-colors": "^4.1.3",
|
|
29
29
|
"chalk": "^5.3.0",
|
|
30
30
|
"clsx": "^2.1.1",
|
|
@@ -97,7 +97,6 @@
|
|
|
97
97
|
display: flex
|
|
98
98
|
align-items: center
|
|
99
99
|
gap: 15px
|
|
100
|
-
cursor: pointer
|
|
101
100
|
+rwdmax(767)
|
|
102
101
|
.cookie-main
|
|
103
102
|
padding: 50px 25px
|
|
@@ -309,7 +308,7 @@
|
|
|
309
308
|
color: #4B4B4B
|
|
310
309
|
pointer-events: none
|
|
311
310
|
transform: translate3d(calc(-100% - 10px),0,0)
|
|
312
|
-
content:
|
|
311
|
+
content: var(--switch-closeText)
|
|
313
312
|
&::before
|
|
314
313
|
content: ''
|
|
315
314
|
position: absolute
|
|
@@ -331,7 +330,7 @@
|
|
|
331
330
|
transform: translateX(18px)
|
|
332
331
|
background: #fff
|
|
333
332
|
input:checked + .switch::after
|
|
334
|
-
content:
|
|
333
|
+
content: var(--switch-openText)
|
|
335
334
|
input:checked + .switch:active::before
|
|
336
335
|
box-shadow: 0 2px 8px rgba(0,0,0,0.28), 0 0 0 20px rgba(128,128,128,0.1)
|
|
337
336
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// 此處為 demo 資料, 後端會用相同資料結構洗資料在 #_wcookie 上
|
|
2
2
|
// 給後端: 基本上進階版細項開關不開, 有需求才開; 若開啟進階版細項, 大分類的 close 開關預設會無效, 會以判斷細項是否有開啟 or 全關為主
|
|
3
3
|
// close: 0 -> 開啟; close: 1 -> 關閉
|
|
4
|
+
// 說明文件
|
|
4
5
|
export const cookieData = [
|
|
5
6
|
{
|
|
6
|
-
multiType: true, //
|
|
7
|
-
title: "您的 Cookie 偏好設定",
|
|
8
|
-
text: "我們使用不同類型的 Cookies 來優化您在我們的網站上的體驗,點擊下面類別以了解其目的與更多訊息。<br>您可以選擇允許的 Cookies 類型,也可以隨時更改您的偏好設定。<br>提醒您,停用 Cookies 可能會影響您在網站上的體驗,您可以瀏覽我們的隱私權政策,進一步了解我們如何使用 Cookie。",
|
|
7
|
+
multiType: true, // 進階版時是否開啟個別選項設定
|
|
9
8
|
options: [
|
|
10
9
|
{
|
|
11
10
|
key: "required",
|
|
@@ -159,7 +159,7 @@ export const cookieStep2ListCollapseEl = (
|
|
|
159
159
|
num
|
|
160
160
|
)=> {
|
|
161
161
|
const isDisabled = li.disabled ? " disabled" : ""
|
|
162
|
-
const isListChecked = cookieNewObj[parentKey].list[li.key].close == 0 ? "checked" : ""
|
|
162
|
+
const isListChecked = cookieNewObj[parentKey].list?.[li.key].close == 0 ? "checked" : ""
|
|
163
163
|
return `
|
|
164
164
|
<div class="list collapseItem list-option ${isListChecked}" data-collapse>
|
|
165
165
|
<div class="title collapseTitle" data-collapse-click>
|
|
@@ -223,7 +223,7 @@ export const cookieStep2ListNormalEl = (
|
|
|
223
223
|
index,
|
|
224
224
|
parentKey
|
|
225
225
|
) => {
|
|
226
|
-
const isListChecked = cookieNewObj[parentKey].list[li.key].close == 0 ? "checked" : ""
|
|
226
|
+
const isListChecked = cookieNewObj[parentKey].list?.[li.key].close == 0 ? "checked" : ""
|
|
227
227
|
return `
|
|
228
228
|
<div class="list list-option ${isListChecked}">
|
|
229
229
|
<div class="title">
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
methods
|
|
3
|
-
} from "@/commons"
|
|
3
|
+
} from "@/commons"
|
|
4
4
|
import {
|
|
5
5
|
scrollLock,
|
|
6
6
|
scrollUnlock
|
|
7
|
-
} from '@xwadex/fesd/tools'
|
|
8
|
-
import Cookies from "js-cookie"
|
|
9
|
-
import { OverlayScrollbars } from 'overlayscrollbars'
|
|
10
|
-
import { cookieData } from "./cookieData"
|
|
7
|
+
} from '@xwadex/fesd/tools'
|
|
8
|
+
import Cookies from "js-cookie"
|
|
9
|
+
import { OverlayScrollbars } from 'overlayscrollbars'
|
|
10
|
+
import { cookieData } from "./cookieData"
|
|
11
11
|
import { cookieStep1El, cookieStep2TittleEl, cookieStep2ContentEl } from "./cookieElement"
|
|
12
12
|
|
|
13
13
|
// 基礎版 cookie
|
|
@@ -17,103 +17,87 @@ const cookieAdvanced = {}
|
|
|
17
17
|
const cookieSetting = {}
|
|
18
18
|
const cookieElements = {}
|
|
19
19
|
// 設定一個預存空間
|
|
20
|
-
let cookieNewObj = {}
|
|
20
|
+
let cookieNewObj = {}
|
|
21
21
|
|
|
22
22
|
// 簡易版 cookie 設定
|
|
23
|
-
cookieBasic.basicCookiePolicy = () => {
|
|
24
|
-
const $cookiePolicy = $(".cookie-check")
|
|
25
|
-
if (!$cookiePolicy.length) return
|
|
26
|
-
|
|
27
|
-
const $agreeBtn = $cookiePolicy.find(".agree")
|
|
28
|
-
const $closeBtn = $cookiePolicy.find(".close")
|
|
23
|
+
cookieBasic.basicCookiePolicy = (el) => {
|
|
24
|
+
const $cookiePolicy = $(".cookie-check")
|
|
25
|
+
if (!$cookiePolicy.length) return
|
|
26
|
+
|
|
27
|
+
const $agreeBtn = $cookiePolicy.find(".agree")
|
|
28
|
+
const $closeBtn = $cookiePolicy.find(".close")
|
|
29
29
|
|
|
30
30
|
const firstEnterDetect = () => {
|
|
31
31
|
// 請記得更改 cookie 存入的值
|
|
32
|
-
const agreeLocal = localStorage.getItem(
|
|
33
|
-
const agreeSession = sessionStorage.getItem(
|
|
34
|
-
|
|
32
|
+
const agreeLocal = localStorage.getItem(el)
|
|
33
|
+
const agreeSession = sessionStorage.getItem(el)
|
|
34
|
+
|
|
35
35
|
if (agreeLocal !== "true" && agreeSession !== "false") {
|
|
36
|
-
$cookiePolicy.addClass("first-enter")
|
|
36
|
+
$cookiePolicy.addClass("first-enter")
|
|
37
37
|
setTimeout(() => {
|
|
38
|
-
$cookiePolicy.addClass("show")
|
|
39
|
-
}, 800)
|
|
38
|
+
$cookiePolicy.addClass("show")
|
|
39
|
+
}, 800)
|
|
40
40
|
}
|
|
41
|
-
}
|
|
42
|
-
|
|
41
|
+
}
|
|
42
|
+
|
|
43
43
|
// 點擊事件
|
|
44
44
|
const clickEvent = () => {
|
|
45
45
|
$agreeBtn.on("click", () => {
|
|
46
|
-
$cookiePolicy.addClass("agree")
|
|
47
|
-
localStorage.setItem(
|
|
48
|
-
|
|
49
|
-
if (typeof window._wdSetting === 'function') {
|
|
50
|
-
window._wdSetting()
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
$closeBtn.on("click", () => {
|
|
55
|
-
$cookiePolicy.addClass("hidden");
|
|
56
|
-
sessionStorage.setItem("frameworkCookie-agree", "false");
|
|
57
|
-
if (typeof window._wdSetting === 'function') {
|
|
58
|
-
window._wdSetting()
|
|
59
|
-
}
|
|
60
|
-
});
|
|
46
|
+
$cookiePolicy.addClass("agree")
|
|
47
|
+
localStorage.setItem(el, "true")
|
|
48
|
+
})
|
|
61
49
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
$cookiePolicy.removeClass("agree");
|
|
66
|
-
$cookiePolicy.removeClass("hidden");
|
|
67
|
-
setTimeout(() => {
|
|
68
|
-
$cookiePolicy.addClass("show");
|
|
69
|
-
}, 800);
|
|
50
|
+
$closeBtn.on("click", () => {
|
|
51
|
+
$cookiePolicy.addClass("hidden")
|
|
52
|
+
sessionStorage.setItem(el, "false")
|
|
70
53
|
})
|
|
71
|
-
}
|
|
54
|
+
}
|
|
72
55
|
|
|
73
56
|
// 執行事件
|
|
74
|
-
firstEnterDetect()
|
|
75
|
-
clickEvent()
|
|
76
|
-
}
|
|
57
|
+
firstEnterDetect()
|
|
58
|
+
clickEvent()
|
|
59
|
+
}
|
|
77
60
|
|
|
78
61
|
// 產生新物件
|
|
79
62
|
cookieAdvanced.newObj = () => {
|
|
80
|
-
if (Cookies.get('_wd') !== undefined) {
|
|
63
|
+
if (Cookies.get('_wd') !== undefined && $("#_wcookie").val()) {
|
|
81
64
|
const cloudDdata = JSON.parse(Cookies.get('_wd'))
|
|
82
65
|
cookieNewObj = { ...cloudDdata }
|
|
83
66
|
} else {
|
|
84
67
|
const data = $("#_wcookie").val() ? JSON.parse($("#_wcookie").val())[0] : cookieData[0]
|
|
85
|
-
const options = data?.options
|
|
68
|
+
const options = data?.options
|
|
69
|
+
|
|
86
70
|
if (options) {
|
|
87
71
|
// 判斷是否全開 or 全關 (排除 required)
|
|
88
|
-
const nonRequiredOptions = options.filter(opt => opt.key !== 'required')
|
|
89
|
-
const allNonRequiredAreFalse = nonRequiredOptions.every(opt => opt.close === 1)
|
|
90
|
-
const globalCloseValue = allNonRequiredAreFalse == 1 ? 1 : 0
|
|
91
|
-
|
|
92
|
-
cookieNewObj.close = globalCloseValue
|
|
72
|
+
const nonRequiredOptions = options.filter(opt => opt.key !== 'required')
|
|
73
|
+
const allNonRequiredAreFalse = nonRequiredOptions.every(opt => opt.close === 1)
|
|
74
|
+
const globalCloseValue = allNonRequiredAreFalse == 1 ? 1 : 0
|
|
75
|
+
|
|
76
|
+
cookieNewObj.close = globalCloseValue
|
|
93
77
|
|
|
94
78
|
const categoriesObject = options.reduce((acc, option) => {
|
|
95
|
-
const topLevelKey = option.key
|
|
96
|
-
const dataItems = option.data?.items
|
|
79
|
+
const topLevelKey = option.key
|
|
80
|
+
const dataItems = option.data?.items
|
|
97
81
|
|
|
98
|
-
acc[topLevelKey] = { close: option.close }
|
|
82
|
+
acc[topLevelKey] = { close: option.close }
|
|
99
83
|
|
|
100
84
|
if (data.multiType && dataItems !== undefined && dataItems.length > 0) {
|
|
101
|
-
acc[topLevelKey].list = {}
|
|
85
|
+
acc[topLevelKey].list = {}
|
|
102
86
|
|
|
103
87
|
dataItems.forEach(item => {
|
|
104
88
|
item.list?.forEach(listItem => {
|
|
105
89
|
if (listItem.key) {
|
|
106
90
|
acc[topLevelKey].list[listItem.key] = {
|
|
107
91
|
close: listItem.close
|
|
108
|
-
}
|
|
92
|
+
}
|
|
109
93
|
}
|
|
110
|
-
})
|
|
111
|
-
})
|
|
94
|
+
})
|
|
95
|
+
})
|
|
112
96
|
}
|
|
113
|
-
return acc
|
|
114
|
-
}, {})
|
|
97
|
+
return acc
|
|
98
|
+
}, {})
|
|
115
99
|
// 若無 cookie 紀錄可抓, 直接新建一個新的物件預存
|
|
116
|
-
Object.assign(cookieNewObj, categoriesObject)
|
|
100
|
+
Object.assign(cookieNewObj, categoriesObject)
|
|
117
101
|
}
|
|
118
102
|
}
|
|
119
103
|
}
|
|
@@ -129,9 +113,6 @@ export const generateHtmlString = (target, fn) => {
|
|
|
129
113
|
|
|
130
114
|
// 產資料結構
|
|
131
115
|
cookieAdvanced.dataAppend = () => {
|
|
132
|
-
const $block = $(".cookie-setting")
|
|
133
|
-
if (!$block.length) return
|
|
134
|
-
|
|
135
116
|
const val = $("#_wcookie").val()
|
|
136
117
|
const data = val ? JSON.parse(val)[0] : cookieData[0]
|
|
137
118
|
const $step1 = $(".setting-container[t4-id='step1']")
|
|
@@ -142,7 +123,7 @@ cookieAdvanced.dataAppend = () => {
|
|
|
142
123
|
generateHtmlString(data.options, cookieElements.getStep1Element)
|
|
143
124
|
)
|
|
144
125
|
|
|
145
|
-
const step2OpenClick = function() {
|
|
126
|
+
const step2OpenClick = function () {
|
|
146
127
|
const index = $(this).closest(".item").data("index")
|
|
147
128
|
const opt = data.options[index]
|
|
148
129
|
const items = opt.data?.items || []
|
|
@@ -153,21 +134,23 @@ cookieAdvanced.dataAppend = () => {
|
|
|
153
134
|
)
|
|
154
135
|
// 產 step2 下方結構
|
|
155
136
|
$step2.find(".content-block").html(
|
|
156
|
-
cookieStep2ContentEl(items,cookieNewObj,data,opt)
|
|
137
|
+
cookieStep2ContentEl(items, cookieNewObj, data, opt)
|
|
157
138
|
)
|
|
158
139
|
// 換頁
|
|
159
|
-
document.body.fesd.cookieGoToNext()
|
|
140
|
+
document.body.fesd.cookieGoToNext()
|
|
160
141
|
// 重綁收合
|
|
161
142
|
methods.collapseEvent(
|
|
162
|
-
'[data-collapse-click]',
|
|
163
|
-
'[data-collapse]',
|
|
164
|
-
'[data-collapse-content]',
|
|
165
|
-
false,
|
|
143
|
+
'[data-collapse-click]',
|
|
144
|
+
'[data-collapse]',
|
|
145
|
+
'[data-collapse-content]',
|
|
146
|
+
false,
|
|
166
147
|
true
|
|
167
148
|
)
|
|
168
149
|
}
|
|
169
150
|
// step2 點擊打開
|
|
170
|
-
$step1
|
|
151
|
+
$step1
|
|
152
|
+
.off('click.step2Open', '[data-open]')
|
|
153
|
+
.on('click.step2Open', '[data-open]', step2OpenClick)
|
|
171
154
|
}
|
|
172
155
|
|
|
173
156
|
// switch 的一些狀態
|
|
@@ -176,178 +159,256 @@ cookieAdvanced.switchState = () => {
|
|
|
176
159
|
const $step2contentSwitch = `[t4-id='step2'] .content-block input[type="checkbox"][data-parent][data-name]:not(.disabled)`
|
|
177
160
|
const $step2topSwitch = `[t4-id='step2'] .top-block input[type="checkbox"][data-name]:not(.disabled)`
|
|
178
161
|
|
|
179
|
-
let isSyncing = false
|
|
162
|
+
let isSyncing = false
|
|
180
163
|
|
|
181
164
|
// 開關同步連動控制
|
|
182
|
-
$(document)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
$($
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
165
|
+
$(document)
|
|
166
|
+
.off('change.switch')
|
|
167
|
+
.on('change.switch', 'input[type="checkbox"][data-name]:not(:disabled)', function () {
|
|
168
|
+
if (isSyncing) return
|
|
169
|
+
isSyncing = true
|
|
170
|
+
|
|
171
|
+
const $this = $(this)
|
|
172
|
+
const key = $this.data('name')
|
|
173
|
+
const val = $this.prop('checked')
|
|
174
|
+
|
|
175
|
+
$(`input[type="checkbox"][data-name="${key}"]`).not(this).prop('checked', val)
|
|
176
|
+
if (!val) {
|
|
177
|
+
$($this).parents(".main-option,.list-option").removeClass("checked")
|
|
178
|
+
} else {
|
|
179
|
+
$($this).parents(".main-option,.list-option").addClass("checked")
|
|
180
|
+
}
|
|
181
|
+
isSyncing = false
|
|
182
|
+
})
|
|
198
183
|
|
|
199
184
|
// step1 變動 → 更新 cookieNewObj
|
|
200
|
-
$(document)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
185
|
+
$(document)
|
|
186
|
+
.off('change.cookieStep1')
|
|
187
|
+
.on('change.cookieStep1', `[t4-id='step1'] input[type="checkbox"][data-name]`, function () {
|
|
188
|
+
if ($(this).closest("[t4-id='step2']").length) return
|
|
189
|
+
if (isSyncing) return
|
|
190
|
+
isSyncing = true
|
|
191
|
+
|
|
192
|
+
const $this = $(this)
|
|
193
|
+
const key = $this.data("name")
|
|
194
|
+
const val = $this.prop('checked') ? 0 : 1
|
|
195
|
+
|
|
196
|
+
if (cookieNewObj[key]) {
|
|
197
|
+
cookieNewObj[key].close = val
|
|
198
|
+
const targetList = cookieNewObj[key].list
|
|
199
|
+
if (targetList && typeof targetList === 'object') {
|
|
200
|
+
Object.keys(targetList).forEach(childKey => {
|
|
201
|
+
if (targetList[childKey].close !== undefined) {
|
|
202
|
+
targetList[childKey].close = val
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
}
|
|
206
|
+
}
|
|
219
207
|
isSyncing = false
|
|
220
208
|
})
|
|
221
209
|
|
|
222
|
-
$(document)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
else if (checked === 0) {
|
|
247
|
-
cookieNewObj[parent].close = 1
|
|
248
|
-
$($step2topSwitch).prop("checked", false)
|
|
249
|
-
$step1Switch.prop("checked", false)
|
|
250
|
-
$step2.find(".main-option").removeClass("checked")
|
|
251
|
-
}
|
|
210
|
+
$(document)
|
|
211
|
+
.off("change.step2content")
|
|
212
|
+
.on("change.step2content", $step2contentSwitch, function () {
|
|
213
|
+
// if ($(this).closest("[t4-id='step2']").length) return
|
|
214
|
+
if (isSyncing) return
|
|
215
|
+
isSyncing = true
|
|
216
|
+
|
|
217
|
+
const key = $(this).data("name")
|
|
218
|
+
const parent = $(this).data("parent")
|
|
219
|
+
const val = $(this).prop('checked') ? 0 : 1
|
|
220
|
+
const $step1Switch = $(`[t4-id='step1'] input[type="checkbox"][data-name='${parent}']:not(.disabled)`)
|
|
221
|
+
const total = $($step2contentSwitch).length
|
|
222
|
+
const checked = $($step2contentSwitch).filter(':checked').length
|
|
223
|
+
|
|
224
|
+
// 改變 cookieNewObj 的值
|
|
225
|
+
cookieNewObj[parent].list[key].close = val
|
|
226
|
+
|
|
227
|
+
// Step2 子項全部打開
|
|
228
|
+
if (checked === total || checked > 0) {
|
|
229
|
+
cookieNewObj[parent].close = 0
|
|
230
|
+
$($step2topSwitch).prop("checked", true)
|
|
231
|
+
$step1Switch.prop("checked", true)
|
|
232
|
+
$step2.find(".main-option").addClass("checked")
|
|
233
|
+
}
|
|
252
234
|
|
|
253
|
-
|
|
254
|
-
|
|
235
|
+
// Step2 子項全部關閉
|
|
236
|
+
else if (checked === 0) {
|
|
237
|
+
cookieNewObj[parent].close = 1
|
|
238
|
+
$($step2topSwitch).prop("checked", false)
|
|
239
|
+
$step1Switch.prop("checked", false)
|
|
240
|
+
$step2.find(".main-option").removeClass("checked")
|
|
241
|
+
}
|
|
255
242
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (list) {
|
|
277
|
-
Object.keys(list).forEach(childKey => {
|
|
278
|
-
list[childKey].close = val
|
|
279
|
-
})
|
|
243
|
+
isSyncing = false
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
$(document)
|
|
247
|
+
.off("change.step2top")
|
|
248
|
+
.on("change.step2top", $step2topSwitch, function () {
|
|
249
|
+
if (isSyncing) return
|
|
250
|
+
isSyncing = true
|
|
251
|
+
const $this = $(this)
|
|
252
|
+
const key = $this.data("name")
|
|
253
|
+
const isChecked = $this.prop("checked")
|
|
254
|
+
const detectVal = $this.prop('checked')
|
|
255
|
+
const val = isChecked ? 0 : 1
|
|
256
|
+
|
|
257
|
+
// 改所有子項
|
|
258
|
+
$($step2contentSwitch).prop('checked', isChecked)
|
|
259
|
+
if (!detectVal) {
|
|
260
|
+
$step2.find(".list-option").removeClass("checked")
|
|
261
|
+
} else {
|
|
262
|
+
$step2.find(".list-option").addClass("checked")
|
|
280
263
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
264
|
+
// 改 cookie main
|
|
265
|
+
if (cookieNewObj[key]) {
|
|
266
|
+
const list = cookieNewObj[key].list
|
|
267
|
+
cookieNewObj[key].close = val
|
|
268
|
+
if (list) {
|
|
269
|
+
Object.keys(list).forEach(childKey => {
|
|
270
|
+
list[childKey].close = val
|
|
271
|
+
})
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
isSyncing = false
|
|
275
|
+
})
|
|
284
276
|
}
|
|
285
277
|
|
|
286
278
|
// 存 cookie
|
|
287
279
|
cookieAdvanced.cookieSaveFn = () => {
|
|
288
280
|
const nonRequiredValues = Object.entries(cookieNewObj)
|
|
289
|
-
.filter(
|
|
290
|
-
|
|
281
|
+
.filter(
|
|
282
|
+
([key]) => key !== 'required' && key !== 'close'
|
|
283
|
+
)
|
|
284
|
+
.map(
|
|
285
|
+
([, value]) => value
|
|
286
|
+
)
|
|
291
287
|
|
|
292
|
-
const allNonRequiredAreClosed = nonRequiredValues.every(opt => opt.close === 1)
|
|
293
|
-
const globalCloseValue = allNonRequiredAreClosed ? 1 : 0
|
|
288
|
+
const allNonRequiredAreClosed = nonRequiredValues.every(opt => opt.close === 1)
|
|
289
|
+
const globalCloseValue = allNonRequiredAreClosed ? 1 : 0
|
|
294
290
|
|
|
295
291
|
// 判斷設定第一層的 close 值
|
|
296
|
-
cookieNewObj.close = globalCloseValue
|
|
292
|
+
cookieNewObj.close = globalCloseValue
|
|
297
293
|
|
|
298
294
|
// 存入 cookie
|
|
299
295
|
Cookies.set('_wd', JSON.stringify(cookieNewObj), {
|
|
300
296
|
expires: 180,
|
|
301
297
|
path: '/',
|
|
302
298
|
sameSite: true,
|
|
303
|
-
})
|
|
299
|
+
})
|
|
304
300
|
}
|
|
305
301
|
|
|
306
302
|
// 點擊方法
|
|
307
|
-
cookieAdvanced.clickEvent = () => {
|
|
303
|
+
cookieAdvanced.clickEvent = (el) => {
|
|
308
304
|
const $settingBlock = $(".cookie-setting")
|
|
305
|
+
const $cookiePolicy = $(".cookie-check")
|
|
309
306
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
307
|
+
let scrollInstance = null
|
|
308
|
+
|
|
309
|
+
$(document)
|
|
310
|
+
.off("click.cookie", "[data-cookie]")
|
|
311
|
+
.on("click.cookie", "[data-cookie]", function (e) {
|
|
312
|
+
const $this = $(this)
|
|
313
|
+
|
|
314
|
+
// 打開 cookie 介面
|
|
315
|
+
if ($this.is("[data-cookie-open]")) {
|
|
316
|
+
const mode = $this.attr("data-cookie-open")
|
|
317
|
+
const targets = new Set(mode === "all" ? ["advance", "basic"] : [mode])
|
|
318
|
+
|
|
319
|
+
const openAdvance = () => {
|
|
320
|
+
cookieSetting.openEvent($settingBlock, () => {
|
|
321
|
+
if (scrollInstance) return
|
|
322
|
+
const scrollerEl = $settingBlock.find(".scroller")[0]
|
|
323
|
+
if (scrollerEl) scrollInstance = OverlayScrollbars(scrollerEl, {})
|
|
324
|
+
})
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const openBasic = () => {
|
|
328
|
+
$cookiePolicy
|
|
329
|
+
.addClass("first-enter")
|
|
330
|
+
.removeClass("agree hidden")
|
|
331
|
+
|
|
332
|
+
// 如果你只是要等動畫再 show,建議先清掉舊的 timer,避免連點造成閃爍
|
|
333
|
+
clearTimeout(cookieAdvanced._basicTimer)
|
|
334
|
+
cookieAdvanced._basicTimer = setTimeout(() => {
|
|
335
|
+
$cookiePolicy.addClass("show")
|
|
336
|
+
}, 800)
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (targets.has("advance")) openAdvance()
|
|
340
|
+
if (targets.has("basic")) openBasic()
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 啟用進階版 cookie 所有選項
|
|
344
|
+
if ($this.is("[data-cookie-all]")) {
|
|
345
|
+
cookieSetting.enableAll()
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// 關閉進階版 cookie 所有選項
|
|
349
|
+
if ($this.is("[data-cookie-reject]")) {
|
|
350
|
+
cookieSetting.rejectAll()
|
|
351
|
+
cookieAdvanced.cookieSaveFn()
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// 關閉進階版 cookie 視窗
|
|
355
|
+
if ($this.is("[data-cookie-close]")) {
|
|
356
|
+
const mode = $this.attr("data-cookie-close")
|
|
357
|
+
const targets = new Set(mode === "all" ? ["basic", "advance"] : [mode])
|
|
358
|
+
|
|
359
|
+
const closeAdvance = () => {
|
|
360
|
+
cookieSetting.closeEvent($settingBlock)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const closeBasic = () => {
|
|
364
|
+
$cookiePolicy.addClass("hidden")
|
|
365
|
+
sessionStorage.setItem(el, "true")
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (targets.has("advance")) closeAdvance()
|
|
369
|
+
if (targets.has("basic")) closeBasic()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// 儲存, 放在預存當前選項的按鈕上(功能:將目前選項紀錄預存至 cookie 中, 尚未真正啟用)
|
|
373
|
+
if ($this.is("[data-cookie-save]")) {
|
|
374
|
+
cookieAdvanced.cookieSaveFn()
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// 放在真正要啟動 cookie 功能的儲存按鈕上(功能:依照目前 save 的 cookie 選項, 執行後端程式)
|
|
378
|
+
if ($this.is("[data-cookie-run]")) {
|
|
379
|
+
if (typeof window._wdSetting === "function") {
|
|
380
|
+
window._wdSetting()
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
})
|
|
323
384
|
}
|
|
324
385
|
|
|
325
386
|
// ** --- 工具們 --- **
|
|
326
387
|
// cookie 開啟方法
|
|
327
|
-
cookieSetting.openEvent = (el) => {
|
|
388
|
+
cookieSetting.openEvent = (el, scrollInit) => {
|
|
328
389
|
$(el).addClass("active")
|
|
329
390
|
setTimeout(() => {
|
|
330
391
|
$(el).addClass("show")
|
|
331
|
-
scrollLock()
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}, 500);
|
|
392
|
+
scrollLock()
|
|
393
|
+
scrollInit?.()
|
|
394
|
+
}, 500)
|
|
335
395
|
}
|
|
336
396
|
|
|
337
397
|
// cookie 關閉方法
|
|
338
398
|
cookieSetting.closeEvent = (el) => {
|
|
339
399
|
$(el).removeClass("show")
|
|
400
|
+
|
|
340
401
|
setTimeout(() => {
|
|
341
402
|
$(el).removeClass("active")
|
|
342
|
-
scrollUnlock()
|
|
343
|
-
}, 500)
|
|
403
|
+
scrollUnlock()
|
|
404
|
+
}, 500)
|
|
344
405
|
}
|
|
345
406
|
|
|
346
407
|
// 全部打開
|
|
347
408
|
cookieSetting.enableAll = () => {
|
|
348
409
|
$(`input[type="checkbox"][data-name]:not(.disabled)`).prop('checked', true).trigger('change')
|
|
349
410
|
$('.main-option:not([data-name="required"]),.list-option:not([data-name="required"])').addClass("checked")
|
|
350
|
-
cookieNewObj.close
|
|
411
|
+
cookieNewObj.close = 0
|
|
351
412
|
Object.keys(cookieNewObj).forEach(data => {
|
|
352
413
|
cookieNewObj[data].close == 0
|
|
353
414
|
})
|
|
@@ -365,73 +426,73 @@ cookieSetting.rejectAll = () => {
|
|
|
365
426
|
|
|
366
427
|
// 判斷該分類底下細項按鈕狀態
|
|
367
428
|
cookieSetting.hasAnyListItemsOpen = (key) => {
|
|
368
|
-
const category = cookieNewObj[key]
|
|
369
|
-
if (!category) return false
|
|
429
|
+
const category = cookieNewObj[key]
|
|
430
|
+
if (!category) return false
|
|
370
431
|
|
|
371
|
-
const targetList = category.list
|
|
432
|
+
const targetList = category.list
|
|
372
433
|
|
|
373
434
|
// 沒有子 list,就看自己這一層
|
|
374
435
|
if (!targetList) {
|
|
375
|
-
return category.close === 0
|
|
436
|
+
return category.close === 0
|
|
376
437
|
}
|
|
377
438
|
|
|
378
439
|
// 有子 list,就看子項目有沒有任何一個是 open
|
|
379
|
-
return Object.values(targetList).some(item => item?.close === 0)
|
|
380
|
-
}
|
|
440
|
+
return Object.values(targetList).some(item => item?.close === 0)
|
|
441
|
+
}
|
|
381
442
|
|
|
382
443
|
cookieSetting.isCategoryChecked = (key) => {
|
|
383
|
-
if (key === "required") return true
|
|
384
|
-
return cookieSetting.hasAnyListItemsOpen(key)
|
|
385
|
-
}
|
|
444
|
+
if (key === "required") return true
|
|
445
|
+
return cookieSetting.hasAnyListItemsOpen(key)
|
|
446
|
+
}
|
|
386
447
|
|
|
387
448
|
// 共用:產生 step 元件用的 getter
|
|
388
449
|
const createStepElement = (templateFn) => {
|
|
389
450
|
return (opt, index) => {
|
|
390
|
-
const isDisabled = opt.disabled ? "disabled" : ""
|
|
451
|
+
const isDisabled = opt.disabled ? "disabled" : ""
|
|
391
452
|
const isChecked = () =>
|
|
392
|
-
cookieSetting.isCategoryChecked(opt.key) ? "checked" : ""
|
|
453
|
+
cookieSetting.isCategoryChecked(opt.key) ? "checked" : ""
|
|
393
454
|
|
|
394
|
-
return templateFn(opt, index, isDisabled, isChecked)
|
|
395
|
-
}
|
|
396
|
-
}
|
|
455
|
+
return templateFn(opt, index, isDisabled, isChecked)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
397
458
|
|
|
398
459
|
// step 1 結構
|
|
399
|
-
cookieElements.getStep1Element = createStepElement(cookieStep1El)
|
|
460
|
+
cookieElements.getStep1Element = createStepElement(cookieStep1El)
|
|
400
461
|
// step 2 上面結構
|
|
401
|
-
cookieElements.getStep2TopElement = createStepElement(cookieStep2TittleEl)
|
|
462
|
+
cookieElements.getStep2TopElement = createStepElement(cookieStep2TittleEl)
|
|
402
463
|
|
|
403
464
|
// 頁籤
|
|
404
465
|
cookieSetting.goToTab = (el = true) => {
|
|
405
466
|
const tab = $('tab-el[t4-name="cookie-tab"]')
|
|
406
467
|
const $step2 = $(".setting-container[t4-id='step2']")
|
|
407
468
|
if (el) {
|
|
408
|
-
tab[0].goNext()
|
|
469
|
+
tab[0].goNext()
|
|
409
470
|
} else {
|
|
410
|
-
tab[0].goPrev()
|
|
411
|
-
$step2.find(".top-block, .content-block").empty()
|
|
471
|
+
tab[0].goPrev()
|
|
472
|
+
$step2.find(".top-block, .content-block").empty()
|
|
412
473
|
}
|
|
413
474
|
}
|
|
414
475
|
|
|
415
|
-
methods.toBackend({
|
|
476
|
+
methods.toBackend({
|
|
416
477
|
getCookieValue: (el) => Cookies.get(el),
|
|
417
478
|
cookieGoToNext: (el) => cookieSetting.goToTab(el),
|
|
418
|
-
})
|
|
479
|
+
})
|
|
419
480
|
|
|
420
481
|
// init
|
|
421
|
-
cookieAdvanced.init = () => {
|
|
422
|
-
cookieAdvanced.newObj()
|
|
423
|
-
cookieAdvanced.dataAppend()
|
|
424
|
-
// cookieAdvanced.initialState()
|
|
425
|
-
cookieAdvanced.clickEvent()
|
|
426
|
-
cookieAdvanced.cookieSaveFn()
|
|
427
|
-
cookieAdvanced.switchState()
|
|
482
|
+
cookieAdvanced.init = (el) => {
|
|
483
|
+
cookieAdvanced.newObj()
|
|
484
|
+
cookieAdvanced.dataAppend()
|
|
485
|
+
// cookieAdvanced.initialState()
|
|
486
|
+
cookieAdvanced.clickEvent(el)
|
|
487
|
+
cookieAdvanced.cookieSaveFn()
|
|
488
|
+
cookieAdvanced.switchState()
|
|
428
489
|
}
|
|
429
490
|
|
|
430
491
|
// 預設為 false -> 基礎版, true -> 進階版
|
|
431
|
-
export const init = (isOpenAdvanced = false) => {
|
|
492
|
+
export const init = (isOpenAdvanced = false, el) => {
|
|
432
493
|
// cookie 基本版
|
|
433
|
-
cookieBasic.basicCookiePolicy()
|
|
494
|
+
cookieBasic.basicCookiePolicy(el)
|
|
434
495
|
if (!isOpenAdvanced) return
|
|
435
496
|
// cookie 進階版
|
|
436
|
-
cookieAdvanced.init()
|
|
497
|
+
cookieAdvanced.init(el)
|
|
437
498
|
}
|
|
@@ -54,8 +54,8 @@ export const inits = () => {
|
|
|
54
54
|
new ImageValidate();
|
|
55
55
|
// uiInit
|
|
56
56
|
navbar.init();
|
|
57
|
-
// 預設為 false -> 基礎版, true ->
|
|
58
|
-
cookie.init(true);
|
|
57
|
+
// 預設為 false -> 基礎版, true -> 進階版, 各專案 key 名要記得更改
|
|
58
|
+
cookie.init(true,"frameworkCookie-agree");
|
|
59
59
|
// coding...
|
|
60
60
|
firstEntryHandler.init();
|
|
61
61
|
}
|
package/src/layouts/_navbar.pug
CHANGED
|
@@ -12,7 +12,9 @@ nav.navbar
|
|
|
12
12
|
li
|
|
13
13
|
a(href="./play.html") Play
|
|
14
14
|
li
|
|
15
|
-
|
|
15
|
+
div(data-cookie data-cookie-open="basic") cookie 設定
|
|
16
|
+
li
|
|
17
|
+
div(data-cookie data-cookie-open="advance") cookie 進階設定
|
|
16
18
|
li
|
|
17
19
|
a(href="javascript:;" data-modal-target="my-modal") 燈箱
|
|
18
20
|
li
|
|
@@ -52,3 +52,8 @@ html(lang="zh-Hant-TW" data-overlayscrollbars-initialize)
|
|
|
52
52
|
modern-modal(data-modal-id="my-modal" data-modal-animate="clip-right")
|
|
53
53
|
.close-btn(data-modal-close)
|
|
54
54
|
.title Modal4
|
|
55
|
+
.photo-box(video-target video-id="5bMdjkfvONE" video-type="youtube" video-mode="onPage")
|
|
56
|
+
picture
|
|
57
|
+
source(srcset="https://cdn.wdd.idv.tw/image/video-cover02.webp" type="image/webp")
|
|
58
|
+
source(srcset="https://cdn.wdd.idv.tw/image/video-cover02.jpg" type="image/jpeg")
|
|
59
|
+
img(src="https://cdn.wdd.idv.tw/image/video-cover02.jpg" alt="")
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
// 基本版 cookie
|
|
2
|
+
//- .close & .agree 按鈕名字需保留, 不可更改
|
|
3
|
+
//- 若需要以下按鈕功能, 按鈕必須加上 [data-cookie]
|
|
4
|
+
//- [data-cookie-open="xxx"] -> 開啟 cookie 介面按鈕功能綁定, 值: basic(基本)/advance(進階)/all(所有)
|
|
5
|
+
//- [data-cookie-close="xxx"] -> 關閉 cookie 視窗, 值: basic(基本)/advance(進階)/all(所有)
|
|
6
|
+
//- [data-cookie-reject] -> 關閉進階版 cookie 所有選項
|
|
7
|
+
//- [data-cookie-all] -> 啟用進階版 cookie 所有選項
|
|
8
|
+
//- [data-cookie-save] -> 放在預存當前選項的按鈕上(功能:將目前選項紀錄預存至 cookie 中, 尚未真正啟用)
|
|
9
|
+
//- [data-cookie-run] -> 放在真正要啟動 cookie 功能的儲存按鈕上(功能:依照目前 save 的 cookie 選項, 執行後端程式)
|
|
2
10
|
.cookie-check
|
|
3
11
|
.cookie-main
|
|
4
12
|
.cookie-container
|
|
@@ -7,10 +15,10 @@
|
|
|
7
15
|
p.text 關於本網站使用瀏覽器紀錄 Cookie 來提供您最好的使用體驗,我們使用的 Cookie 也包括了第三方 Cookie 。<br>相關資訊請訪問我們的隱私權與 Cookie 政策。如果您選擇繼續瀏覽或關閉這個提示,便表示您已接受我們的網站使用條款。
|
|
8
16
|
.button-group
|
|
9
17
|
// [data-cookie-reject] -> 全部關閉; [data-cookie-save] -> 儲存 cookie
|
|
10
|
-
.cookie-btn.close(data-cookie-reject) Reject 我拒絕
|
|
11
|
-
.cookie-btn.agree(data-cookie-save) OK 我接受
|
|
18
|
+
.cookie-btn.close(data-cookie data-cookie-reject) Reject 我拒絕
|
|
19
|
+
.cookie-btn.agree(data-cookie data-cookie-save) OK 我接受
|
|
12
20
|
// 若此專案無開進階版 cookie, 請移除以下結構!
|
|
13
|
-
.cookie-setting-btn
|
|
21
|
+
.cookie-setting-btn(data-cookie data-cookie-open="advance")
|
|
14
22
|
span Cookie 偏好設定
|
|
15
23
|
.icon
|
|
16
24
|
i.icon-arrow
|
|
@@ -19,7 +27,8 @@
|
|
|
19
27
|
// 若此專案無開進階版 cookie, 請移除以下結構!
|
|
20
28
|
.cookie-setting
|
|
21
29
|
.scroller
|
|
22
|
-
|
|
30
|
+
// 多語系 ** --switch-openText: 開啟按鈕文字設定 / --switch-closeText: 關閉按鈕文字設定
|
|
31
|
+
.setting-main(style=`--switch-openText: "啟用";--switch-closeText: "拒絕";`)
|
|
23
32
|
tab-el(t4-name="cookie-tab" t4-type="process" t4-group='false')
|
|
24
33
|
// 步驟 1
|
|
25
34
|
.setting-container(t4-role="tabPanel" t4-id='step1')
|
|
@@ -29,8 +38,8 @@
|
|
|
29
38
|
.content-block
|
|
30
39
|
// JS append 結構 (cookieStep1El)
|
|
31
40
|
.button-group
|
|
32
|
-
.cookie-btn.open(data-cookie-all) 啟用所有 Cookie
|
|
33
|
-
.cookie-btn.save(data-cookie-close) SAVE 儲存本次變更
|
|
41
|
+
.cookie-btn.open(data-cookie data-cookie-all) 啟用所有 Cookie
|
|
42
|
+
.cookie-btn.save(data-cookie data-cookie-close="advance") SAVE 儲存本次變更
|
|
34
43
|
// 步驟 2
|
|
35
44
|
.setting-container(t4-role="tabPanel" t4-id='step2')
|
|
36
45
|
.back(onClick=`document.body.fesd.cookieGoToNext(false)`)
|
|
@@ -41,6 +50,7 @@
|
|
|
41
50
|
// JS append 結構 (cookieStep2TittleEl)
|
|
42
51
|
.content-block
|
|
43
52
|
// JS append 結構 (cookieStep2ContentEl)
|
|
53
|
+
|
|
44
54
|
// 後端 cookie 資料放這兒, 先放假結構
|
|
45
55
|
// 若此專案無開進階版 cookie, 請移除以下結構!
|
|
46
56
|
input(type="hidden" id="_wcookie" value='')
|
package/src/pages/index.pug
CHANGED
|
@@ -24,11 +24,11 @@ block content
|
|
|
24
24
|
.text
|
|
25
25
|
.box
|
|
26
26
|
span 您可前往
|
|
27
|
-
span(data-cookie-open) Cookie 偏好設定
|
|
27
|
+
span(data-cookie data-cookie-open="advance") Cookie 偏好設定
|
|
28
28
|
span 進行修改
|
|
29
29
|
.box
|
|
30
30
|
span Modify your
|
|
31
|
-
span(data-cookie-open) cookie preferences.
|
|
31
|
+
span(data-cookie data-cookie-open="advance") cookie preferences.
|
|
32
32
|
.swiper-slide
|
|
33
33
|
.media-box
|
|
34
34
|
video(src="https://cdn.wdd.idv.tw/video/chappie_Video1.mp4" muted playsinline)
|
|
@@ -93,13 +93,13 @@ block content
|
|
|
93
93
|
h3 Video4 👇
|
|
94
94
|
.row
|
|
95
95
|
.grid
|
|
96
|
-
.photo-box(video-target="video-ig-template" video-target-route="./ajax/ajax_igDemo.html" video-id="../../public/assets/img/banner.jpg" video-type="instagram")
|
|
96
|
+
.photo-box(video-target="video-ig-template" video-button="playing" video-target-route="./ajax/ajax_igDemo.html" video-id="../../public/assets/img/banner.jpg" video-type="instagram")
|
|
97
97
|
picture
|
|
98
98
|
source(srcset="https://cdn.wdd.idv.tw/image/video-cover01.webp" type="image/webp")
|
|
99
99
|
source(srcset="https://cdn.wdd.idv.tw/image/video-cover01.jpg" type="image/jpeg")
|
|
100
100
|
img(src="https://cdn.wdd.idv.tw/image/video-cover01.jpg" alt="")
|
|
101
101
|
.grid
|
|
102
|
-
.photo-box(video-target video-id="5bMdjkfvONE" video-type="youtube"
|
|
102
|
+
.photo-box(video-target video-id="5bMdjkfvONE" video-type="youtube")
|
|
103
103
|
picture
|
|
104
104
|
source(srcset="https://cdn.wdd.idv.tw/image/video-cover02.webp" type="image/webp")
|
|
105
105
|
source(srcset="https://cdn.wdd.idv.tw/image/video-cover02.jpg" type="image/jpeg")
|