jobdone-shared-files 1.1.10 → 1.1.12
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 +1 -1
- package/tagEditor.vue +156 -112
package/package.json
CHANGED
package/tagEditor.vue
CHANGED
|
@@ -13,131 +13,156 @@
|
|
|
13
13
|
</div>
|
|
14
14
|
</div>
|
|
15
15
|
</template>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
<template v-if="!disabled && !isMaxTagCountReached">
|
|
17
|
+
<input class="new-tag-input" list="datalistOptions" type="text" :placeholder="placeholder"
|
|
18
|
+
:maxlength="singleTagMaxLength || 50" v-model.trim="newTag" @keydown.enter="addTag($event)"
|
|
19
|
+
@keyup="keyupFunction">
|
|
20
|
+
<datalist id="datalistOptions" v-if="autoCompleteShow">
|
|
21
|
+
<option :value="opt" v-for="(opt, idx) in autoCompleteOption" :key="idx">{{ opt }}</option>
|
|
22
|
+
</datalist>
|
|
23
|
+
</template>
|
|
21
24
|
</div>
|
|
22
25
|
</div>
|
|
23
26
|
<div class="helper-text-box" v-if="!disabled">
|
|
24
|
-
<small class="helper-text"
|
|
27
|
+
<small class="helper-text">在輸入框中輸入文字後,按下鍵盤的「Enter鍵」即可增加標籤。標籤僅接受中文、英文、數字、下底線、<span class="text-danger">長度限制
|
|
28
|
+
{{ singleTagMaxLength }} 個字元</span>。
|
|
29
|
+
<template v-if="maxTagCount > 0">
|
|
30
|
+
<span class="text-danger">最多新增 {{ maxTagCount }} 個標籤</span>,
|
|
31
|
+
</template>標籤不可重複。</small>
|
|
25
32
|
</div>
|
|
26
33
|
</div>
|
|
27
34
|
</template>
|
|
28
|
-
<script>
|
|
35
|
+
<script setup>
|
|
29
36
|
import { ref, reactive, onMounted, computed } from 'vue'
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
type: Array,
|
|
35
|
-
default: () => []
|
|
36
|
-
},
|
|
37
|
-
placeholder: {
|
|
38
|
-
type: String,
|
|
39
|
-
default: '請在此處輸入標籤文字'
|
|
40
|
-
},
|
|
41
|
-
autoComplete: {
|
|
42
|
-
type: Boolean,
|
|
43
|
-
default: true
|
|
44
|
-
},
|
|
45
|
-
autoCompleteOption: {
|
|
46
|
-
type: Array,
|
|
47
|
-
default: () => []
|
|
48
|
-
},
|
|
49
|
-
disabled: {
|
|
50
|
-
type: Boolean,
|
|
51
|
-
default: false
|
|
52
|
-
}
|
|
37
|
+
const props = defineProps({
|
|
38
|
+
defaultTagList: {
|
|
39
|
+
type: Array,
|
|
40
|
+
default: () => []
|
|
53
41
|
},
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
42
|
+
placeholder: {
|
|
43
|
+
type: String,
|
|
44
|
+
default: '請輸入標籤文字'
|
|
45
|
+
},
|
|
46
|
+
autoComplete: {
|
|
47
|
+
type: Boolean,
|
|
48
|
+
default: true
|
|
49
|
+
},
|
|
50
|
+
autoCompleteOption: {
|
|
51
|
+
type: Array,
|
|
52
|
+
default: () => []
|
|
53
|
+
},
|
|
54
|
+
disabled: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: false
|
|
57
|
+
},
|
|
58
|
+
singleTagMaxLength: {
|
|
59
|
+
type: Number,
|
|
60
|
+
default: 50
|
|
61
|
+
},
|
|
62
|
+
maxTagCount: {
|
|
63
|
+
type: Number,
|
|
64
|
+
default: 0
|
|
65
|
+
}
|
|
66
|
+
})
|
|
76
67
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
function keyupFunction() {
|
|
82
|
-
if (props.autoCompleteOption) {
|
|
83
|
-
updateAutoCompleteOption()
|
|
84
|
-
}
|
|
85
|
-
newTag.value = newTag.value.replace(/[^\a-\z\A-\Z0-9\u4E00-\u9FA5]/g, '_')
|
|
86
|
-
}
|
|
68
|
+
// 基本資料
|
|
69
|
+
const tagAry = reactive([])
|
|
70
|
+
const newTag = ref('')
|
|
87
71
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
72
|
+
const autoCompleteShow = computed(() => {
|
|
73
|
+
let show = props.autoComplete
|
|
74
|
+
if (!newTag.value || props.autoCompleteOption.length < 1) {
|
|
75
|
+
show = false
|
|
76
|
+
}
|
|
77
|
+
return show
|
|
78
|
+
})
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
function reset() {
|
|
105
|
-
tagAry.splice(0, tagAry.length)
|
|
106
|
-
newTag.value = ''
|
|
107
|
-
}
|
|
80
|
+
// 輸入時 AutoComplete
|
|
81
|
+
var autoCompleteTimer
|
|
82
|
+
function updateAutoCompleteOption() {
|
|
83
|
+
clearTimeout(autoCompleteTimer);
|
|
84
|
+
if (newTag.value) {
|
|
85
|
+
autoCompleteTimer = setTimeout(() => {
|
|
86
|
+
emit('update-auto-complete-option', newTag.value);
|
|
87
|
+
}, 500);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
108
90
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
91
|
+
// 刷新預設選中內容
|
|
92
|
+
function getDefaultTag(updateData = []) {
|
|
93
|
+
tagAry.splice(0, tagAry.length, ...updateData)
|
|
94
|
+
}
|
|
95
|
+
function keyupFunction() {
|
|
96
|
+
if (props.autoCompleteOption) {
|
|
97
|
+
updateAutoCompleteOption()
|
|
98
|
+
}
|
|
99
|
+
newTag.value = newTag.value.replace(/[^\a-\z\A-\Z0-9\u4E00-\u9FA5]/g, '_')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 重複標籤禁止新增
|
|
103
|
+
var isDuplicate = computed(() => {
|
|
104
|
+
return newTag.value ? !!tagAry.find(tag => tag == newTag.value) : false
|
|
105
|
+
})
|
|
106
|
+
const isMaxTagCountReached = computed(() => {
|
|
107
|
+
return props.maxTagCount > 0 ? tagAry.length >= props.maxTagCount : false
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// 增加Tag
|
|
111
|
+
function addTag(event) {
|
|
112
|
+
// 檢查是否為中文輸入法組合狀態
|
|
113
|
+
if (event.isComposing || event.keyCode === 229) {
|
|
114
|
+
return
|
|
125
115
|
}
|
|
116
|
+
if (newTag.value && !isDuplicate.value && !isMaxTagCountReached.value) {
|
|
117
|
+
tagAry.push(newTag.value)
|
|
118
|
+
}
|
|
119
|
+
newTag.value = ''
|
|
120
|
+
}
|
|
121
|
+
// 移除Tag
|
|
122
|
+
function removeTag(index) {
|
|
123
|
+
tagAry.splice(index, 1)
|
|
126
124
|
}
|
|
125
|
+
function reset() {
|
|
126
|
+
tagAry.splice(0, tagAry.length)
|
|
127
|
+
newTag.value = ''
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
onMounted(() => {
|
|
131
|
+
getDefaultTag(props.defaultTagList)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
defineExpose({
|
|
135
|
+
tagAry,
|
|
136
|
+
newTag,
|
|
137
|
+
addTag,
|
|
138
|
+
removeTag,
|
|
139
|
+
updateAutoCompleteOption,
|
|
140
|
+
autoCompleteTimer,
|
|
141
|
+
autoCompleteShow,
|
|
142
|
+
reset,
|
|
143
|
+
keyupFunction,
|
|
144
|
+
isDuplicate,
|
|
145
|
+
getDefaultTag,
|
|
146
|
+
isMaxTagCountReached
|
|
147
|
+
})
|
|
148
|
+
const emit = defineEmits(['update-auto-complete-option'])
|
|
149
|
+
|
|
127
150
|
</script>
|
|
128
151
|
|
|
129
152
|
<style lang="scss" scoped>
|
|
130
153
|
.tag-editor {
|
|
131
|
-
background: var(--bs-gray-100);
|
|
132
|
-
border-radius: var(--bs-border-radius);
|
|
133
|
-
padding: .
|
|
154
|
+
background: var(--bs-gray-100, #f9f9fd);
|
|
155
|
+
border-radius: var(--bs-border-radius-lg, 0.5rem);
|
|
156
|
+
padding: .75rem;
|
|
134
157
|
|
|
135
158
|
.helper-text-box {
|
|
159
|
+
margin-top: .25rem;
|
|
136
160
|
text-align: start;
|
|
137
161
|
|
|
138
162
|
.helper-text {
|
|
139
|
-
|
|
140
|
-
|
|
163
|
+
line-height: 125%;
|
|
164
|
+
font-size: 0.875rem;
|
|
165
|
+
color: var(--bs-gray-500, #A9B3BD);
|
|
141
166
|
}
|
|
142
167
|
}
|
|
143
168
|
|
|
@@ -158,9 +183,9 @@ export default {
|
|
|
158
183
|
position: absolute;
|
|
159
184
|
top: calc(100% + 0.25rem);
|
|
160
185
|
background: #fff;
|
|
161
|
-
border: 1px solid var(--bs-gray-300);
|
|
162
|
-
border-radius: var(--bs-border-radius);
|
|
163
|
-
box-shadow: var(--bs-box-shadow);
|
|
186
|
+
border: 1px solid var(--bs-gray-300, #DBE2E7);
|
|
187
|
+
border-radius: var(--bs-border-radius, 0.375rem);
|
|
188
|
+
box-shadow: var(--bs-box-shadow, 0 0.5rem 1rem rgba(73, 85, 98, 0.15));
|
|
164
189
|
}
|
|
165
190
|
|
|
166
191
|
.opt-item {
|
|
@@ -180,7 +205,8 @@ export default {
|
|
|
180
205
|
display: flex;
|
|
181
206
|
align-items: center;
|
|
182
207
|
flex-wrap: wrap;
|
|
183
|
-
|
|
208
|
+
gap: .25rem;
|
|
209
|
+
padding: .5rem;
|
|
184
210
|
|
|
185
211
|
&.is-disabled:hover {
|
|
186
212
|
cursor: not-allowed;
|
|
@@ -188,22 +214,41 @@ export default {
|
|
|
188
214
|
}
|
|
189
215
|
|
|
190
216
|
.new-tag-input {
|
|
217
|
+
height: 32px;
|
|
191
218
|
all: unset;
|
|
192
219
|
flex-grow: 1;
|
|
193
|
-
|
|
194
|
-
background: var(--bs-gray-100);
|
|
220
|
+
background: var(--bs-gray-100, #f9f9fd);
|
|
195
221
|
padding: .25rem .75rem;
|
|
196
222
|
border-radius: 99px;
|
|
197
223
|
border: 1px solid rgba(#000, 0);
|
|
224
|
+
transition: .2s ease-out;
|
|
225
|
+
border: 1px solid var(--bs-gray-200, #e8edf4);
|
|
226
|
+
outline-width: 3px;
|
|
227
|
+
outline-color: rgba(var(--purple-500-rgb, 100, 122, 241), 0);
|
|
228
|
+
outline-style: solid;
|
|
229
|
+
|
|
230
|
+
&::placeholder {
|
|
231
|
+
color: var(--bs-secondary-color, rgba(103, 120, 137, 0.5));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
&:focus {
|
|
235
|
+
outline-width: 3px;
|
|
236
|
+
outline-color: rgba(var(--purple-500-rgb, 100, 122, 241), .25);
|
|
237
|
+
outline-style: solid;
|
|
238
|
+
}
|
|
198
239
|
}
|
|
199
240
|
|
|
200
241
|
.badge.rounded-pill {
|
|
201
|
-
margin: 0 .25rem .25rem 0;
|
|
202
242
|
display: inline-flex;
|
|
203
243
|
align-items: stretch;
|
|
204
244
|
overflow: hidden;
|
|
245
|
+
height: 32px;
|
|
246
|
+
padding: .5rem .75rem;
|
|
247
|
+
margin: 0;
|
|
205
248
|
|
|
206
249
|
.tag-text {
|
|
250
|
+
font-size: .75rem;
|
|
251
|
+
font-weight: 500;
|
|
207
252
|
margin: auto;
|
|
208
253
|
overflow: hidden;
|
|
209
254
|
text-overflow: ellipsis;
|
|
@@ -217,7 +262,7 @@ export default {
|
|
|
217
262
|
display: block;
|
|
218
263
|
content: ' ';
|
|
219
264
|
height: 100%;
|
|
220
|
-
border-left: 1px solid var(--bs-gray-300);
|
|
265
|
+
border-left: 1px solid var(--bs-gray-300, #DBE2E7);
|
|
221
266
|
margin: 0 0.25rem;
|
|
222
267
|
}
|
|
223
268
|
}
|
|
@@ -231,7 +276,7 @@ export default {
|
|
|
231
276
|
.material-icons {
|
|
232
277
|
font-size: 16px;
|
|
233
278
|
line-height: 16px;
|
|
234
|
-
color: var(--bs-purple);
|
|
279
|
+
color: var(--bs-purple, #647AF1);
|
|
235
280
|
opacity: .5;
|
|
236
281
|
transition: .3s;
|
|
237
282
|
}
|
|
@@ -245,6 +290,5 @@ export default {
|
|
|
245
290
|
}
|
|
246
291
|
}
|
|
247
292
|
}
|
|
248
|
-
|
|
249
293
|
}
|
|
250
294
|
</style>
|