jobdone-shared-files 1.0.6 → 1.0.9
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 +5 -1
- package/README.md +338 -101
- package/autocompleteSelect.vue +455 -0
- package/package.json +1 -1
- package/tagEditor.vue +0 -14
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
</div>
|
|
26
26
|
<div class="nav-item navbar-line-item btn nav-link border-0 dropdown"
|
|
27
27
|
:class="{'active': isActive(navLinkObj.moduleLink)}">
|
|
28
|
+
<!-- NOTE 自己的獨立AP 沒有list,目前因為隕石已被GeneralLink取代 -->
|
|
28
29
|
<a :href="navLinkObj.url + projectId" class="dropdown-link" v-if="navLinkObj?.moduleLink?.selfIsApp">
|
|
29
30
|
<span class="material-icons icon-18 me-2">{{ navLinkObj?.moduleLink?.icon }}</span>
|
|
30
31
|
{{ navLinkObj?.moduleLink?.name }}
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
</ul>
|
|
44
45
|
</div>
|
|
45
46
|
<div class="nav-item navbar-line-item btn nav-link border-0 dropdown"
|
|
46
|
-
:class="{'active': isActive(item.
|
|
47
|
+
:class="{'active': isActive(item.id)}"
|
|
47
48
|
v-for="item in navLinkObj.generalLink">
|
|
48
49
|
<a :href="item.url + '?projectId=' + projectId" class="dropdown-link">
|
|
49
50
|
<span class="material-icons icon-18 me-2">{{item.icon}}</span>
|
|
@@ -167,12 +168,15 @@
|
|
|
167
168
|
|
|
168
169
|
const isActive = (target) => {
|
|
169
170
|
if (target === null) {
|
|
171
|
+
// 專案資訊
|
|
170
172
|
return target === null && !props.activeClientId;
|
|
171
173
|
}
|
|
172
174
|
if (typeof target === 'object') {
|
|
175
|
+
// 功能選單
|
|
173
176
|
return !!props.activeClientId && (target.clientId === props.activeClientId || target.apps.some(x => x.clientId == props.activeClientId))
|
|
174
177
|
}
|
|
175
178
|
if (!!props.activeClientId && target == props.activeClientId) {
|
|
179
|
+
// 功能選單
|
|
176
180
|
return true;
|
|
177
181
|
}
|
|
178
182
|
return false;
|
package/README.md
CHANGED
|
@@ -1,52 +1,175 @@
|
|
|
1
|
-
|
|
2
|
-
如果要使用 vue 檔,需要直接引用 node_modules 裡的檔案,例如:
|
|
3
|
-
`import OOXX from '../../node_modules/jobdone-shared-files/OOXX.vue';`
|
|
4
|
-
|
|
5
|
-
## Vue Components
|
|
6
|
-
|
|
7
|
-
### paginate
|
|
8
|
-
可設定尺寸:`container-class`
|
|
9
|
-
- 小:`pagination-sm`
|
|
10
|
-
- 中:預設
|
|
11
|
-
- 大:`pagination-lg`
|
|
12
|
-
|
|
13
|
-
----
|
|
1
|
+
|
|
14
2
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
###
|
|
21
|
-
|
|
22
|
-
- 小: `sm`
|
|
23
|
-
- 中:預設
|
|
3
|
+
# **Vue Components**
|
|
4
|
+
如果要使用 vue 檔,需要直接引用 node_modules 裡的檔案,例如:
|
|
5
|
+
```
|
|
6
|
+
import OOXX from '../../node_modules/jobdone-shared-files/OOXX.vue';
|
|
7
|
+
```
|
|
8
|
+
### .
|
|
9
|
+
## 01.ProjectManagement/projectNavbar - 專案資訊Navbar
|
|
24
10
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
### 需要傳入的值:
|
|
12
|
+
| 數值名稱 | 說明 | 必要 |
|
|
13
|
+
|------------------|--------------------------------------------------------------------------------|------|
|
|
14
|
+
| root-domain | 底層的 domain,傳入的值有無斜線皆可 | 是 |
|
|
15
|
+
| arrow-back-link | 離開專案的連結,一般來說是底層的 ProjectList | 是 |
|
|
16
|
+
| project-info | 可接受api url或物件,api僅支援Get請求,結構應為`{name: "", caseNo: "", caseNo: "",beginDT:"",endDT:"",approvedWorkingPeriod:0, extendToDT:"",extendDays:""}`| 是 |
|
|
17
|
+
| project-apps | 可接受api url或物件,api僅支援Get請求,結構去Copy底層API取得專案所屬Module的App連結(V2 TOP)| 是 |
|
|
18
|
+
| active-client-id | Navbar active的對象,請填入client id,不填預設active專案資訊| N |
|
|
19
|
+
| project-id | project id | 是 |
|
|
20
|
+
| user-id | 傳入 user 的帳號 id | 是 |
|
|
21
|
+
| user-picture | 傳入 user 的大頭貼 | 是 |
|
|
22
|
+
| log-out-link | 登出連結 | 是 |
|
|
23
|
+
### .
|
|
24
|
+
## 02.Paginate - 分頁
|
|
25
|
+
### 範例
|
|
26
|
+
```
|
|
27
|
+
<template>
|
|
28
|
+
<paginate
|
|
29
|
+
container-class="..."
|
|
30
|
+
:total-pages="..."
|
|
31
|
+
:current-page="..."
|
|
32
|
+
@to-page="...">
|
|
33
|
+
</paginate>
|
|
34
|
+
</template>
|
|
28
35
|
|
|
29
|
-
|
|
36
|
+
<script setup>
|
|
37
|
+
import paginate from 'jobdone-shared-files/paginate.vue';
|
|
38
|
+
<script>
|
|
39
|
+
```
|
|
40
|
+
### 可用參數
|
|
41
|
+
| # | 參數 Attribute | 型別 Type | 預設值 Default | 選項 Option | 說明 Description |
|
|
42
|
+
| --- | ----------------- | --------- | -------------- | ----------------------------------------- | --------------- |
|
|
43
|
+
| 1 | `container-class` | `String` | `''` | `' '` `'pagination-sm'` `'pagination-lg'` | 尺寸設定|
|
|
44
|
+
| 2 | `total-pages` | `Number` | `1` | 任意阿拉伯數字 | 總頁數 |
|
|
45
|
+
| 3 | `current-page` | `Number` | `1` | 任意阿拉伯數字 | 當前頁碼 |
|
|
46
|
+
| 4 | `page-range` | `Number` | `5` | 任意阿拉伯數字 | 最多顯示幾個頁碼 |
|
|
47
|
+
### 可用方法
|
|
48
|
+
| # | 參數 Attribute | 說明 Description |
|
|
49
|
+
| --- | ----------------- | --------------- |
|
|
50
|
+
| 1 | `to-page` | 點選頁碼執行的動作 |
|
|
51
|
+
### .
|
|
52
|
+
## 03.TagEditor - 標籤
|
|
53
|
+
### 範例
|
|
54
|
+
```
|
|
55
|
+
<template>
|
|
56
|
+
<tagEditor
|
|
57
|
+
ref="tagEditorComponent"
|
|
58
|
+
:auto-complete-option='autoCompleteTagList'
|
|
59
|
+
@update-auto-complete-option="getTagListByKeyWord">
|
|
60
|
+
</tagEditor>
|
|
61
|
+
</template>
|
|
30
62
|
|
|
63
|
+
<script setup>
|
|
64
|
+
import tagEditor from 'jobdone-shared-files/tagEditor.vue';
|
|
65
|
+
|
|
66
|
+
<!-- autoComplete 功能 -->
|
|
67
|
+
const autoCompleteTagList = ref([]);
|
|
68
|
+
async function getTagListByKeyWord(keyword) {
|
|
69
|
+
autoCompleteTagList.value = await axios.get(...).data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
<!-- 取得建立的TAG值 -->
|
|
73
|
+
const tagEditorComponent = ref(null);
|
|
74
|
+
const tags = ref([])
|
|
75
|
+
functio getTag(){
|
|
76
|
+
tags.value = tagEditorComponent.value.tagAry
|
|
77
|
+
}
|
|
78
|
+
<script>
|
|
79
|
+
```
|
|
80
|
+
### 可用參數
|
|
81
|
+
| # | 參數 Attribute | 型別 Type | 預設值 Default | 選項 Option | 說明 Description |
|
|
82
|
+
| --- | ----------------- | --------- | -------------- | ----------------------------------------- | --------------- |
|
|
83
|
+
| 1 | `placeholder` | `String` | `'請在此處輸入標籤文字'` | 任意字串 | input的提示文字|
|
|
84
|
+
| 2 | `auto-complete` | `Boolean` | `true` | `true` `false` | 是否啟用AutoComplete,啟用的話必需傳入用於AutoComplete的Array |
|
|
85
|
+
| 3 | `auto-complete-option` | `Array` | `[]` | `['...','...','...']` | 僅接受字串陣列。 |
|
|
86
|
+
| 4 | `disabled` | `Boolean` | `false` | `true` `false` | 停用 |
|
|
87
|
+
### 可用方法
|
|
88
|
+
| # | 參數 Attribute | 說明 Description |
|
|
89
|
+
| --- | ----------------- | --------------- |
|
|
90
|
+
| 1 | `update-auto-complete-option` | 文字輸入時呼叫,value為當前輸入的值,可用於刷新autoCompleteOption。 |
|
|
91
|
+
### .
|
|
92
|
+
## 04.Tree - 樹狀結構
|
|
93
|
+
### 範例
|
|
94
|
+
```
|
|
95
|
+
<template>
|
|
96
|
+
<tree :items="items" :click-item="clickItem" size="sm"></tree>
|
|
97
|
+
</template>
|
|
31
98
|
|
|
32
|
-
|
|
33
|
-
|
|
99
|
+
<script setup>
|
|
100
|
+
import tree from 'jobdone-shared-files/tree.vue';
|
|
101
|
+
const items = [
|
|
102
|
+
{
|
|
103
|
+
id:1,
|
|
104
|
+
name:'分類A',
|
|
105
|
+
children{
|
|
106
|
+
id:11,
|
|
107
|
+
title:'分類A'-1,
|
|
108
|
+
children:[
|
|
109
|
+
...
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
},{
|
|
113
|
+
id:2,
|
|
114
|
+
name:'分類B',
|
|
115
|
+
children{
|
|
116
|
+
id:12,
|
|
117
|
+
title:'分類B-1',
|
|
118
|
+
children:[
|
|
119
|
+
...
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
...
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
function clickItem(v){
|
|
127
|
+
console.log(v)
|
|
128
|
+
}
|
|
129
|
+
<script>
|
|
130
|
+
```
|
|
131
|
+
### 可用參數
|
|
132
|
+
| # | 參數 Attribute | 型別 Type | 預設值 Default | 選項 Option | 說明 Description |
|
|
133
|
+
| --- | ----------------- | --------- | -------------- | ----------------------------------------- | --------------- |
|
|
134
|
+
| 1 | `items` | `Array` | `[]` | 須包含Id,名稱必須為title,子層的Key必須為children | 尺寸設定|
|
|
135
|
+
| 2 | `size` | `String` | `''` | `''` `'sm'` | 內容陣列|
|
|
136
|
+
| 4 | `bgColor` | `String` | `''` | `''` `'white'` | 用於什麼樣子的背景色上,預設用在灰色背景上。用在白背景上可使用white模式|
|
|
137
|
+
### 可用方法
|
|
138
|
+
| Function | 說明 Description |
|
|
139
|
+
| --------- | ------------------ |
|
|
140
|
+
| clickItem | 點選項目執行的方法 |
|
|
141
|
+
### .
|
|
142
|
+
## 05.VueLoadingOverlay - 指定樣式讀取樣式
|
|
143
|
+
### 範例
|
|
144
|
+
```
|
|
145
|
+
<template>
|
|
146
|
+
<Loading :is-active="isLoading"></Loading>
|
|
147
|
+
</template>
|
|
148
|
+
<script setup>
|
|
149
|
+
import Loading from "jobdone-shared-files/vueLoadingOverlay.vue";
|
|
34
150
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
151
|
+
<!-- loading 初始化 "isLoading" => 自定義變數 -->
|
|
152
|
+
const isLoading = ref(false);
|
|
153
|
+
|
|
154
|
+
<!-- 開啟loading -->
|
|
155
|
+
function showLodaing(){
|
|
156
|
+
isLoading.value = true;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
<!-- 關閉loading -->
|
|
160
|
+
function showLodaing(){
|
|
161
|
+
isLoading.value = false;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
</script>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 可用變數:
|
|
45
168
|
| # | 參數 Attribute | 型別 Type | 預設值 Default | 說明Description |
|
|
46
169
|
| --- | -------------- | --------- | -------------- | ---------------------------------------------------- |
|
|
47
170
|
| 1. | full-page | `Boolean` | `true` | 是否填滿整個視窗。<br>為False時,則填滿放置位置的父層 |
|
|
48
171
|
|
|
49
|
-
|
|
172
|
+
### 其他可用變數(統一樣式不推薦修改):
|
|
50
173
|
|
|
51
174
|
| # | 參數 Attribute | 型別 Type | 預設值 Default | 說明Description |
|
|
52
175
|
| --- | ---------------- |:---------:|:--------------:|:------------------------------------------ |
|
|
@@ -54,74 +177,160 @@
|
|
|
54
177
|
| 2. | loader | `String` | `bars` | 圖案樣式<br>spinner / dots / bars 三種可選 |
|
|
55
178
|
| 3. | colors | `String` | `#fff` | 圖案顏色 |
|
|
56
179
|
| 4. | background-color | `String` | `#000` | 遮罩顏色 |
|
|
57
|
-
| 5. | opacity | `Number ` | `0.5` | 遮罩透明度 |
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
----
|
|
61
|
-
|
|
62
|
-
### ProjectManagement/projectNavbar
|
|
63
|
-
專案資訊 Navbar
|
|
64
|
-
|
|
65
|
-
需要傳入的值:
|
|
66
|
-
| 數值名稱 | 說明 | 必要 |
|
|
67
|
-
|------------------|--------------------------------------------------------------------------------|------|
|
|
68
|
-
| root-domain | 底層的 domain,傳入的值有無斜線皆可 | 是 |
|
|
69
|
-
| arrow-back-link | 離開專案的連結,一般來說是底層的 ProjectList | 是 |
|
|
70
|
-
| project-info | 可接受api url或物件,api僅支援Get請求,結構應為`{name: "", caseNo: "", caseNo: "",beginDT:"",endDT:"",approvedWorkingPeriod:0, extendToDT:"",extendDays:""}`| 是 |
|
|
71
|
-
| project-apps | 可接受api url或物件,api僅支援Get請求,結構去Copy底層API取得專案所屬Module的App連結(V2 TOP)| 是 |
|
|
72
|
-
| active-client-id | Navbar active的對象,請填入client id,不填預設active專案資訊| N |
|
|
73
|
-
| project-id | project id | 是 |
|
|
74
|
-
| user-id | 傳入 user 的帳號 id | 是 |
|
|
75
|
-
| user-picture | 傳入 user 的大頭貼 | 是 |
|
|
76
|
-
| log-out-link | 登出連結 | 是 |
|
|
180
|
+
| 5. | opacity | `Number ` | `0.5` | 遮罩透明度 |
|
|
181
|
+
### .
|
|
182
|
+
## 06.lightboxWithOverview - 有縮圖的圖片光箱
|
|
77
183
|
|
|
184
|
+
```
|
|
185
|
+
<lightbox-with-overview
|
|
186
|
+
:pictures-data="picturesData"
|
|
187
|
+
ref="lightboxWithOverviewRef">
|
|
188
|
+
</lightbox-with-overview>
|
|
189
|
+
```
|
|
78
190
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
### lightboxWithOverview
|
|
82
|
-
有縮圖的圖片光箱
|
|
83
|
-
|
|
84
|
-
`<lightbox-with-overview :pictures-data="picturesData" ref="lightboxWithOverviewRef"></lightbox-with-overview>`
|
|
85
|
-
|
|
86
|
-
可傳入的值:
|
|
191
|
+
### 可傳入的值:
|
|
87
192
|
| 數值名稱 | 說明 | 必要 |
|
|
88
193
|
|------------------|-----------------------------------------------------------------|----|
|
|
89
194
|
| pictures-data | 格式為: `[{src: "", title: ""}]` | 是 |
|
|
90
195
|
| preview | 是否要顯示縮圖,可傳入 `true` or `false` | 否 |
|
|
91
196
|
| display-index | 預設 index 為 0 | 否 |
|
|
92
|
-
| preview-count | preview 的圖片陣列數量,預設為 5,可使用 `lightboxWithOverviewRef?.previewPictures` 與 `lightboxWithOverviewRef?.otherPictureCount` | 否 |
|
|
197
|
+
| preview-count | preview 的圖片陣列數量,預設為 5,可使用 `lightboxWithOverviewRef?.previewPictures` 與 `lightboxWithOverviewRef?.otherPictureCount` | 否 |
|
|
198
|
+
### .
|
|
199
|
+
## 07.autocompleteSelect - autocomplete下拉選單
|
|
200
|
+
### 範例1:簡易型,String Array 的選單
|
|
201
|
+
```
|
|
202
|
+
<template>
|
|
203
|
+
<autocomplete-select
|
|
204
|
+
:opts="opts"
|
|
205
|
+
:selected-data="selected"
|
|
206
|
+
@select="(v) => selected = v">
|
|
207
|
+
</autocomplete-select>
|
|
208
|
+
</template>
|
|
93
209
|
|
|
210
|
+
<script setup>
|
|
211
|
+
import autocompleteSelect from 'jobdone-shared-files/autocompleteSelect.vue';
|
|
212
|
+
|
|
213
|
+
const selected =''
|
|
214
|
+
const opts =['汪汪汪','喵喵喵','啾啾啾']
|
|
215
|
+
<script>
|
|
216
|
+
```
|
|
217
|
+
### 範例2:複雜型,Object Array 的選單
|
|
218
|
+
```
|
|
219
|
+
<template>
|
|
220
|
+
<autocomplete-select
|
|
221
|
+
:selected-data="selected"
|
|
222
|
+
:opts="members"
|
|
223
|
+
:filter-keys="['firstName','lastName','enName']"
|
|
224
|
+
preview-key="enName + ' ( ' + lastName + ' ' + firstName + ' ) '"
|
|
225
|
+
binding-key="id"
|
|
226
|
+
@select="(v) => selected = v.id"
|
|
227
|
+
></autocomplete-select>
|
|
228
|
+
</template>
|
|
229
|
+
<script setup>
|
|
230
|
+
import autocompleteSelect from 'jobdone-shared-files/autocompleteSelect.vue';
|
|
231
|
+
|
|
232
|
+
const selected = '1'
|
|
233
|
+
const members =[{
|
|
234
|
+
id:'1',
|
|
235
|
+
firstName:'美美',
|
|
236
|
+
lastName:'陳',
|
|
237
|
+
enName:'Amy'
|
|
238
|
+
},{
|
|
239
|
+
id:'2',
|
|
240
|
+
firstName:'大蒙',
|
|
241
|
+
lastName:'王',
|
|
242
|
+
enName:'Jed'
|
|
243
|
+
},{
|
|
244
|
+
...
|
|
245
|
+
}]
|
|
246
|
+
<script>
|
|
247
|
+
```
|
|
248
|
+
### 範例3:自訂選單樣式()
|
|
249
|
+
```
|
|
250
|
+
<template>
|
|
251
|
+
<autocomplete-select
|
|
252
|
+
:selected-data="selected"
|
|
253
|
+
:opts="members"
|
|
254
|
+
:filter-keys="['firstName','lastName','enName']"
|
|
255
|
+
preview-key="enName + ' ( ' + lastName + ' ' + firstName + ' ) '"
|
|
256
|
+
binding-key="id"
|
|
257
|
+
@select="(v) => selected = v.id"
|
|
258
|
+
:html-option='true'
|
|
259
|
+
>
|
|
260
|
+
<template #option = "{ optData }">
|
|
261
|
+
<div>我全要喵喵喵喵!!</div>
|
|
262
|
+
<div>
|
|
263
|
+
{{optData.phoneNumber}}
|
|
264
|
+
</div>
|
|
265
|
+
<div>
|
|
266
|
+
{{ `${optData.enName} (${ optData.firstName } ${ optData.lastName })` }}
|
|
267
|
+
</div>
|
|
268
|
+
</template>
|
|
269
|
+
</autocomplete-select>
|
|
270
|
+
</template>
|
|
271
|
+
<script setup>
|
|
272
|
+
import autocompleteSelect from 'jobdone-shared-files/autocompleteSelect.vue';
|
|
273
|
+
|
|
274
|
+
const selected = '1'
|
|
275
|
+
const members =[{
|
|
276
|
+
id:'1',
|
|
277
|
+
firstName:'美美',
|
|
278
|
+
lastName:'陳',
|
|
279
|
+
enName:'Amy',
|
|
280
|
+
phoneNumber:'0911111111',
|
|
281
|
+
},{
|
|
282
|
+
id:'2',
|
|
283
|
+
firstName:'大蒙',
|
|
284
|
+
lastName:'王',
|
|
285
|
+
enName:'Jed',
|
|
286
|
+
phoneNumber:'092222222',
|
|
287
|
+
},{
|
|
288
|
+
...
|
|
289
|
+
}]
|
|
290
|
+
<script>
|
|
291
|
+
```
|
|
292
|
+
### 基本參數
|
|
293
|
+
>
|
|
294
|
+
| # | 參數 Attribute | 型別 Type | 預設值 Default | 選項 Option | 說明 Description |
|
|
295
|
+
| --- | ----------------- | --------- | -------------- | ----------------------------------------- | --------------- |
|
|
296
|
+
| 1 | `selected-data` | `String` `Number` `Object` | `none` | | 當前選擇的值(類似平常`vModel`的用法) |
|
|
297
|
+
| 2 | `opts` | `Array` | `[]` | | 選項列表,只接受`StringArray` `NumberArray` `ObjectArray`,且請陣列內容格
|
|
298
|
+
| 3 | `placeholder` | `String` | `'請選擇'` | | 未選擇內容時的提示文字 |
|
|
299
|
+
| 4 | `search-placeholder` | `String` | `''` | | 自定義搜尋框的提示文字。如未設定,有選擇內容時會將選擇的內容當成提示文字,而未選擇時則顯示placeholder(參數3)的值 |
|
|
300
|
+
| 5 | `trigger-class` | `String` | `''` | | 預覽框與輸入框的樣式CLSS,可用於驗證提示 |
|
|
301
|
+
| 6 | `list-put` | `String`| `'body'` | | 列表Dom放置位置 |
|
|
302
|
+
|
|
303
|
+
### 清除按鈕參數
|
|
304
|
+
| # | 參數 Attribute | 型別 Type | 預設值 Default | 選項 Option | 說明 Description |
|
|
305
|
+
| --- | ----------------- | --------- | -------------- | ----------------------------------------- | --------------- |
|
|
306
|
+
| 7 | `clear-btn` | `Boolean` | `True` | `True` `False` |是否顯示清除按鈕 |
|
|
307
|
+
| 8 | `clear-placeholder` | `String` | `'清除選擇'` | |清除按鈕的文字 |
|
|
308
|
+
| 9 | `reset-value` | `String` `Number` `Object` | `''` | | 清空選擇替換的預設值 |
|
|
309
|
+
### Object陣列選項清單用參數
|
|
310
|
+
| # | 參數 Attribute | 型別 Type | 預設值 Default | 選項 Option | 說明 Description |
|
|
311
|
+
| --- | ------------------- | --------- | ----------------------------------------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
312
|
+
| 10 | `preview-key` | `String` | `''` | 顯示指定字符連接。請填入符合規定的字串 | 用+號連接keyName和自定義字串,自定義字串請用''包起來(空白同理)。範例`enName + ' ( ' + lastName + ' ' + firstName + ' ) ' ` |
|
|
313
|
+
| 11 | `filter-key` | `Array` | `['name']` | 字串陣列 | 傳入需要進行篩選比對的keyName,可多個。 |
|
|
314
|
+
| 12 | `binding-key` | `String` | `''` | 本參數僅供提傳入供單一且為唯一值得keyName,一般用於ID。 | 如傳入的選項列表為複雜object陣列,但選中的值是某個key。可傳入Key Name以供Component辨並從傳入的陣列中找出該object。如綁定的是整個object,不需使用此參數。 |
|
|
315
|
+
| 13 | `is-undefined-hint` | `String` | `'⚠️ 未找到符合原選擇的選項,可能相關資料曾發生異動` | 字串 | 傳入binding-key但比對失敗,在opts(參數2)中找不到binding-key內容與selected-data值相符的選項,用於提示的預覽文字 |
|
|
316
|
+
| 14 | `html-option` | `Boolean` | `false` | `true` `false` | 是否啟用自訂選項樣式,用法請參考範例3。請注意就算起啟用preview-key還是必須傳入。|
|
|
317
|
+
### 可用方法
|
|
318
|
+
| Function | 說明 Description |
|
|
319
|
+
| --------- | ------------------ |
|
|
320
|
+
| select | 選到項目要做什麼 |
|
|
321
|
+
|
|
322
|
+
### .
|
|
323
|
+
### .
|
|
324
|
+
### .
|
|
94
325
|
----
|
|
95
326
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
### Layout
|
|
99
|
-
| Layout 名稱 | 說明 | 在 jobdone-shared-files 內? |
|
|
100
|
-
|-------------------|-------------------------|-----------------------------|
|
|
101
|
-
| LayoutBase | 客製化的 Bootstrap (5.3.0) | 是 |
|
|
102
|
-
| LayoutProject | Project 使用的共用樣式,含開合雙欄樣式 | 是 |
|
|
103
|
-
| LayoutSinglePage | 單頁版面的樣式,如:登入、錯誤頁、條款等 | 是 |
|
|
104
|
-
| LayoutTwoColumn | 雙欄可開合樣式 | 是 |
|
|
105
|
-
| LayoutMobile | 針對 (底層) 手機的共用樣式 | 是 |
|
|
106
|
-
| LayoutDocs | 針對條文調整的樣式 | 否 |
|
|
107
|
-
| LayoutInnerColumn | 針對 Project 雙欄樣式內的雙欄樣式 | 否 |
|
|
108
|
-
| LayoutOutside | 針對 (底層) Project 外的共用樣式 | 否 |
|
|
109
|
-
|
|
110
|
-
### Common
|
|
111
|
-
|
|
112
|
-
- Animation:目前只有 Fade In keyframe
|
|
113
|
-
- SelectableTable:勾選 tr focus style
|
|
114
|
-
- thumbnail-group:方形大頭貼與小大頭貼相疊 style
|
|
115
|
-
- filepond:客製化 filepond 樣式
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
# Directives
|
|
120
|
-
|
|
121
|
-
## SelectPlaceholder 使用範例:
|
|
327
|
+
# **Directives**
|
|
328
|
+
## **01.SelectPlaceholder**
|
|
122
329
|
|
|
330
|
+
`<select>`未選擇值時加上Placeholder樣式
|
|
123
331
|
`null` `false` `undefined` `''` 預設會被判斷為Placeholder,如有增減需求請參考option可用變數。
|
|
124
332
|
|
|
333
|
+
### 使用範例:
|
|
125
334
|
```
|
|
126
335
|
<template>
|
|
127
336
|
<select class="form-select"
|
|
@@ -149,18 +358,17 @@
|
|
|
149
358
|
</script>
|
|
150
359
|
```
|
|
151
360
|
|
|
152
|
-
|
|
361
|
+
### 可用變數
|
|
153
362
|
| # | 參數 Attribute | 型別 Type | 必填 required | 預設值 Default | 說明Description |
|
|
154
363
|
| --- | ---------------- |:---------:|:-------------:|:----------------:|:---------------- |
|
|
155
364
|
| 1 | value | -- | 是 | -- | 當前選擇內容的值 |
|
|
156
365
|
| 2 | placeholderValue | `Array` | 否 | [] | 除了預設會被判斷為Placeholder的值,如有其他需求可增加。|
|
|
157
366
|
| 3 | excludeValue | `Array` | 否 | [] | 如預設會被判斷為Placeholder的值,需被排除判斷,可在此處排除|
|
|
158
367
|
| 4 | unselectClass | `String` | 否 | '' | 如在未選擇值時希望有更多特殊樣式,可在此傳入Class。不需要加. |
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
368
|
+
|
|
369
|
+
### .
|
|
370
|
+
## 02.textareaAutoHeight
|
|
371
|
+
### 使用範例:
|
|
164
372
|
```
|
|
165
373
|
<template>
|
|
166
374
|
<textarea v-textarea-auto-height v-model="inputValue"></textarea>
|
|
@@ -172,4 +380,33 @@
|
|
|
172
380
|
const inputValue = ref(null)
|
|
173
381
|
|
|
174
382
|
</script>
|
|
175
|
-
```
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
## SCSS
|
|
389
|
+
|
|
390
|
+
### Layout
|
|
391
|
+
| Layout 名稱 | 說明 | 在 jobdone-shared-files 內? |
|
|
392
|
+
|-------------------|-------------------------|-----------------------------|
|
|
393
|
+
| LayoutBase | 客製化的 Bootstrap (5.3.0) | 是 |
|
|
394
|
+
| LayoutProject | Project 使用的共用樣式,含開合雙欄樣式 | 是 |
|
|
395
|
+
| LayoutSinglePage | 單頁版面的樣式,如:登入、錯誤頁、條款等 | 是 |
|
|
396
|
+
| LayoutTwoColumn | 雙欄可開合樣式 | 是 |
|
|
397
|
+
| LayoutMobile | 針對 (底層) 手機的共用樣式 | 是 |
|
|
398
|
+
| LayoutDocs | 針對條文調整的樣式 | 否 |
|
|
399
|
+
| LayoutInnerColumn | 針對 Project 雙欄樣式內的雙欄樣式 | 否 |
|
|
400
|
+
| LayoutOutside | 針對 (底層) Project 外的共用樣式 | 否 |
|
|
401
|
+
|
|
402
|
+
### Common
|
|
403
|
+
|
|
404
|
+
- Animation:目前只有 Fade In keyframe
|
|
405
|
+
- SelectableTable:勾選 tr focus style
|
|
406
|
+
- thumbnail-group:方形大頭貼與小大頭貼相疊 style
|
|
407
|
+
- filepond:客製化 filepond 樣式
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="autocomplete-select-component-trigger-content" ref="componentContentInput">
|
|
3
|
+
<!-- preview input -->
|
|
4
|
+
<button type="button" class='form-control autocomplete-select-component-display-selecting' :disabled="disabled"
|
|
5
|
+
:class="[triggerClass, previewInput]">{{
|
|
6
|
+
previewText
|
|
7
|
+
}}</button>
|
|
8
|
+
<!-- search input -->
|
|
9
|
+
<input class='form-control autocomplete-select-component-keyword-filter-input' :class="[triggerClass, searchInput]"
|
|
10
|
+
type="text" ref="keywordFilterInput" v-model="keyword"
|
|
11
|
+
:placeholder="searchPlaceholder == '' ? previewText : searchPlaceholder" maxlength="50"
|
|
12
|
+
@keydown.enter="keyboardSelectConfirm()" @keydown.up="keyboardSwitch(-1)" @keydown.down="keyboardSwitch(1)"
|
|
13
|
+
@change="keyboardSwitchIndexReset()">
|
|
14
|
+
<Teleport :to="listPut">
|
|
15
|
+
<div class="autocomplete-select-component-selector-content" :class="{ 'active': active }"
|
|
16
|
+
ref="componentContentList" :style="positionStyle">
|
|
17
|
+
<ul class="autocomplete-select-component-opt-list" ref="opt_list">
|
|
18
|
+
<li v-if="!listIsOnTop && clearBtn" class="border-bottom" :title="clearPlaceholder">
|
|
19
|
+
<button class="autocomplete-select-component-opt-item is-clear" type="button"
|
|
20
|
+
@click.stop="clearSelected()">
|
|
21
|
+
{{ clearPlaceholder }}
|
|
22
|
+
</button>
|
|
23
|
+
</li>
|
|
24
|
+
<li v-if="filterList?.length == 0">
|
|
25
|
+
<div class="autocomplete-select-component-opt-item is-nothing">
|
|
26
|
+
沒有符合條件的選項
|
|
27
|
+
</div>
|
|
28
|
+
</li>
|
|
29
|
+
<template v-if="active">
|
|
30
|
+
<li v-for="(opt, idx) in filterList" :key="idx" @click.stop="selectConfirm(opt)" :title="opt.name" :id="`autocomplete-select-component-opt-item_${idx}`">
|
|
31
|
+
<button class="autocomplete-select-component-opt-item" type="button"
|
|
32
|
+
:class="idx == keyboardSwitchIndex ? 'active' : ''">
|
|
33
|
+
<template v-if="htmlOption">
|
|
34
|
+
<slot name="option" :optData="opt"></slot>
|
|
35
|
+
</template>
|
|
36
|
+
<template v-else>
|
|
37
|
+
{{ previewAdjust(opt) }}
|
|
38
|
+
</template>
|
|
39
|
+
</button>
|
|
40
|
+
</li>
|
|
41
|
+
</template>
|
|
42
|
+
<li v-if="listIsOnTop && clearBtn" class="border-top" :title="clearPlaceholder">
|
|
43
|
+
<button class="autocomplete-select-component-opt-item is-clear" type="button"
|
|
44
|
+
@click.stop="clearSelected()">
|
|
45
|
+
{{ clearPlaceholder }}
|
|
46
|
+
</button>
|
|
47
|
+
</li>
|
|
48
|
+
</ul>
|
|
49
|
+
</div>
|
|
50
|
+
</Teleport>
|
|
51
|
+
</div>
|
|
52
|
+
</template>
|
|
53
|
+
<script setup>
|
|
54
|
+
// enum Data & Functions
|
|
55
|
+
|
|
56
|
+
// vue & bootstrap
|
|
57
|
+
import { ref, onMounted, onUnmounted, computed, toRaw } from 'vue'
|
|
58
|
+
|
|
59
|
+
// plugins
|
|
60
|
+
|
|
61
|
+
// vue components
|
|
62
|
+
|
|
63
|
+
const props = defineProps({
|
|
64
|
+
selectedData: {
|
|
65
|
+
required: true
|
|
66
|
+
},
|
|
67
|
+
opts: {
|
|
68
|
+
type: Array,
|
|
69
|
+
default: []
|
|
70
|
+
},
|
|
71
|
+
placeholder: {
|
|
72
|
+
type: String,
|
|
73
|
+
default: '請選擇'
|
|
74
|
+
},
|
|
75
|
+
searchPlaceholder: {
|
|
76
|
+
type: String,
|
|
77
|
+
default: ''
|
|
78
|
+
},
|
|
79
|
+
triggerClass: {
|
|
80
|
+
type: String,
|
|
81
|
+
default: ''
|
|
82
|
+
},
|
|
83
|
+
listPut: {
|
|
84
|
+
type: String,
|
|
85
|
+
default: 'body'
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
clearBtn: {
|
|
89
|
+
type: Boolean,
|
|
90
|
+
default: true
|
|
91
|
+
},
|
|
92
|
+
clearPlaceholder: {
|
|
93
|
+
type: String,
|
|
94
|
+
default: '清除選擇'
|
|
95
|
+
},
|
|
96
|
+
resetValue: {
|
|
97
|
+
default: ''
|
|
98
|
+
},
|
|
99
|
+
disabled:{
|
|
100
|
+
type:Boolean,
|
|
101
|
+
default: false
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
previewKey: {
|
|
106
|
+
type: String,
|
|
107
|
+
default: '',
|
|
108
|
+
},
|
|
109
|
+
bindingKey: {
|
|
110
|
+
type: String,
|
|
111
|
+
default: '',
|
|
112
|
+
},
|
|
113
|
+
filterKeys: {
|
|
114
|
+
type: Array,
|
|
115
|
+
default: ['name'],
|
|
116
|
+
},
|
|
117
|
+
isUndefinedHint: {
|
|
118
|
+
type: String,
|
|
119
|
+
default: '⚠️ 未找到符合原選擇的選項,可能相關資料曾發生異動',
|
|
120
|
+
},
|
|
121
|
+
htmlOption: {
|
|
122
|
+
type: Boolean,
|
|
123
|
+
default: false
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const emit = defineEmits(['select'])
|
|
130
|
+
|
|
131
|
+
// Just DOM
|
|
132
|
+
const componentContentInput = ref(null)
|
|
133
|
+
const componentContentList = ref(null)
|
|
134
|
+
const keywordFilterInput = ref(null)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
// 是否開啟下拉選單
|
|
138
|
+
const active = ref(false)
|
|
139
|
+
const domPosition = ref({ bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0, x: 0, y: 0 })
|
|
140
|
+
function activeSelector() {
|
|
141
|
+
if(props.disabled){
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
active.value = true
|
|
145
|
+
keywordFilterInput.value.focus()
|
|
146
|
+
domPosition.value = componentContentInput.value.getBoundingClientRect()
|
|
147
|
+
}
|
|
148
|
+
const listIsOnTop = ref(false)
|
|
149
|
+
const positionStyle = computed(() => {
|
|
150
|
+
let screenHeight = window.innerHeight;
|
|
151
|
+
let info = toRaw(domPosition.value)
|
|
152
|
+
listIsOnTop.value = info.top >= (screenHeight - info.bottom)
|
|
153
|
+
let str = ''
|
|
154
|
+
if (listIsOnTop.value) {
|
|
155
|
+
str = `bottom:${screenHeight - info.top}px;max-height:calc(${info.top}px - 10%);`
|
|
156
|
+
} else {
|
|
157
|
+
str = `top:${info.bottom}px;max-height:calc(${screenHeight - info.bottom}px - 10%);`
|
|
158
|
+
}
|
|
159
|
+
return `${str}left:${info.x}px;max-width:calc(90% - ${info.x}px);min-width:${Math.abs(info.x - info.right)}px;`
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
function leave() {
|
|
163
|
+
listIsOnTop.value = false
|
|
164
|
+
active.value = false
|
|
165
|
+
keyword.value = ''
|
|
166
|
+
// TODO SCROLL CLOSE
|
|
167
|
+
keyboardSwitchIndexReset()
|
|
168
|
+
keywordFilterInput.value.blur()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
// 確認傳入選單列表內的選項Type,只檢查第一個,請內容統一,不要傳奇怪的東西進來
|
|
174
|
+
const allowOptItemType = ['string', 'number', 'object']
|
|
175
|
+
const optItemType = computed(() => {
|
|
176
|
+
if (!Array.isArray(props.opts) || props.opts?.length < 1) {
|
|
177
|
+
return ''
|
|
178
|
+
}
|
|
179
|
+
let sampling = props.opts[0]
|
|
180
|
+
let type = typeof sampling
|
|
181
|
+
if (!allowOptItemType.includes(type)) {
|
|
182
|
+
return ''
|
|
183
|
+
}
|
|
184
|
+
if ((sampling instanceof Date) && (sampling instanceof RegExp) && Array.isArray(sampling)) {
|
|
185
|
+
return ''
|
|
186
|
+
}
|
|
187
|
+
return type?.toLowerCase()
|
|
188
|
+
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
//
|
|
192
|
+
// 篩選
|
|
193
|
+
const keyword = ref("")
|
|
194
|
+
const filterList = computed(() => {
|
|
195
|
+
if (optItemType.value === '') {
|
|
196
|
+
return []
|
|
197
|
+
}
|
|
198
|
+
if (keyword.value == '') {
|
|
199
|
+
return props.opts
|
|
200
|
+
}
|
|
201
|
+
let kwReplace = keyword.value.replace(/[.*+?^${}()|[\]\\]/g, "");
|
|
202
|
+
let regex = new RegExp(kwReplace, "i");
|
|
203
|
+
if (optItemType.value === 'object') {
|
|
204
|
+
let final = props.opts.filter(i => {
|
|
205
|
+
for (var idx = 0; idx < props.filterKeys.length; idx++) {
|
|
206
|
+
if (regex.test(i[props.filterKeys[idx].toString()])) {
|
|
207
|
+
return i
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
return final
|
|
212
|
+
}
|
|
213
|
+
return props.opts.filter(i => {
|
|
214
|
+
if (regex.test(i)) {
|
|
215
|
+
return i
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
// 從選項列表中挖出整個選中的item。因選中的值(可能是某個key),挖不到就直接吐props的值出來。
|
|
221
|
+
const selectedItem = computed(() => {
|
|
222
|
+
if (!props.selectedData) {
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// 如果選中的值是某個Key,需自行指定bindingKey
|
|
227
|
+
if (props.bindingKey !== '') {
|
|
228
|
+
return props.opts.find(i => i[props.bindingKey.toString()] == props.selectedData) ?? props.selectedData
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return props.selectedData
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
// 選項的展示 與 選中的值
|
|
236
|
+
function previewAdjust(itemObj) {
|
|
237
|
+
if (props.previewKey !== '') {
|
|
238
|
+
let strAry = props.previewKey.split('+')
|
|
239
|
+
let obj = toRaw(itemObj)
|
|
240
|
+
let isUndefined = false
|
|
241
|
+
|
|
242
|
+
let finalStr = strAry.reduce((acc, cur) => {
|
|
243
|
+
let v = cur.trim()
|
|
244
|
+
if (v.match(/'[^']+'/g)) {
|
|
245
|
+
return acc + v.substring(1, v.length - 1)
|
|
246
|
+
} else {
|
|
247
|
+
if (obj[v] === undefined) {
|
|
248
|
+
isUndefined = true
|
|
249
|
+
}
|
|
250
|
+
return acc + obj[v]
|
|
251
|
+
}
|
|
252
|
+
}, '')
|
|
253
|
+
if (isUndefined) {
|
|
254
|
+
return props.isUndefinedHint
|
|
255
|
+
}
|
|
256
|
+
return finalStr ? finalStr : itemObj
|
|
257
|
+
}
|
|
258
|
+
return itemObj
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const previewText = computed(() => {
|
|
262
|
+
if (!selectedItem.value || Object.keys(selectedItem.value).length === 0) {
|
|
263
|
+
return props.placeholder
|
|
264
|
+
}
|
|
265
|
+
return previewAdjust(selectedItem.value)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
// 清除選擇內容
|
|
269
|
+
function clearSelected() {
|
|
270
|
+
emit('select', props.resetValue)
|
|
271
|
+
leave()
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// 選擇送出
|
|
275
|
+
function selectConfirm(v) {
|
|
276
|
+
if (!v) {
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
emit('select', JSON.parse(JSON.stringify(v)))
|
|
280
|
+
leave()
|
|
281
|
+
keyboardSwitchIndexReset()
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 鍵盤送出
|
|
285
|
+
function keyboardSelectConfirm() {
|
|
286
|
+
if (filterList.value?.length) {
|
|
287
|
+
selectConfirm(filterList.value[keyboardSwitchIndex.value])
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 鍵盤挑選
|
|
292
|
+
const keyboardSwitchIndex = ref(0)
|
|
293
|
+
function keyboardSwitch(v) {
|
|
294
|
+
if (filterList.value?.length < 1) {
|
|
295
|
+
keyboardSwitchIndexReset()
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
let idx = Number(keyboardSwitchIndex.value) + v
|
|
299
|
+
|
|
300
|
+
if (idx < 0) {
|
|
301
|
+
keyboardSwitchIndex.value = filterList.value.length - 1
|
|
302
|
+
document.getElementById(`autocomplete-select-component-opt-item_${keyboardSwitchIndex.value}`)?.scrollIntoView()
|
|
303
|
+
return
|
|
304
|
+
}
|
|
305
|
+
if (idx > filterList.value.length - 1) {
|
|
306
|
+
keyboardSwitchIndexReset()
|
|
307
|
+
document.getElementById(`autocomplete-select-component-opt-item_${keyboardSwitchIndex.value}`)?.scrollIntoView()
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
keyboardSwitchIndex.value = idx
|
|
311
|
+
document.getElementById(`autocomplete-select-component-opt-item_${keyboardSwitchIndex.value}`)?.scrollIntoView()
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function keyboardSwitchIndexReset() {
|
|
315
|
+
keyboardSwitchIndex.value = 0
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 樣式
|
|
319
|
+
const previewInput = computed(() => {
|
|
320
|
+
if (previewText.value == props.placeholder) {
|
|
321
|
+
return 'autocomplete-select-component-value-null'
|
|
322
|
+
}
|
|
323
|
+
return ''
|
|
324
|
+
})
|
|
325
|
+
const searchInput = computed(() => {
|
|
326
|
+
if (active.value) {
|
|
327
|
+
return 'active'
|
|
328
|
+
}
|
|
329
|
+
return ''
|
|
330
|
+
})
|
|
331
|
+
function initTrigger() {
|
|
332
|
+
if (componentContentInput.value.contains(event.target) || componentContentList.value.contains(event.target)) {
|
|
333
|
+
activeSelector()
|
|
334
|
+
} else {
|
|
335
|
+
leave()
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
onMounted(() => {
|
|
339
|
+
window.addEventListener("click", initTrigger);
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
onUnmounted(() => {
|
|
343
|
+
window.removeEventListener("click", initTrigger);
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
</script>
|
|
347
|
+
<style lang="scss" scoped>
|
|
348
|
+
* {
|
|
349
|
+
word-break: break-word;
|
|
350
|
+
overflow-wrap: break-word;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.autocomplete-select-component-trigger-content {
|
|
354
|
+
position: relative;
|
|
355
|
+
width: 100%;
|
|
356
|
+
|
|
357
|
+
.form-control {
|
|
358
|
+
background-image: url("data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3e%3cpath fill=%27none%27 stroke=%27%232D4155%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%272%27 d=%27m2 5 6 6 6-6%27/%3e%3c/svg%3e");
|
|
359
|
+
background-repeat: no-repeat;
|
|
360
|
+
background-position: right 0.75rem center;
|
|
361
|
+
background-size: 16px 12px;
|
|
362
|
+
text-align: start;
|
|
363
|
+
width: 100%;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.autocomplete-select-component-value-null {
|
|
367
|
+
color: var(--bs-secondary-color);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.autocomplete-select-component-display-selecting {
|
|
371
|
+
position: absolute;
|
|
372
|
+
top: 0;
|
|
373
|
+
left: 0;
|
|
374
|
+
z-index: 1;
|
|
375
|
+
white-space: nowrap;
|
|
376
|
+
overflow: hidden;
|
|
377
|
+
text-overflow: ellipsis;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.autocomplete-select-component-keyword-filter-input {
|
|
381
|
+
opacity: 0;
|
|
382
|
+
position: relative;
|
|
383
|
+
|
|
384
|
+
&.active {
|
|
385
|
+
opacity: 1;
|
|
386
|
+
z-index: 2;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.autocomplete-select-component-selector-content {
|
|
392
|
+
visibility: hidden;
|
|
393
|
+
display: none;
|
|
394
|
+
position: fixed;
|
|
395
|
+
z-index: 2000;
|
|
396
|
+
margin-top: 0.25rem;
|
|
397
|
+
margin-bottom: 0.25rem;
|
|
398
|
+
max-height: 76vh;
|
|
399
|
+
overflow: auto;
|
|
400
|
+
|
|
401
|
+
ul.autocomplete-select-component-opt-list {
|
|
402
|
+
background: #fff;
|
|
403
|
+
list-style: none;
|
|
404
|
+
border: 1px solid var(--gray-400);
|
|
405
|
+
border-radius: var(--bs-border-radius);
|
|
406
|
+
margin: 0;
|
|
407
|
+
padding: 0;
|
|
408
|
+
overflow: auto;
|
|
409
|
+
padding: .25rem 0;
|
|
410
|
+
|
|
411
|
+
>li {
|
|
412
|
+
width: 100%;
|
|
413
|
+
overflow: auto;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.autocomplete-select-component-opt-item {
|
|
417
|
+
background-color: transparent;
|
|
418
|
+
border: none;
|
|
419
|
+
padding: .25rem .5rem;
|
|
420
|
+
word-break: break-word;
|
|
421
|
+
width: 100%;
|
|
422
|
+
overflow: hidden;
|
|
423
|
+
text-align: start;
|
|
424
|
+
text-overflow: ellipsis;
|
|
425
|
+
color: var(--gray-600);
|
|
426
|
+
|
|
427
|
+
&.is-nothing {
|
|
428
|
+
color: var(--bs-danger);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
&.is-clear {
|
|
432
|
+
color: var(--gray-500);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
&:hover,
|
|
436
|
+
&.is-clear:hover {
|
|
437
|
+
background: rgba(var(--bs-primary-rgb), 1) !important;
|
|
438
|
+
color: #fff !important;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
&.active {
|
|
442
|
+
background: rgba(var(--bs-primary-rgb), .1);
|
|
443
|
+
color: var(--bs-primary);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
&.active {
|
|
449
|
+
display: block;
|
|
450
|
+
box-shadow: var(--bs-box-shadow);
|
|
451
|
+
visibility: visible;
|
|
452
|
+
z-index: 2000;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
</style>
|
package/package.json
CHANGED
package/tagEditor.vue
CHANGED
|
@@ -26,21 +26,7 @@
|
|
|
26
26
|
</div>
|
|
27
27
|
</template>
|
|
28
28
|
<script>
|
|
29
|
-
// enum Data & Functions
|
|
30
|
-
// import { } from '../common/enum.js'
|
|
31
|
-
|
|
32
|
-
// vue & bootstrap
|
|
33
29
|
import { ref, reactive, onMounted, computed } from 'vue'
|
|
34
|
-
// import { } from 'bootstrap';
|
|
35
|
-
|
|
36
|
-
// plugins
|
|
37
|
-
// import { v4 as uuidv4 } from 'uuid';
|
|
38
|
-
// import { useVuelidate } from '@vuelidate/core'
|
|
39
|
-
// import { required } from '@vuelidate/validators'
|
|
40
|
-
// import { SwalSuccess, SwalConfirmDelete } from '../common/sweetalert.js';
|
|
41
|
-
|
|
42
|
-
// vue components
|
|
43
|
-
// import Loading from "./vueLoadingOverlay.vue";
|
|
44
30
|
|
|
45
31
|
export default {
|
|
46
32
|
props: {
|