jobdone-shared-files 1.0.4 → 1.0.6
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/ProjectManagement/projectNavbar.vue +359 -359
- package/README.md +20 -3
- package/common/directives/collapse.js +12 -12
- package/common/directives/popovers.js +10 -10
- package/common/directives/selectPlaceholder.js +52 -52
- package/common/directives/textareaAutoHeight.js +11 -0
- package/common/directives/tooltip.js +10 -10
- package/common/format.js +26 -26
- package/index.js +14 -14
- package/lightboxWithOverview.vue +156 -156
- package/package.json +19 -19
- package/paginate.vue +141 -141
- package/style/css/vue-loading-overlay/index.css +40 -40
- package/style/scss/Common/Animation.scss +9 -9
- package/style/scss/Common/SelectableTable.scss +34 -34
- package/style/scss/Common/filepond.scss +31 -31
- package/style/scss/Common/thumbnail-group.scss +14 -14
- package/style/scss/Layout/LayoutBase.scss +1031 -1031
- package/style/scss/Layout/LayoutMobile.scss +206 -206
- package/style/scss/Layout/LayoutProject.scss +126 -126
- package/style/scss/Layout/LayoutSinglePage.scss +17 -17
- package/style/scss/Layout/LayoutTwoColumn.scss +60 -60
- package/style/scss/Settings/_Mixins.scss +232 -232
- package/style/scss/Settings/_MobileVariables.scss +11 -11
- package/style/scss/Settings/_bs-variables-dark.scss +70 -70
- package/style/scss/Settings/_bs-variables.scss +1743 -1743
- package/style/scss/Settings/_color-mode.scss +122 -122
- package/style/scss/Settings/_custom-variables.scss +10 -10
- package/tagEditor.vue +263 -263
- package/tree.vue +69 -69
- package/treeItem.vue +371 -371
- package/vueLoadingOverlay.vue +74 -74
package/README.md
CHANGED
|
@@ -114,14 +114,14 @@
|
|
|
114
114
|
- thumbnail-group:方形大頭貼與小大頭貼相疊 style
|
|
115
115
|
- filepond:客製化 filepond 樣式
|
|
116
116
|
|
|
117
|
+
---
|
|
117
118
|
|
|
118
119
|
# Directives
|
|
119
120
|
|
|
120
|
-
## SelectPlaceholder
|
|
121
|
+
## SelectPlaceholder 使用範例:
|
|
121
122
|
|
|
122
123
|
`null` `false` `undefined` `''` 預設會被判斷為Placeholder,如有增減需求請參考option可用變數。
|
|
123
124
|
|
|
124
|
-
### 使用範例:
|
|
125
125
|
```
|
|
126
126
|
<template>
|
|
127
127
|
<select class="form-select"
|
|
@@ -155,4 +155,21 @@
|
|
|
155
155
|
| 1 | value | -- | 是 | -- | 當前選擇內容的值 |
|
|
156
156
|
| 2 | placeholderValue | `Array` | 否 | [] | 除了預設會被判斷為Placeholder的值,如有其他需求可增加。|
|
|
157
157
|
| 3 | excludeValue | `Array` | 否 | [] | 如預設會被判斷為Placeholder的值,需被排除判斷,可在此處排除|
|
|
158
|
-
| 4 | unselectClass | `String` | 否 | '' | 如在未選擇值時希望有更多特殊樣式,可在此傳入Class。不需要加. |
|
|
158
|
+
| 4 | unselectClass | `String` | 否 | '' | 如在未選擇值時希望有更多特殊樣式,可在此傳入Class。不需要加. |
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## textareaAutoHeight 使用範例:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
<template>
|
|
166
|
+
<textarea v-textarea-auto-height v-model="inputValue"></textarea>
|
|
167
|
+
</template>
|
|
168
|
+
<script setup>
|
|
169
|
+
import { ref } from "vue";
|
|
170
|
+
import vTextareaAutoHeight from 'jobdone-shared-files/common/directives/textareaAutoHeight.js';
|
|
171
|
+
|
|
172
|
+
const inputValue = ref(null)
|
|
173
|
+
|
|
174
|
+
</script>
|
|
175
|
+
```
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import Collapse from '../../../bootstrap/js/dist/collapse';
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
mounted(el) {
|
|
5
|
-
Collapse.getOrCreateInstance(el, {
|
|
6
|
-
toggle: false
|
|
7
|
-
});
|
|
8
|
-
},
|
|
9
|
-
unmounted(el) {
|
|
10
|
-
var instance = Collapse.getOrCreateInstance(el);
|
|
11
|
-
instance.dispose();
|
|
12
|
-
}
|
|
1
|
+
import Collapse from '../../../bootstrap/js/dist/collapse';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
mounted(el) {
|
|
5
|
+
Collapse.getOrCreateInstance(el, {
|
|
6
|
+
toggle: false
|
|
7
|
+
});
|
|
8
|
+
},
|
|
9
|
+
unmounted(el) {
|
|
10
|
+
var instance = Collapse.getOrCreateInstance(el);
|
|
11
|
+
instance.dispose();
|
|
12
|
+
}
|
|
13
13
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import Popover from '../../../bootstrap/js/dist/popover';
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
mounted(el) {
|
|
5
|
-
Popover.getOrCreateInstance(el);
|
|
6
|
-
},
|
|
7
|
-
unmounted(el) {
|
|
8
|
-
var instance = Popover.getOrCreateInstance(el);
|
|
9
|
-
instance.dispose();
|
|
10
|
-
}
|
|
1
|
+
import Popover from '../../../bootstrap/js/dist/popover';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
mounted(el) {
|
|
5
|
+
Popover.getOrCreateInstance(el);
|
|
6
|
+
},
|
|
7
|
+
unmounted(el) {
|
|
8
|
+
var instance = Popover.getOrCreateInstance(el);
|
|
9
|
+
instance.dispose();
|
|
10
|
+
}
|
|
11
11
|
}
|
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
/** 使用方式:
|
|
2
|
-
* <select class="form-select" v-model="selectingValue" v-select-placeholder="{ value: selectingValue }">
|
|
3
|
-
<option :value="null" disabled hidden>請選擇一隻貓</option>
|
|
4
|
-
<option :value="1" v-for="(item, index) in optAry" :key="index">Jed</option>
|
|
5
|
-
<option :value="2" v-for="(item, index) in optAry" :key="index">奧迪</option>
|
|
6
|
-
<option :value="3" v-for="(item, index) in optAry" :key="index">賓士貓</option>
|
|
7
|
-
</select>
|
|
8
|
-
|
|
9
|
-
v-select-placeholder="{
|
|
10
|
-
value: value, // 必須傳入
|
|
11
|
-
placeholderValue: [], // 選擇性傳入 指定為Placeholder的值,需為陣列型式['...','...','...']
|
|
12
|
-
excludeValue: [], // 選擇性傳入 需排除預設 null, 0, false, undefined, '' 等等值,不將其判斷為Placeholder,需為陣列型式['...','...','...']
|
|
13
|
-
unselectClass: '' // 選擇性傳入 未選擇時的特殊樣式
|
|
14
|
-
}
|
|
15
|
-
*
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
function updateDom(el, binding) {
|
|
19
|
-
el.querySelectorAll('option')?.forEach(element => {
|
|
20
|
-
element.style.color = 'var(--bs-body-color)'
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// 預設為判斷為Placeholder的值
|
|
24
|
-
let isUnselectValuePackage = [null, false, undefined, '']
|
|
25
|
-
|
|
26
|
-
// [增加] 判斷為Placeholder的值
|
|
27
|
-
binding.value.placeholderValue?.forEach(i => {
|
|
28
|
-
!isUnselectValuePackage.includes(i) ? isUnselectValuePackage.push(i) : ""
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
// [移除] 判斷為 Placeholder 的值
|
|
32
|
-
binding.value.excludeValue?.forEach(i => {
|
|
33
|
-
isUnselectValuePackage.includes(i) ? isUnselectValuePackage.splice(isUnselectValuePackage.indexOf(i), 1) : ""
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
if (isUnselectValuePackage.includes(binding.value.value)) {
|
|
37
|
-
el.classList.add(binding.value.unselectClass)
|
|
38
|
-
el.classList.remove(binding.value.selectingClass)
|
|
39
|
-
el.style.color = 'var(--bs-input-placeholder-color) '
|
|
40
|
-
} else {
|
|
41
|
-
el.classList.add(binding.value.selectingClass)
|
|
42
|
-
el.classList.remove(binding.value.unselectClass)
|
|
43
|
-
el.style.color = 'var(--bs-body-color) '
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
export default {
|
|
47
|
-
mounted(el, binding) {
|
|
48
|
-
updateDom(el, binding)
|
|
49
|
-
},
|
|
50
|
-
updated(el, binding) {
|
|
51
|
-
updateDom(el, binding)
|
|
52
|
-
}
|
|
1
|
+
/** 使用方式:
|
|
2
|
+
* <select class="form-select" v-model="selectingValue" v-select-placeholder="{ value: selectingValue }">
|
|
3
|
+
<option :value="null" disabled hidden>請選擇一隻貓</option>
|
|
4
|
+
<option :value="1" v-for="(item, index) in optAry" :key="index">Jed</option>
|
|
5
|
+
<option :value="2" v-for="(item, index) in optAry" :key="index">奧迪</option>
|
|
6
|
+
<option :value="3" v-for="(item, index) in optAry" :key="index">賓士貓</option>
|
|
7
|
+
</select>
|
|
8
|
+
|
|
9
|
+
v-select-placeholder="{
|
|
10
|
+
value: value, // 必須傳入
|
|
11
|
+
placeholderValue: [], // 選擇性傳入 指定為Placeholder的值,需為陣列型式['...','...','...']
|
|
12
|
+
excludeValue: [], // 選擇性傳入 需排除預設 null, 0, false, undefined, '' 等等值,不將其判斷為Placeholder,需為陣列型式['...','...','...']
|
|
13
|
+
unselectClass: '' // 選擇性傳入 未選擇時的特殊樣式
|
|
14
|
+
}
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
function updateDom(el, binding) {
|
|
19
|
+
el.querySelectorAll('option')?.forEach(element => {
|
|
20
|
+
element.style.color = 'var(--bs-body-color)'
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// 預設為判斷為Placeholder的值
|
|
24
|
+
let isUnselectValuePackage = [null, false, undefined, '']
|
|
25
|
+
|
|
26
|
+
// [增加] 判斷為Placeholder的值
|
|
27
|
+
binding.value.placeholderValue?.forEach(i => {
|
|
28
|
+
!isUnselectValuePackage.includes(i) ? isUnselectValuePackage.push(i) : ""
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// [移除] 判斷為 Placeholder 的值
|
|
32
|
+
binding.value.excludeValue?.forEach(i => {
|
|
33
|
+
isUnselectValuePackage.includes(i) ? isUnselectValuePackage.splice(isUnselectValuePackage.indexOf(i), 1) : ""
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if (isUnselectValuePackage.includes(binding.value.value)) {
|
|
37
|
+
el.classList.add(binding.value.unselectClass)
|
|
38
|
+
el.classList.remove(binding.value.selectingClass)
|
|
39
|
+
el.style.color = 'var(--bs-input-placeholder-color) '
|
|
40
|
+
} else {
|
|
41
|
+
el.classList.add(binding.value.selectingClass)
|
|
42
|
+
el.classList.remove(binding.value.unselectClass)
|
|
43
|
+
el.style.color = 'var(--bs-body-color) '
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export default {
|
|
47
|
+
mounted(el, binding) {
|
|
48
|
+
updateDom(el, binding)
|
|
49
|
+
},
|
|
50
|
+
updated(el, binding) {
|
|
51
|
+
updateDom(el, binding)
|
|
52
|
+
}
|
|
53
53
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import Tooltip from '../../../bootstrap/js/dist/tooltip';
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
mounted(el) {
|
|
5
|
-
Tooltip.getOrCreateInstance(el);
|
|
6
|
-
},
|
|
7
|
-
unmounted(el) {
|
|
8
|
-
var instance = Tooltip.getOrCreateInstance(el);
|
|
9
|
-
instance.dispose();
|
|
10
|
-
}
|
|
1
|
+
import Tooltip from '../../../bootstrap/js/dist/tooltip';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
mounted(el) {
|
|
5
|
+
Tooltip.getOrCreateInstance(el);
|
|
6
|
+
},
|
|
7
|
+
unmounted(el) {
|
|
8
|
+
var instance = Tooltip.getOrCreateInstance(el);
|
|
9
|
+
instance.dispose();
|
|
10
|
+
}
|
|
11
11
|
}
|
package/common/format.js
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import dayjs from 'dayjs';
|
|
2
|
-
import { computed } from 'vue';
|
|
3
|
-
|
|
4
|
-
// 時間格式
|
|
5
|
-
export const formatDate = (value) => {
|
|
6
|
-
if (value === null || !value) {
|
|
7
|
-
return value;
|
|
8
|
-
} else {
|
|
9
|
-
return dayjs(value).format('YYYY-MM-DD');
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export const formatDT = (value) => {
|
|
14
|
-
if (value === null || !value) {
|
|
15
|
-
return value;
|
|
16
|
-
} else {
|
|
17
|
-
return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// 千分位格式
|
|
23
|
-
export const formatThousands = computed(() => {
|
|
24
|
-
return function(number){
|
|
25
|
-
return new Intl.NumberFormat().format(number);
|
|
26
|
-
}
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
|
|
4
|
+
// 時間格式
|
|
5
|
+
export const formatDate = (value) => {
|
|
6
|
+
if (value === null || !value) {
|
|
7
|
+
return value;
|
|
8
|
+
} else {
|
|
9
|
+
return dayjs(value).format('YYYY-MM-DD');
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const formatDT = (value) => {
|
|
14
|
+
if (value === null || !value) {
|
|
15
|
+
return value;
|
|
16
|
+
} else {
|
|
17
|
+
return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// 千分位格式
|
|
23
|
+
export const formatThousands = computed(() => {
|
|
24
|
+
return function(number){
|
|
25
|
+
return new Intl.NumberFormat().format(number);
|
|
26
|
+
}
|
|
27
27
|
});
|
package/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import paginate from "./paginate.vue";
|
|
2
|
-
import tagEditor from "./tagEditor.vue";
|
|
3
|
-
import tree from "./tree.vue";
|
|
4
|
-
import vueLoadingOverlay from "./vueLoadingOverlay.vue";
|
|
5
|
-
import projectNavbar from "./ProjectManagement/projectNavbar.vue";
|
|
6
|
-
|
|
7
|
-
export default {
|
|
8
|
-
paginate,
|
|
9
|
-
tagEditor,
|
|
10
|
-
tree,
|
|
11
|
-
vueLoadingOverlay,
|
|
12
|
-
|
|
13
|
-
// ProjectManagement
|
|
14
|
-
projectNavbar
|
|
1
|
+
import paginate from "./paginate.vue";
|
|
2
|
+
import tagEditor from "./tagEditor.vue";
|
|
3
|
+
import tree from "./tree.vue";
|
|
4
|
+
import vueLoadingOverlay from "./vueLoadingOverlay.vue";
|
|
5
|
+
import projectNavbar from "./ProjectManagement/projectNavbar.vue";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
paginate,
|
|
9
|
+
tagEditor,
|
|
10
|
+
tree,
|
|
11
|
+
vueLoadingOverlay,
|
|
12
|
+
|
|
13
|
+
// ProjectManagement
|
|
14
|
+
projectNavbar
|
|
15
15
|
}
|
package/lightboxWithOverview.vue
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<Teleport to="body">
|
|
3
|
-
<vue-easy-lightbox :visible="lightboxVisibleRef" :imgs="picturesData" :index="picturesIndexRef"
|
|
4
|
-
@hide="hideLightbox" @on-prev-click="picturesIndexRef--" @on-next-click="picturesIndexRef++"></vue-easy-lightbox>
|
|
5
|
-
<div class="lightbox-thumbnail-toolbar text-center overflow-hidden" v-if="lightboxVisibleRef">
|
|
6
|
-
<div class="text-nowrap overflow-x-auto pt-3 px-2 pb-1">
|
|
7
|
-
<div class="d-inline-block">
|
|
8
|
-
<div class="d-inline-block thumbnail-content rounded hover-border"
|
|
9
|
-
:class="{'active': index == picturesIndexRef}" :style="'background-image: url(' + item.src +');'"
|
|
10
|
-
@click="changePictureIndex(index)" v-for="(item, index) in picturesData"></div>
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
</div>
|
|
14
|
-
</Teleport>
|
|
15
|
-
|
|
16
|
-
<template v-if="preview">
|
|
17
|
-
<small class="text-gray-500" v-if="picturesData.length==0">無</small>
|
|
18
|
-
<div class="d-inline-block thumbnail-content thumbnail-clickable thumbnail-32 rounded hover-border me-2"
|
|
19
|
-
:style="'background-image: url(' + item.src +');'"
|
|
20
|
-
@click="showLightbox(index)"
|
|
21
|
-
v-for="(item, index) in previewPictures"></div>
|
|
22
|
-
<button type="button" class="btn btn-sm btn-outline-primary hover-border"
|
|
23
|
-
@click="showLightbox(previewCount)"
|
|
24
|
-
v-if="otherPictureCount">
|
|
25
|
-
+{{otherPictureCount}}
|
|
26
|
-
</button>
|
|
27
|
-
</template>
|
|
28
|
-
</template>
|
|
29
|
-
|
|
30
|
-
<script>
|
|
31
|
-
import { ref, computed } from 'vue';
|
|
32
|
-
import VueEasyLightbox from 'vue-easy-lightbox';
|
|
33
|
-
|
|
34
|
-
export default {
|
|
35
|
-
props: {
|
|
36
|
-
previewCount: {
|
|
37
|
-
type: Number,
|
|
38
|
-
required: false,
|
|
39
|
-
default: 5
|
|
40
|
-
},
|
|
41
|
-
picturesData: {
|
|
42
|
-
type: Array,
|
|
43
|
-
required: true
|
|
44
|
-
},
|
|
45
|
-
displayIndex: {
|
|
46
|
-
type: Number,
|
|
47
|
-
required: false,
|
|
48
|
-
default: 0
|
|
49
|
-
},
|
|
50
|
-
preview: {
|
|
51
|
-
type: Boolean,
|
|
52
|
-
required: false,
|
|
53
|
-
default: false
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
components: {
|
|
57
|
-
VueEasyLightbox
|
|
58
|
-
},
|
|
59
|
-
setup(props) {
|
|
60
|
-
const previewPictures = computed(() => {
|
|
61
|
-
return props.picturesData.slice(0, props.previewCount)
|
|
62
|
-
});
|
|
63
|
-
const otherPictureCount = computed(() => {
|
|
64
|
-
var count = props.picturesData.length - props.previewCount;
|
|
65
|
-
return count < 0 ? 0 : count;
|
|
66
|
-
});
|
|
67
|
-
const lightboxVisibleRef = ref(false);
|
|
68
|
-
const picturesIndexRef = ref(props.displayIndex);
|
|
69
|
-
|
|
70
|
-
const changePictureIndex = (index) => {
|
|
71
|
-
picturesIndexRef.value = index;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const showLightbox = (index) => {
|
|
75
|
-
changePictureIndex(index);
|
|
76
|
-
lightboxVisibleRef.value = true;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const hideLightbox = () => {
|
|
80
|
-
lightboxVisibleRef.value = false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
lightboxVisibleRef,
|
|
85
|
-
picturesIndexRef, changePictureIndex,
|
|
86
|
-
showLightbox, hideLightbox,
|
|
87
|
-
previewCount: props.previewCount,
|
|
88
|
-
previewPictures,
|
|
89
|
-
otherPictureCount
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
</script>
|
|
94
|
-
|
|
95
|
-
<style scoped lang="scss">
|
|
96
|
-
@import "../bootstrap/scss/functions";
|
|
97
|
-
@import "./style/scss/Settings/bs-variables";
|
|
98
|
-
@import "../bootstrap/scss/mixins";
|
|
99
|
-
@import "./style/scss/Settings/custom-variables";
|
|
100
|
-
@import "./style/scss/Settings/Mixins";
|
|
101
|
-
|
|
102
|
-
.hover-border{
|
|
103
|
-
transition: $transition-base;
|
|
104
|
-
&:hover{
|
|
105
|
-
box-shadow: $focus-ring-box-shadow;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.vel-modal{
|
|
110
|
-
backdrop-filter: var(--backdrop-blur);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
.lightbox-thumbnail-toolbar .thumbnail-content{
|
|
114
|
-
cursor: pointer;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
.lightbox-thumbnail-toolbar{
|
|
118
|
-
$padding-x: 10vw;
|
|
119
|
-
position: fixed;
|
|
120
|
-
top: 0;
|
|
121
|
-
left: $padding-x;
|
|
122
|
-
right: $padding-x;
|
|
123
|
-
margin: auto;
|
|
124
|
-
z-index: 10000;
|
|
125
|
-
animation: fadeIn .5s;
|
|
126
|
-
pointer-events: none;
|
|
127
|
-
.overflow-x-auto{
|
|
128
|
-
display: inline-block;
|
|
129
|
-
max-width: 100%;
|
|
130
|
-
pointer-events: auto;
|
|
131
|
-
}
|
|
132
|
-
.thumbnail-content{
|
|
133
|
-
--bs-border-color: rgba(0,0,0, 0.12);
|
|
134
|
-
@include solid-size(40px);
|
|
135
|
-
filter: brightness(0.5);
|
|
136
|
-
transition: $transition-base;
|
|
137
|
-
&:not(:last-child){
|
|
138
|
-
margin-right: 1rem;
|
|
139
|
-
}
|
|
140
|
-
&:hover{
|
|
141
|
-
filter: brightness(.8);
|
|
142
|
-
}
|
|
143
|
-
&.active{
|
|
144
|
-
filter: brightness(1);
|
|
145
|
-
}
|
|
146
|
-
&:hover, &.active{
|
|
147
|
-
--bs-border-color: rgba(255,255,255, 0.3);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
.hover-border:hover{
|
|
151
|
-
box-shadow: 0 0 $focus-ring-blur $focus-ring-width rgba(255,255,255, .25);
|
|
152
|
-
}
|
|
153
|
-
.overflow-x-auto{
|
|
154
|
-
--bs-emphasis-color-rgb: 255, 255, 255;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
1
|
+
<template>
|
|
2
|
+
<Teleport to="body">
|
|
3
|
+
<vue-easy-lightbox :visible="lightboxVisibleRef" :imgs="picturesData" :index="picturesIndexRef"
|
|
4
|
+
@hide="hideLightbox" @on-prev-click="picturesIndexRef--" @on-next-click="picturesIndexRef++"></vue-easy-lightbox>
|
|
5
|
+
<div class="lightbox-thumbnail-toolbar text-center overflow-hidden" v-if="lightboxVisibleRef">
|
|
6
|
+
<div class="text-nowrap overflow-x-auto pt-3 px-2 pb-1">
|
|
7
|
+
<div class="d-inline-block">
|
|
8
|
+
<div class="d-inline-block thumbnail-content rounded hover-border"
|
|
9
|
+
:class="{'active': index == picturesIndexRef}" :style="'background-image: url(' + item.src +');'"
|
|
10
|
+
@click="changePictureIndex(index)" v-for="(item, index) in picturesData"></div>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</Teleport>
|
|
15
|
+
|
|
16
|
+
<template v-if="preview">
|
|
17
|
+
<small class="text-gray-500" v-if="picturesData.length==0">無</small>
|
|
18
|
+
<div class="d-inline-block thumbnail-content thumbnail-clickable thumbnail-32 rounded hover-border me-2"
|
|
19
|
+
:style="'background-image: url(' + item.src +');'"
|
|
20
|
+
@click="showLightbox(index)"
|
|
21
|
+
v-for="(item, index) in previewPictures"></div>
|
|
22
|
+
<button type="button" class="btn btn-sm btn-outline-primary hover-border"
|
|
23
|
+
@click="showLightbox(previewCount)"
|
|
24
|
+
v-if="otherPictureCount">
|
|
25
|
+
+{{otherPictureCount}}
|
|
26
|
+
</button>
|
|
27
|
+
</template>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script>
|
|
31
|
+
import { ref, computed } from 'vue';
|
|
32
|
+
import VueEasyLightbox from 'vue-easy-lightbox';
|
|
33
|
+
|
|
34
|
+
export default {
|
|
35
|
+
props: {
|
|
36
|
+
previewCount: {
|
|
37
|
+
type: Number,
|
|
38
|
+
required: false,
|
|
39
|
+
default: 5
|
|
40
|
+
},
|
|
41
|
+
picturesData: {
|
|
42
|
+
type: Array,
|
|
43
|
+
required: true
|
|
44
|
+
},
|
|
45
|
+
displayIndex: {
|
|
46
|
+
type: Number,
|
|
47
|
+
required: false,
|
|
48
|
+
default: 0
|
|
49
|
+
},
|
|
50
|
+
preview: {
|
|
51
|
+
type: Boolean,
|
|
52
|
+
required: false,
|
|
53
|
+
default: false
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
components: {
|
|
57
|
+
VueEasyLightbox
|
|
58
|
+
},
|
|
59
|
+
setup(props) {
|
|
60
|
+
const previewPictures = computed(() => {
|
|
61
|
+
return props.picturesData.slice(0, props.previewCount)
|
|
62
|
+
});
|
|
63
|
+
const otherPictureCount = computed(() => {
|
|
64
|
+
var count = props.picturesData.length - props.previewCount;
|
|
65
|
+
return count < 0 ? 0 : count;
|
|
66
|
+
});
|
|
67
|
+
const lightboxVisibleRef = ref(false);
|
|
68
|
+
const picturesIndexRef = ref(props.displayIndex);
|
|
69
|
+
|
|
70
|
+
const changePictureIndex = (index) => {
|
|
71
|
+
picturesIndexRef.value = index;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const showLightbox = (index) => {
|
|
75
|
+
changePictureIndex(index);
|
|
76
|
+
lightboxVisibleRef.value = true;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const hideLightbox = () => {
|
|
80
|
+
lightboxVisibleRef.value = false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
lightboxVisibleRef,
|
|
85
|
+
picturesIndexRef, changePictureIndex,
|
|
86
|
+
showLightbox, hideLightbox,
|
|
87
|
+
previewCount: props.previewCount,
|
|
88
|
+
previewPictures,
|
|
89
|
+
otherPictureCount
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<style scoped lang="scss">
|
|
96
|
+
@import "../bootstrap/scss/functions";
|
|
97
|
+
@import "./style/scss/Settings/bs-variables";
|
|
98
|
+
@import "../bootstrap/scss/mixins";
|
|
99
|
+
@import "./style/scss/Settings/custom-variables";
|
|
100
|
+
@import "./style/scss/Settings/Mixins";
|
|
101
|
+
|
|
102
|
+
.hover-border{
|
|
103
|
+
transition: $transition-base;
|
|
104
|
+
&:hover{
|
|
105
|
+
box-shadow: $focus-ring-box-shadow;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.vel-modal{
|
|
110
|
+
backdrop-filter: var(--backdrop-blur);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.lightbox-thumbnail-toolbar .thumbnail-content{
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.lightbox-thumbnail-toolbar{
|
|
118
|
+
$padding-x: 10vw;
|
|
119
|
+
position: fixed;
|
|
120
|
+
top: 0;
|
|
121
|
+
left: $padding-x;
|
|
122
|
+
right: $padding-x;
|
|
123
|
+
margin: auto;
|
|
124
|
+
z-index: 10000;
|
|
125
|
+
animation: fadeIn .5s;
|
|
126
|
+
pointer-events: none;
|
|
127
|
+
.overflow-x-auto{
|
|
128
|
+
display: inline-block;
|
|
129
|
+
max-width: 100%;
|
|
130
|
+
pointer-events: auto;
|
|
131
|
+
}
|
|
132
|
+
.thumbnail-content{
|
|
133
|
+
--bs-border-color: rgba(0,0,0, 0.12);
|
|
134
|
+
@include solid-size(40px);
|
|
135
|
+
filter: brightness(0.5);
|
|
136
|
+
transition: $transition-base;
|
|
137
|
+
&:not(:last-child){
|
|
138
|
+
margin-right: 1rem;
|
|
139
|
+
}
|
|
140
|
+
&:hover{
|
|
141
|
+
filter: brightness(.8);
|
|
142
|
+
}
|
|
143
|
+
&.active{
|
|
144
|
+
filter: brightness(1);
|
|
145
|
+
}
|
|
146
|
+
&:hover, &.active{
|
|
147
|
+
--bs-border-color: rgba(255,255,255, 0.3);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
.hover-border:hover{
|
|
151
|
+
box-shadow: 0 0 $focus-ring-blur $focus-ring-width rgba(255,255,255, .25);
|
|
152
|
+
}
|
|
153
|
+
.overflow-x-auto{
|
|
154
|
+
--bs-emphasis-color-rgb: 255, 255, 255;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
157
|
</style>
|