w-web-perm 1.1.2 → 1.1.4

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.
@@ -1,181 +1,566 @@
1
- <template>
2
- <div style="height:100svh; background:#f5f5f5;">
3
-
4
- <!-- menu top, 因窄版導致名稱換行故須使用overflow-y:hidden -->
5
- <div data-fmid="app-topbar" :style="`height:${heightToolbar}px; box-sizing:border-box; overflow-y:hidden; padding:0px 10px; background:#fff; border-bottom:1px solid #ccc; display:flex; align-items:center;`">
6
-
7
- <div style="padding-left:5px; white-space:nowrap">
8
- <div style="display:flex; align-items:center;">
9
-
10
- <div style="padding-right:10px; display:flex; align-items:center;" v-if="webLogo">
11
- <img style="width:36px; _min-width:36px; height:36px;" :src="webLogo" />
12
- </div>
13
-
14
- <div>
15
- <div style="font-size:1.2rem; color:#000;">{{webName}}</div>
16
- <div style="font-size:0.8rem; color:#666;">{{$t('webDescription')}}</div>
17
- </div>
18
-
19
- </div>
20
- </div>
21
-
22
- <div style="width:100%;"></div>
23
-
24
- <div
25
- style="padding-right:10px; white-space:nowrap;"
26
- v-if="showLangSelect"
27
- >
28
- <WTextSelect
29
- style="width:100px;"
30
- :items="keysLang"
31
- :value="lang"
32
- @input="toggleLang"
33
- >
34
- <template v-slot:select="props">
35
- {{getLangText(props.item)}}
36
- </template>
37
- <template v-slot:item="props">
38
- {{getLangText(props.item)}}
39
- </template>
40
- </WTextSelect>
41
- </div>
42
-
43
- </div>
44
-
45
- <div :style="`height:calc( 100% - ${heightToolbar}px );`">
46
- <LayoutContent
47
- ></LayoutContent>
48
- </div>
49
-
50
-
51
- </div>
52
- </template>
53
-
54
- <script>
55
- import { mdiMenu } from '@mdi/js/mdi.js'
56
- import get from 'lodash-es/get.js'
57
- // import cloneDeep from 'lodash-es/cloneDeep.js'
58
- import isestr from 'wsemi/src/isestr.mjs'
59
- import WTextSelect from 'w-component-vue/src/components/WTextSelect.vue'
60
- import WButtonCircle from 'w-component-vue/src/components/WButtonCircle.vue'
61
- import LayoutContent from './LayoutContent.vue'
62
-
63
-
64
- export default {
65
- components: {
66
- WTextSelect,
67
- WButtonCircle,
68
- LayoutContent,
69
- },
70
- props: {
71
- },
72
- data: function() {
73
- return {
74
- mdiMenu,
75
-
76
- firstSetting: true,
77
-
78
- showLangSelect: false,
79
-
80
- keysLang: [
81
- 'eng',
82
- 'cht',
83
- ],
84
- kpLangSelect: {
85
- 'eng': 'English',
86
- 'cht': '中文',
87
- },
88
-
89
- }
90
- },
91
- mounted: function() {
92
- // console.log('mounted')
93
-
94
- let vo = this
95
-
96
- //firstSetting
97
- if (vo.firstSetting) {
98
- // console.log('webInfor', vo.webInfor)
99
- let showLanguage = get(vo, 'webInfor.showLanguage', '')
100
- // console.log('showLanguage', showLanguage)
101
- vo.showLangSelect = showLanguage === 'y'
102
- let language = get(vo, 'webInfor.language', '')
103
- // console.log('language', language)
104
- vo.$ui.setLang(language, 'layout mounted')
105
- vo.firstSetting = false
106
- }
107
-
108
- },
109
- computed: {
110
-
111
- // viewState: function() {
112
- // let vo = this
113
- // return get(vo, '$store.state.viewState', '')
114
- // },
115
-
116
- heightToolbar: function() {
117
- let vo = this
118
- return get(vo, `$store.state.heightToolbar`, 0)
119
- },
120
-
121
- webInfor: function() {
122
- let wi = get(this, `$store.state.webInfor`)
123
- return wi
124
- },
125
-
126
- webName: {
127
- get() {
128
- let vo = this
129
- let c = vo.$t('webName', '')
130
- // console.log('get webName1', c)
131
- if (!isestr(c)) {
132
- c = vo.$t('waitingData', '')
133
- }
134
- // console.log('get webName2', c)
135
- document.title = c //更換網頁title
136
- return c
137
- },
138
- // set(value) {
139
- // return value
140
- // },
141
- },
142
-
143
- webLogo: function() {
144
- let vo = this
145
- return get(vo, 'webInfor.webLogo', '')
146
- },
147
-
148
- lang: function() {
149
- let vo = this
150
- return get(vo, '$store.state.lang', '')
151
- },
152
-
153
- },
154
- methods: {
155
-
156
- getLangText: function(lang) {
157
- // console.log('methods getLangText', lang)
158
-
159
- let vo = this
160
-
161
- let t = get(vo, `kpLangSelect.${lang}`, '')
162
-
163
- return t
164
- },
165
-
166
- toggleLang: function(lang) {
167
- // console.log('methods toggleLang', lang)
168
-
169
- let vo = this
170
-
171
- //setLang
172
- vo.$ui.setLang(lang, 'toggle')
173
-
174
- },
175
-
176
- }
177
- }
178
- </script>
179
-
180
- <style scoped>
181
- </style>
1
+ <template>
2
+ <div style="height:100svh; background:#f5f5f5;">
3
+
4
+ <!-- menu top, 內容驅動之RWD(同 w-web-sso ADR-067): 系統名稱/說明/語系選單全部攤開所需之自然寬未超過標題列寬時為寬版(與原版面相同),
5
+ 超過則右側收合為漢堡按鈕(WPopup內含當前登入者資訊/語系切換); 名稱與說明各自以剩餘寬為上限, 超出顯示省略符號並以WTooltip提供全文.
6
+ overflow:hidden為保險, 正常情況下任何寬度皆不溢出(原overflow-y:hidden會使水平方向自動出現捲軸, 語系選單被擠出畫面) -->
7
+ <div
8
+ ref="rfHeader"
9
+ data-fmid="app-topbar"
10
+ :style="`height:${heightToolbar}px; box-sizing:border-box; overflow:hidden; padding:0px 10px; background:#fff; border-bottom:1px solid #ccc; display:flex; align-items:center;`"
11
+ v-domresize
12
+ @domresize="resizeHeader"
13
+ >
14
+
15
+ <!-- 識別區, flex:1與min-width:0使寬度隨版寬伸縮, 不再以nowrap撐開整列 -->
16
+ <div style="padding-left:5px; display:flex; align-items:center; flex:1 1 auto; min-width:0;">
17
+
18
+ <div style="padding-right:10px; display:flex; align-items:center; flex:0 0 auto;" v-if="webLogo">
19
+ <img style="width:36px; _min-width:36px; height:36px;" :src="webLogo" />
20
+ </div>
21
+
22
+ <div style="flex:1 1 auto; min-width:0;">
23
+
24
+ <!-- 系統名稱, 超出剩餘寬時省略; 僅於確實被截斷時啟用tooltip, 顯示中則維持啟用直到移出, 避免截斷狀態於顯示中翻轉致提示無法關閉(editable為false時mouseleave不處理) -->
25
+ <WTooltip
26
+ :displayType="'line'"
27
+ :isolated="true"
28
+ :placement="'bottom-start'"
29
+ :maxWidth="tooltipMaxWidth"
30
+ :editable="webNameTruncated || tipNameShown"
31
+ @show="tipNameShown=true"
32
+ @hide="tipNameShown=false"
33
+ >
34
+ <template v-slot:trigger>
35
+ <div
36
+ ref="rfWebName"
37
+ style="font-size:1.2rem; color:#000; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;"
38
+ >{{webName}}</div>
39
+ </template>
40
+ <template v-slot:content>
41
+ <div style="white-space:normal; word-break:break-word;">{{webName}}</div>
42
+ </template>
43
+ </WTooltip>
44
+
45
+ <!-- 系統說明, 同上 -->
46
+ <WTooltip
47
+ :displayType="'line'"
48
+ :isolated="true"
49
+ :placement="'bottom-start'"
50
+ :maxWidth="tooltipMaxWidth"
51
+ :editable="webDescTruncated || tipDescShown"
52
+ @show="tipDescShown=true"
53
+ @hide="tipDescShown=false"
54
+ >
55
+ <template v-slot:trigger>
56
+ <div
57
+ ref="rfWebDesc"
58
+ style="font-size:0.8rem; color:#666; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;"
59
+ >{{webDesc}}</div>
60
+ </template>
61
+ <template v-slot:content>
62
+ <div style="white-space:normal; word-break:break-word;">{{webDesc}}</div>
63
+ </template>
64
+ </WTooltip>
65
+
66
+ </div>
67
+
68
+ </div>
69
+
70
+ <div
71
+ style="padding-right:10px; white-space:nowrap; flex:0 0 auto;"
72
+ v-if="showLangSelect && !isNarrow"
73
+ >
74
+ <WTextSelect
75
+ style="width:100px;"
76
+ :items="keysLang"
77
+ :value="lang"
78
+ @input="toggleLang"
79
+ >
80
+ <template v-slot:select="props">
81
+ {{getLangText(props.item)}}
82
+ </template>
83
+ <template v-slot:item="props">
84
+ {{getLangText(props.item)}}
85
+ </template>
86
+ </WTextSelect>
87
+ </div>
88
+
89
+ <!-- 窄版: 右側收合為漢堡按鈕, 彈窗內依序為當前登入者資訊/語系切換(本系統無登出功能);
90
+ 用v-show而非v-if: 非isolated之WPopup於開啟中被銷毀時不會自行隱藏(不釋放popper與開啟層級清單), 故切回寬版時改以menuOpen=false正常關閉;
91
+ v-show之元素其靜態style不可含display: Vue 2.7.16之updateStyle每次重繪皆重設靜態style全部屬性, 會蓋掉v-show之display:none, 故flex置於內層 -->
92
+ <div
93
+ style="padding-left:10px; padding-right:5px; flex:0 0 auto;"
94
+ v-show="isNarrow && hasMenuItems"
95
+ @keydown.esc="closeMenu"
96
+ >
97
+ <div style="display:flex; align-items:center;">
98
+
99
+ <WPopup
100
+ :placement="'bottom-end'"
101
+ :value="menuOpen"
102
+ @input="setMenuOpen"
103
+ >
104
+ <template v-slot:trigger>
105
+ <!-- 開關一律由toggleMenu經menuOpen控制(再次點擊即關閉, 開啟中以active維持底色); 攔下click不讓WPopup觸發區自行開啟:
106
+ 使用者真實點擊時各監聽器之間會先執行microtask, 按鈕之toggleMenu關閉後觸發區仍收到同一click而重新開啟. 外部點擊關閉走window之mouseup, 不受影響 -->
107
+ <div @click.stop>
108
+ <WButtonCircle
109
+ ref="rfMenuBtn"
110
+ :paddingStyle="{v:6,h:6}"
111
+ :icon="mdiMenu"
112
+ :backgroundColor="'#fff'"
113
+ :backgroundColorHover="'#f2f2f2'"
114
+ :iconColor="'#444'"
115
+ :iconColorHover="'#222'"
116
+ :iconColorFocus="'#222'"
117
+ :iconColorActive="'#222'"
118
+ :shadow="false"
119
+ :active="menuOpen"
120
+ :aria-label="$t('menuUser')"
121
+ :aria-expanded="menuOpen?'true':'false'"
122
+ @click="toggleMenu"
123
+ ></WButtonCircle>
124
+ </div>
125
+ </template>
126
+ <template v-slot:content>
127
+ <div
128
+ :style="`min-width:220px; max-width:${menuMaxWidth}px; padding:4px 0px; font-size:0.85rem; color:#222;`"
129
+ @keydown.esc="closeMenu"
130
+ >
131
+
132
+ <!-- 當前登入者資訊: 姓名為主, Email為輔 -->
133
+ <div
134
+ style="padding:8px 12px; display:flex; align-items:center;"
135
+ v-if="userName || userEmail"
136
+ >
137
+ <div style="flex:0 0 36px; display:flex; justify-content:center;">
138
+ <WIcon
139
+ :icon="mdiAccountCircleOutline"
140
+ :color="'grey darken-1'"
141
+ :colorHover="'grey darken-1'"
142
+ :size="36"
143
+ ></WIcon>
144
+ </div>
145
+ <div style="padding-left:10px; min-width:0;">
146
+ <div style="font-size:0.9rem; color:#222; word-break:break-word;">
147
+ {{userName || userEmail}}
148
+ </div>
149
+ <div
150
+ style="font-size:0.75rem; color:#666; word-break:break-all;"
151
+ v-if="userName && userEmail"
152
+ >
153
+ {{userEmail}}
154
+ </div>
155
+ </div>
156
+ </div>
157
+
158
+ <!-- 語系切換, 下拉選單同寬版 -->
159
+ <div
160
+ :style="`${(userName || userEmail)?'border-top:1px solid #ddd;':''} padding:8px 12px; display:flex; align-items:center;`"
161
+ v-if="showLangSelect"
162
+ >
163
+ <div style="flex:0 0 36px; display:flex; justify-content:center;">
164
+ <WIcon
165
+ :icon="mdiTranslate"
166
+ :color="'grey darken-1'"
167
+ :colorHover="'grey darken-1'"
168
+ :size="20"
169
+ ></WIcon>
170
+ </div>
171
+ <div style="padding-left:10px; flex:1 1 auto; white-space:nowrap;">
172
+ {{$t('language')}}
173
+ </div>
174
+ <div style="padding-left:10px; flex:0 0 auto;">
175
+ <WTextSelect
176
+ style="width:100px;"
177
+ :items="keysLang"
178
+ :value="lang"
179
+ @input="toggleLang"
180
+ >
181
+ <template v-slot:select="props">
182
+ {{getLangText(props.item)}}
183
+ </template>
184
+ <template v-slot:item="props">
185
+ {{getLangText(props.item)}}
186
+ </template>
187
+ </WTextSelect>
188
+ </div>
189
+ </div>
190
+
191
+ </div>
192
+ </template>
193
+ </WPopup>
194
+
195
+ </div>
196
+ </div>
197
+
198
+ </div>
199
+
200
+ <div :style="`height:calc( 100% - ${heightToolbar}px );`">
201
+ <LayoutContent
202
+ ></LayoutContent>
203
+ </div>
204
+
205
+
206
+ </div>
207
+ </template>
208
+
209
+ <script>
210
+ import { mdiMenu, mdiAccountCircleOutline, mdiTranslate } from '@mdi/js/mdi.js'
211
+ import get from 'lodash-es/get.js'
212
+ // import cloneDeep from 'lodash-es/cloneDeep.js'
213
+ import isestr from 'wsemi/src/isestr.mjs'
214
+ import WIcon from 'w-component-vue/src/components/WIcon.vue'
215
+ import WPopup from 'w-component-vue/src/components/WPopup.vue'
216
+ import WTooltip from 'w-component-vue/src/components/WTooltip.vue'
217
+ import WTextSelect from 'w-component-vue/src/components/WTextSelect.vue'
218
+ import WButtonCircle from 'w-component-vue/src/components/WButtonCircle.vue'
219
+ import LayoutContent from './LayoutContent.vue'
220
+
221
+
222
+ //寬窄版判斷用之固定寬(px), 須與template之寬版結構一致
223
+ const hdPadH = 20 //標題列左右padding各10
224
+ const hdLeftPad = 5 //識別區padding-left
225
+ const hdLogoW = 36 + 10 //logo寬+其padding-right
226
+ const hdLangW = 100 + 10 //語系選單寬+其容器padding-right
227
+ const hdGap = 10 //寬版時名稱說明與右側群組間至少保留之間距
228
+
229
+
230
+ //量測文字自然寬(px), 以canvas量測, 不受DOM截斷(省略符號)影響
231
+ let ctxMeasure = null
232
+ function measureTextWidth(text, font) {
233
+ if (!isestr(text)) {
234
+ return 0
235
+ }
236
+ if (ctxMeasure === null) {
237
+ ctxMeasure = document.createElement('canvas').getContext('2d')
238
+ }
239
+ ctxMeasure.font = font
240
+ return ctxMeasure.measureText(text).width
241
+ }
242
+
243
+
244
+ //取元素實際字型字串, 供canvas量測
245
+ function getElFont(el) {
246
+ if (!el) {
247
+ return ''
248
+ }
249
+ let cs = window.getComputedStyle(el)
250
+ return `${cs.fontStyle} ${cs.fontWeight} ${cs.fontSize} ${cs.fontFamily}`
251
+ }
252
+
253
+
254
+ export default {
255
+ components: {
256
+ WIcon,
257
+ WPopup,
258
+ WTooltip,
259
+ WTextSelect,
260
+ WButtonCircle,
261
+ LayoutContent,
262
+ },
263
+ props: {
264
+ },
265
+ data: function() {
266
+ return {
267
+ mdiMenu,
268
+ mdiAccountCircleOutline,
269
+ mdiTranslate,
270
+
271
+ firstSetting: true,
272
+
273
+ showLangSelect: false,
274
+
275
+ keysLang: [
276
+ 'eng',
277
+ 'cht',
278
+ ],
279
+ kpLangSelect: {
280
+ 'eng': 'English',
281
+ 'cht': '中文',
282
+ },
283
+
284
+ hdW: 0, //標題列寬(clientWidth), 0代表尚未量測
285
+ fontName: '', //系統名稱實際字型, 供canvas量測自然寬
286
+ fontDesc: '', //系統說明實際字型
287
+ fontsTick: 0, //字型載入完成後遞增, 觸發文字自然寬重算
288
+
289
+ menuOpen: false, //窄版漢堡選單是否開啟
290
+
291
+ webNameTruncated: false, //系統名稱是否被截斷(顯示省略符號)
292
+ webDescTruncated: false, //系統說明是否被截斷
293
+ tipNameShown: false, //系統名稱tooltip是否顯示中
294
+ tipDescShown: false, //系統說明tooltip是否顯示中
295
+
296
+ }
297
+ },
298
+ mounted: function() {
299
+ // console.log('mounted')
300
+
301
+ let vo = this
302
+
303
+ //字型與標題列寬, 於mounted同步設定, 寬窄版於首次繪製前即確定(資料變更之重繪在microtask內完成)
304
+ vo.readFonts()
305
+ let elHeader = vo.$refs.rfHeader
306
+ if (elHeader) {
307
+ vo.hdW = elHeader.clientWidth
308
+ }
309
+ if (document.fonts && document.fonts.ready) {
310
+ document.fonts.ready.then(() => {
311
+ vo.readFonts()
312
+ vo.fontsTick++
313
+ })
314
+ }
315
+
316
+ //firstSetting
317
+ if (vo.firstSetting) {
318
+ // console.log('webInfor', vo.webInfor)
319
+ let showLanguage = get(vo, 'webInfor.showLanguage', '')
320
+ // console.log('showLanguage', showLanguage)
321
+ vo.showLangSelect = showLanguage === 'y'
322
+ let language = get(vo, 'webInfor.language', '')
323
+ // console.log('language', language)
324
+ vo.$ui.setLang(language, 'layout mounted')
325
+ vo.firstSetting = false
326
+ }
327
+
328
+ },
329
+ computed: {
330
+
331
+ // viewState: function() {
332
+ // let vo = this
333
+ // return get(vo, '$store.state.viewState', '')
334
+ // },
335
+
336
+ heightToolbar: function() {
337
+ let vo = this
338
+ return get(vo, `$store.state.heightToolbar`, 0)
339
+ },
340
+
341
+ webInfor: function() {
342
+ let wi = get(this, `$store.state.webInfor`)
343
+ return wi
344
+ },
345
+
346
+ webName: {
347
+ get() {
348
+ let vo = this
349
+ let c = vo.$t('webName', '')
350
+ // console.log('get webName1', c)
351
+ if (!isestr(c)) {
352
+ c = vo.$t('waitingData', '')
353
+ }
354
+ // console.log('get webName2', c)
355
+ document.title = c //更換網頁title
356
+ return c
357
+ },
358
+ // set(value) {
359
+ // return value
360
+ // },
361
+ },
362
+
363
+ webDesc: function() {
364
+ let vo = this
365
+ return vo.$t('webDescription')
366
+ },
367
+
368
+ webLogo: function() {
369
+ let vo = this
370
+ return get(vo, 'webInfor.webLogo', '')
371
+ },
372
+
373
+ lang: function() {
374
+ let vo = this
375
+ return get(vo, '$store.state.lang', '')
376
+ },
377
+
378
+ userName: function() {
379
+ let vo = this
380
+ return get(vo, '$store.state.userSelf.name', '')
381
+ },
382
+
383
+ userEmail: function() {
384
+ let vo = this
385
+ return get(vo, '$store.state.userSelf.email', '')
386
+ },
387
+
388
+ hasMenuItems: function() {
389
+ //漢堡選單有內容才顯示按鈕
390
+ let vo = this
391
+ return vo.showLangSelect || isestr(vo.userName) || isestr(vo.userEmail)
392
+ },
393
+
394
+ natLeftWidth: function() {
395
+ //識別區自然寬: padding + logo + 名稱與說明之較寬者
396
+ let vo = this
397
+ let wName = vo.getTextWidth(vo.webName, vo.fontName)
398
+ let wDesc = vo.getTextWidth(vo.webDesc, vo.fontDesc)
399
+ return hdLeftPad + (vo.webLogo ? hdLogoW : 0) + Math.max(wName, wDesc)
400
+ },
401
+
402
+ natRightWidth: function() {
403
+ //寬版右側群組自然寬: 語系選單
404
+ let vo = this
405
+ return vo.showLangSelect ? hdLangW : 0
406
+ },
407
+
408
+ isNarrow: function() {
409
+ //全部攤開所需寬度超過標題列寬即為窄版, 未量測前(hdW為0)視為寬版
410
+ let vo = this
411
+ if (vo.hdW <= 0) {
412
+ return false
413
+ }
414
+ return hdPadH + vo.natLeftWidth + vo.natRightWidth + hdGap > vo.hdW
415
+ },
416
+
417
+ tooltipMaxWidth: function() {
418
+ //名稱與說明tooltip最大寬(不含其左右padding共20), 不超出版寬
419
+ let vo = this
420
+ return Math.max(100, Math.min(400, vo.hdW - 40))
421
+ },
422
+
423
+ menuMaxWidth: function() {
424
+ //漢堡選單最大寬, 不超出版寬
425
+ let vo = this
426
+ return Math.max(220, Math.min(320, vo.hdW - 20))
427
+ },
428
+
429
+ layoutSig: function() {
430
+ //影響名稱與說明是否被截斷之因子, 變更時重新偵測截斷狀態
431
+ let vo = this
432
+ return `${vo.hdW}|${vo.isNarrow}|${vo.hasMenuItems}|${!!vo.webLogo}|${vo.webName}|${vo.webDesc}|${vo.fontsTick}`
433
+ },
434
+
435
+ },
436
+ watch: {
437
+
438
+ isNarrow: function(v) {
439
+ //切回寬版時關閉漢堡選單(漢堡按鈕隨之隱藏)
440
+ let vo = this
441
+ if (!v) {
442
+ vo.menuOpen = false
443
+ }
444
+ },
445
+
446
+ layoutSig: function() {
447
+ let vo = this
448
+ vo.$nextTick(() => {
449
+ vo.checkTruncated()
450
+ })
451
+ },
452
+
453
+ },
454
+ methods: {
455
+
456
+ readFonts: function() {
457
+ let vo = this
458
+ vo.fontName = getElFont(vo.$refs.rfWebName)
459
+ vo.fontDesc = getElFont(vo.$refs.rfWebDesc)
460
+ },
461
+
462
+ getTextWidth: function(text, font) {
463
+ //以指定字型量測文字自然寬; fontsTick僅作為相依觸發重算
464
+ let vo = this
465
+ if (vo.fontsTick < 0 || !isestr(font)) {
466
+ return 0
467
+ }
468
+ return measureTextWidth(text, font)
469
+ },
470
+
471
+ resizeHeader: function(msg) {
472
+ // console.log('methods resizeHeader', msg)
473
+
474
+ let vo = this
475
+
476
+ //hdW, 直接讀元素當下寬度, 不用msg.snew(視窗resize事件所帶者為前次輪詢之舊值)
477
+ let el = vo.$refs.rfHeader
478
+ if (el) {
479
+ vo.hdW = el.clientWidth
480
+ }
481
+
482
+ },
483
+
484
+ checkTruncated: function() {
485
+ //偵測名稱與說明是否被截斷: scrollWidth為整數, 溢出不足1px時可能偵測不到, 故再以自然寬比對元素實寬
486
+ let vo = this
487
+ let isTruncated = (el, text, font) => {
488
+ if (!el) {
489
+ return false
490
+ }
491
+ if (el.scrollWidth > el.clientWidth) {
492
+ return true
493
+ }
494
+ return vo.getTextWidth(text, font) > el.getBoundingClientRect().width + 0.05
495
+ }
496
+ vo.webNameTruncated = isTruncated(vo.$refs.rfWebName, vo.webName, vo.fontName)
497
+ vo.webDescTruncated = isTruncated(vo.$refs.rfWebDesc, vo.webDesc, vo.fontDesc)
498
+ },
499
+
500
+ toggleMenu: function() {
501
+ // console.log('methods toggleMenu')
502
+
503
+ let vo = this
504
+
505
+ vo.menuOpen = !vo.menuOpen
506
+
507
+ },
508
+
509
+ setMenuOpen: function(b) {
510
+ // console.log('methods setMenuOpen', b)
511
+
512
+ let vo = this
513
+
514
+ vo.menuOpen = b
515
+
516
+ },
517
+
518
+ closeMenu: function() {
519
+ // console.log('methods closeMenu')
520
+
521
+ let vo = this
522
+
523
+ //check
524
+ if (!vo.menuOpen) {
525
+ return
526
+ }
527
+
528
+ vo.menuOpen = false
529
+
530
+ //焦點回到漢堡按鈕(焦點原位於選單內時, 選單內容銷毀後焦點會遺失); WButtonCircle內有兩個tabindex=0之元素,
531
+ //外層為其tooltip觸發區, 內層為按鈕本體(具Enter處理), 故取最後一個
532
+ let el = get(vo, '$refs.rfMenuBtn.$el', null)
533
+ let els = el ? el.querySelectorAll('[tabindex="0"]') : []
534
+ let elFocus = els.length > 0 ? els[els.length - 1] : null
535
+ if (elFocus) {
536
+ elFocus.focus()
537
+ }
538
+
539
+ },
540
+
541
+ getLangText: function(lang) {
542
+ // console.log('methods getLangText', lang)
543
+
544
+ let vo = this
545
+
546
+ let t = get(vo, `kpLangSelect.${lang}`, '')
547
+
548
+ return t
549
+ },
550
+
551
+ toggleLang: function(lang) {
552
+ // console.log('methods toggleLang', lang)
553
+
554
+ let vo = this
555
+
556
+ //setLang
557
+ vo.$ui.setLang(lang, 'toggle')
558
+
559
+ },
560
+
561
+ }
562
+ }
563
+ </script>
564
+
565
+ <style scoped>
566
+ </style>