@truenewx/tnxvue3 3.0.18 → 3.4.1

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.
Files changed (55) hide show
  1. package/eslint.config.cjs +58 -0
  2. package/index.html +12 -12
  3. package/package.json +16 -40
  4. package/src/aj-captcha/Verify/VerifySlide.vue +9 -9
  5. package/src/aj-captcha/api/index.js +2 -10
  6. package/src/aj-captcha/utils/ase.js +1 -1
  7. package/src/bootstrap-vue/alert/Alert.vue +79 -79
  8. package/src/bootstrap-vue/button/Button.vue +40 -40
  9. package/src/bootstrap-vue/enum-select/EnumSelect.vue +4 -5
  10. package/src/bootstrap-vue/form/Form.vue +320 -320
  11. package/src/bootstrap-vue/form/FormGroup.vue +73 -73
  12. package/src/bootstrap-vue/loading-icon/LoadingIcon.vue +46 -46
  13. package/src/bootstrap-vue/paged/Paged.vue +119 -119
  14. package/src/bootstrap-vue/progress/Progress.vue +58 -58
  15. package/src/bootstrap-vue/query-table/QueryTable.vue +84 -84
  16. package/src/bootstrap-vue/region-cascader/RegionCascader.vue +119 -119
  17. package/src/bootstrap-vue/select/Select.vue +375 -375
  18. package/src/bootstrap-vue/submit-form/SubmitForm.vue +180 -176
  19. package/src/bootstrap-vue/tags-input/TagsInput.vue +64 -64
  20. package/src/bootstrap-vue/tnxbsv.css +107 -107
  21. package/src/bootstrap-vue/tnxbsv.js +113 -79
  22. package/src/bootstrap-vue/upload/Upload.vue +173 -173
  23. package/src/element-plus/avatar/Avatar.vue +8 -9
  24. package/src/element-plus/button/Button.vue +2 -2
  25. package/src/element-plus/curd/Curd.vue +20 -23
  26. package/src/element-plus/date-picker/DatePicker.vue +1 -1
  27. package/src/element-plus/detail-form/DetailForm.vue +1 -1
  28. package/src/element-plus/dialog/Dialog.vue +18 -20
  29. package/src/element-plus/drawer/Drawer.vue +10 -9
  30. package/src/element-plus/edit-table/EditTable.vue +3 -3
  31. package/src/element-plus/enum-select/EnumSelect.vue +3 -3
  32. package/src/element-plus/enum-view/EnumView.vue +41 -41
  33. package/src/element-plus/fetch-cascader/FetchCascader.vue +3 -4
  34. package/src/element-plus/fetch-select/FetchSelect.vue +10 -11
  35. package/src/element-plus/fetch-tags/FetchTags.vue +4 -5
  36. package/src/element-plus/fss-upload/FssUpload.vue +18 -18
  37. package/src/element-plus/fss-view/FssView.vue +1 -1
  38. package/src/element-plus/icon/Icon.vue +15 -0
  39. package/src/element-plus/input-dropdown/InputDropdown.vue +74 -74
  40. package/src/element-plus/query-form/QueryForm.vue +1 -1
  41. package/src/element-plus/query-table/QueryTable.vue +22 -26
  42. package/src/element-plus/region-cascader/RegionCascader.vue +4 -5
  43. package/src/element-plus/select/Select.vue +2 -2
  44. package/src/element-plus/steps-nav/StepsNav.vue +3 -4
  45. package/src/element-plus/submit-form/SubmitForm.vue +36 -39
  46. package/src/element-plus/tabs/Tabs.vue +1 -1
  47. package/src/element-plus/tnxel.css +22 -0
  48. package/src/element-plus/tnxel.js +125 -106
  49. package/src/element-plus/toolbar/ToolBarItem.js +15 -15
  50. package/src/element-plus/toolbar/Toolbar.vue +56 -56
  51. package/src/element-plus/transfer/Transfer.vue +8 -9
  52. package/src/element-plus/upload/Upload.vue +24 -24
  53. package/src/tnxvue-router.js +4 -4
  54. package/src/tnxvue-validator.js +8 -2
  55. package/src/tnxvue.js +14 -28
@@ -1,375 +1,375 @@
1
- <template>
2
- <BFormRadioGroup class="tnxbsv-select tnxbsv-radio-group"
3
- v-model="model"
4
- :options="items"
5
- :value-field="valueName"
6
- :text-field="textName"
7
- :buttons="selector === 'radio-button'"
8
- button-variant="outline-primary"
9
- v-if="items && (selector==='radio' || selector === 'radio-button')"/>
10
- <BDropdown class="tnxbsv-select tnxbsv-dropdown"
11
- :key="groupKey"
12
- :text="currentText"
13
- :variant="theme"
14
- :size="size"
15
- v-else-if="selector==='dropdown'">
16
- <BDropdownItem class="empty-item" :active="isSelected(emptyValue)" @click="select(emptyValue)" v-if="empty">
17
- <span>{{ emptyText || '&nbsp;' }}</span>
18
- </BDropdownItem>
19
- <template v-if="items">
20
- <BDropdownItem v-for="(item, index) of items" :key="index"
21
- :active="isSelected(item[valueName])"
22
- :disabled="item.disabled"
23
- @click="select(item[valueName])"
24
- >
25
- <slot name="option" :item="item" v-if="$slots.option"></slot>
26
- <template v-else>
27
- <i :class="item[iconName]" v-if="item[iconName]"></i>
28
- <span>{{ item[textName] }}</span>
29
- </template>
30
- </BDropdownItem>
31
- </template>
32
- <BDropdownItem v-else>
33
- <LoadingIcon/>
34
- </BDropdownItem>
35
- </BDropdown>
36
- <BFormSelect class="tnxbsv-select"
37
- :class="{'is-empty': model === emptyValue}"
38
- v-model="model"
39
- :key="groupKey"
40
- :variant="theme"
41
- :options="items"
42
- :value-field="valueName"
43
- :text-field="textName"
44
- :size="size"
45
- v-else-if="items">
46
- <template #first v-if="empty">
47
- <BFormSelectOption class="empty-item" :value="emptyValue">{{ emptyText || '&nbsp;' }}</BFormSelectOption>
48
- </template>
49
- <template #option="{value, text}">
50
- <slot name="option" :item="getItem(value)" v-if="$slots.option"></slot>
51
- <template v-else>
52
- <i :class="getItem(value)[iconName]" v-if="getItem(value)[iconName]"></i>
53
- <span>{{ text }}</span>
54
- </template>
55
- </template>
56
- </BFormSelect>
57
- <div class="flex-v-center" v-else>
58
- <LoadingIcon/>
59
- </div>
60
- </template>
61
-
62
- <script>
63
- import {BFormRadioGroup, BDropdown, BDropdownItem, BFormSelect, BFormSelectOption} from 'bootstrap-vue-next';
64
- import LoadingIcon from '../loading-icon/LoadingIcon.vue';
65
-
66
- export const isMultiSelector = function (selector) {
67
- return selector === 'checkbox' || selector === 'tags' || selector === 'multi-select' || selector === 'texts';
68
- }
69
- export default {
70
- name: 'TnxbsvSelect',
71
- components: {BFormRadioGroup, BDropdown, BDropdownItem, BFormSelect, BFormSelectOption, LoadingIcon},
72
- props: {
73
- id: [Number, String],
74
- modelValue: {
75
- type: [String, Number, Boolean, Array],
76
- default: null,
77
- },
78
- selector: String,
79
- items: Array,
80
- valueName: {
81
- type: String,
82
- default: 'value',
83
- },
84
- textName: {
85
- type: String,
86
- default: 'text',
87
- },
88
- indexName: {
89
- type: String,
90
- default: 'index',
91
- },
92
- iconName: {
93
- type: String,
94
- default: 'icon',
95
- },
96
- defaultValue: {
97
- type: [String, Number, Boolean, Array],
98
- default: null,
99
- },
100
- empty: {
101
- type: [Boolean, String],
102
- default: false,
103
- },
104
- emptyValue: {
105
- type: [String, Number, Boolean, Array],
106
- default: null,
107
- },
108
- emptyClass: String,
109
- disabled: Boolean,
110
- tagClick: Function, // 点击一个标签选项时调用,如果返回false,则选项不会被选中
111
- change: Function, // 选中值变化后的事件处理函数,比change事件传递更多参数数据
112
- filterable: Boolean,
113
- theme: {
114
- type: String,
115
- default: 'primary',
116
- },
117
- size: String,
118
- border: Boolean,
119
- },
120
- emits: ['update:modelValue', 'change'],
121
- data() {
122
- let model = this.getModel(this.items);
123
- if (this.items && model !== this.modelValue) {
124
- this.$emit('update:modelValue', model);
125
- }
126
- return {
127
- groupKey: new Date().getTime(),
128
- model: model,
129
- hiddenValues: [],
130
- };
131
- },
132
- computed: {
133
- emptyText() {
134
- return typeof this.empty === 'string' ? this.empty : '';
135
- },
136
- currentText() {
137
- let item = this.getItem(this.model);
138
- return item ? item[this.textName] : this.emptyText;
139
- },
140
- modelEqualsModelValue() {
141
- if (Array.isArray(this.model) && Array.isArray(this.modelValue)) {
142
- return this.model.equals(this.modelValue);
143
- } else {
144
- return this.model === this.modelValue;
145
- }
146
- },
147
- firstEnabledItem() {
148
- for (let item of this.items) {
149
- if (!item.disabled) {
150
- return item;
151
- }
152
- }
153
- return undefined;
154
- },
155
- },
156
- watch: {
157
- model(newValue, oldValue) {
158
- if (!this.modelEqualsModelValue) {
159
- this.$emit('update:modelValue', newValue);
160
- // 新旧值不同时为空才触发变更事件
161
- const util = window.tnx.util;
162
- if (util.object.isNotEmpty(newValue) || util.object.isNotEmpty(oldValue)) {
163
- let vm = this;
164
- // 确保变更事件在值变更应用后再触发
165
- this.$nextTick(function () {
166
- vm.triggerChange(newValue);
167
- });
168
- }
169
- }
170
- },
171
- modelValue() {
172
- this.model = this.getModel(this.items);
173
- },
174
- items(items) {
175
- this.model = this.getModel(items);
176
- },
177
- },
178
- methods: {
179
- isMulti() {
180
- return isMultiSelector(this.selector);
181
- },
182
- triggerChange(value) {
183
- if (this.change) {
184
- let item = undefined;
185
- if (this.isMulti()) {
186
- item = [];
187
- if (Array.isArray(value)) {
188
- for (let v of value) {
189
- item.push(this.getItem(v));
190
- }
191
- }
192
- } else {
193
- item = this.getItem(value);
194
- }
195
- this.change(item, this.id);
196
- } else {
197
- this.$emit('change', value);
198
- }
199
- },
200
- getItem(value) {
201
- if (this.empty && value === this.emptyValue) {
202
- let item = {};
203
- item[this.valueName] = this.emptyValue;
204
- item[this.textName] = this.emptyText;
205
- return item;
206
- }
207
- if (value !== undefined && value !== null && this.items) {
208
- for (let item of this.items) {
209
- if ((item[this.valueName] + '') === (value + '')) {
210
- return item;
211
- }
212
- }
213
- }
214
- return undefined;
215
- },
216
- getText(value) {
217
- let item = this.getItem(value);
218
- return item ? item[this.textName] : undefined;
219
- },
220
- getModel(items) {
221
- const util = window.tnx.util;
222
- let model = this.modelValue;
223
- if (util.object.isNull(model)) {
224
- model = this.defaultValue;
225
- }
226
- if (this.isMulti()) { // 多选时需确保值为数组
227
- if (util.object.isNull(model)) {
228
- return [];
229
- }
230
- if (!Array.isArray(model)) {
231
- model = [model];
232
- }
233
- if (items && items.length) {
234
- // 多选时,如果model中有值不在items中,则去掉
235
- for (let i = model.length - 1; i >= 0; i--) {
236
- let item = this.getItem(model[i]);
237
- if (!item) {
238
- model.splice(i, 1);
239
- }
240
- }
241
- } else {
242
- model = [];
243
- }
244
- return model;
245
- }
246
- if (util.object.isNull(model)) {
247
- return null;
248
- }
249
- if (items && items.length) {
250
- let item = this.getItem(model);
251
- if (item) {
252
- return item[this.valueName];
253
- } else { // 如果当前值找不到匹配的选项,则需要考虑是设置为空还是默认选项
254
- if (!this.empty) { // 如果不能为空,则默认选中第一个可用选项
255
- let firstItem = this.firstEnabledItem;
256
- if (firstItem && firstItem[this.valueName]) {
257
- return firstItem[this.valueName];
258
- }
259
- }
260
- }
261
- }
262
- return model;
263
- },
264
- filter(keyword) {
265
- for (let item of this.items) {
266
- let itemValue = item[this.valueName];
267
- let hiddenIndex = this.hiddenValues.indexOf(itemValue);
268
- if (this.matchesItem(item, keyword)) {
269
- if (hiddenIndex >= 0) { // 匹配且原本已隐藏的,则取消隐藏
270
- this.hiddenValues.splice(hiddenIndex, 1);
271
- }
272
- } else {
273
- if (hiddenIndex < 0) { // 不匹配且原本未隐藏的,则进行隐藏
274
- this.hiddenValues.push(itemValue);
275
- }
276
- }
277
- }
278
- },
279
- matchesItem(item, keyword) {
280
- return !keyword || window.tnx.util.string.matchesForEach(item[this.valueName], keyword)
281
- || window.tnx.util.string.matchesForEach(item[this.textName], keyword)
282
- || window.tnx.util.string.matchesForEach(item[this.indexName], keyword)
283
- },
284
- isSelected(value) {
285
- if (Array.isArray(this.model)) {
286
- return this.model.contains(value);
287
- } else {
288
- return this.model === value;
289
- }
290
- },
291
- select(value, event) {
292
- if (this.tagClick) {
293
- let item = this.getItem(value);
294
- if (item) {
295
- if (this.tagClick(item) === false) {
296
- return;
297
- }
298
- }
299
- }
300
- if (this.isMulti()) {
301
- let index = this.model.indexOf(value);
302
- if (index >= 0) {
303
- this.model = this.model.filter(function (e, i) {
304
- return i !== index;
305
- });
306
- } else {
307
- this.model = this.model.concat([value]);
308
- }
309
- } else {
310
- this.model = value;
311
- }
312
- if (event) {
313
- event.currentTarget.blur();
314
- }
315
- },
316
- clear() {
317
- if (this.isMulti()) {
318
- this.model = [];
319
- } else {
320
- this.model = null;
321
- }
322
- },
323
- disableItem(itemValue, disabled, reverseOther) {
324
- let values = Array.isArray(itemValue) ? itemValue : [itemValue];
325
- for (let item of this.items) {
326
- if (values.contains(item[this.valueName])) {
327
- item.disabled = disabled;
328
- // 如果禁用的选项已被选择,则需从已选择值中移除该选项的值
329
- if (this.isMulti()) {
330
- if (this.model.includes(itemValue)) {
331
- this.model.remove(itemValue);
332
- }
333
- } else {
334
- if (this.model === itemValue) {
335
- this.model = undefined;
336
- }
337
- }
338
- } else if (reverseOther) {
339
- item.disabled = !disabled;
340
- }
341
- }
342
- this.groupKey = new Date().getTime();
343
- },
344
- }
345
- }
346
- </script>
347
-
348
- <style>
349
- .tnxbsv-select[variant="outline-danger"] {
350
- border-color: var(--bs-danger-border-subtle);
351
- color: var(--bs-danger);
352
- }
353
-
354
- .tnxbsv-select[variant="outline-danger"]:focus {
355
- box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
356
- }
357
-
358
- .tnxbsv-radio-group.btn-group > .btn {
359
- flex: none;
360
- --bs-btn-padding-x: 1rem;
361
- }
362
-
363
- select.tnxbsv-select option:disabled {
364
- color: var(--bs-tertiary-color);
365
- }
366
-
367
- select.tnxbsv-select.is-empty,
368
- .tnxbsv-select .empty-item {
369
- color: var(--bs-tertiary-color);
370
- }
371
-
372
- select.tnxbsv-select.is-empty option:not(.empty-item):not(:disabled) {
373
- color: var(--bs-body-color);
374
- }
375
- </style>
1
+ <template>
2
+ <BFormRadioGroup class="tnxbsv-select tnxbsv-radio-group"
3
+ v-model="model"
4
+ :options="items"
5
+ :value-field="valueName"
6
+ :text-field="textName"
7
+ :buttons="selector === 'radio-button'"
8
+ button-variant="outline-primary"
9
+ v-if="items && (selector==='radio' || selector === 'radio-button')"/>
10
+ <BDropdown class="tnxbsv-select tnxbsv-dropdown"
11
+ :key="groupKey"
12
+ :text="currentText"
13
+ :variant="theme"
14
+ :size="size"
15
+ v-else-if="selector==='dropdown'">
16
+ <BDropdownItem class="empty-item" :active="isSelected(emptyValue)" @click="select(emptyValue)" v-if="empty">
17
+ <span>{{ emptyText || '&nbsp;' }}</span>
18
+ </BDropdownItem>
19
+ <template v-if="items">
20
+ <BDropdownItem v-for="(item, index) of items" :key="index"
21
+ :active="isSelected(item[valueName])"
22
+ :disabled="item.disabled"
23
+ @click="select(item[valueName])"
24
+ >
25
+ <slot name="option" :item="item" v-if="$slots.option"></slot>
26
+ <template v-else>
27
+ <i :class="item[iconName]" v-if="item[iconName]"></i>
28
+ <span>{{ item[textName] }}</span>
29
+ </template>
30
+ </BDropdownItem>
31
+ </template>
32
+ <BDropdownItem v-else>
33
+ <LoadingIcon/>
34
+ </BDropdownItem>
35
+ </BDropdown>
36
+ <BFormSelect class="tnxbsv-select"
37
+ :class="{'is-empty': model === emptyValue}"
38
+ v-model="model"
39
+ :key="groupKey"
40
+ :variant="theme"
41
+ :options="items"
42
+ :value-field="valueName"
43
+ :text-field="textName"
44
+ :size="size"
45
+ v-else-if="items">
46
+ <template #first v-if="empty">
47
+ <BFormSelectOption class="empty-item" :value="emptyValue">{{ emptyText || '&nbsp;' }}</BFormSelectOption>
48
+ </template>
49
+ <template #option="{value, text}">
50
+ <slot name="option" :item="getItem(value)" v-if="$slots.option"></slot>
51
+ <template v-else>
52
+ <i :class="getItem(value)[iconName]" v-if="getItem(value)[iconName]"></i>
53
+ <span>{{ text }}</span>
54
+ </template>
55
+ </template>
56
+ </BFormSelect>
57
+ <div class="flex-v-center" v-else>
58
+ <LoadingIcon/>
59
+ </div>
60
+ </template>
61
+
62
+ <script>
63
+ import {BFormRadioGroup, BDropdown, BDropdownItem, BFormSelect, BFormSelectOption} from 'bootstrap-vue-next';
64
+ import LoadingIcon from '../loading-icon/LoadingIcon.vue';
65
+
66
+ export const isMultiSelector = function (selector) {
67
+ return selector === 'checkbox' || selector === 'tags' || selector === 'multi-select' || selector === 'texts';
68
+ }
69
+ export default {
70
+ name: 'TnxbsvSelect',
71
+ components: {BFormRadioGroup, BDropdown, BDropdownItem, BFormSelect, BFormSelectOption, LoadingIcon},
72
+ props: {
73
+ id: [Number, String],
74
+ modelValue: {
75
+ type: [String, Number, Boolean, Array],
76
+ default: null,
77
+ },
78
+ selector: String,
79
+ items: Array,
80
+ valueName: {
81
+ type: String,
82
+ default: 'value',
83
+ },
84
+ textName: {
85
+ type: String,
86
+ default: 'text',
87
+ },
88
+ indexName: {
89
+ type: String,
90
+ default: 'index',
91
+ },
92
+ iconName: {
93
+ type: String,
94
+ default: 'icon',
95
+ },
96
+ defaultValue: {
97
+ type: [String, Number, Boolean, Array],
98
+ default: null,
99
+ },
100
+ empty: {
101
+ type: [Boolean, String],
102
+ default: false,
103
+ },
104
+ emptyValue: {
105
+ type: [String, Number, Boolean, Array],
106
+ default: null,
107
+ },
108
+ emptyClass: String,
109
+ disabled: Boolean,
110
+ tagClick: Function, // 点击一个标签选项时调用,如果返回false,则选项不会被选中
111
+ change: Function, // 选中值变化后的事件处理函数,比change事件传递更多参数数据
112
+ filterable: Boolean,
113
+ theme: {
114
+ type: String,
115
+ default: 'primary',
116
+ },
117
+ size: String,
118
+ border: Boolean,
119
+ },
120
+ emits: ['update:modelValue', 'change'],
121
+ data() {
122
+ let model = this.getModel(this.items);
123
+ if (this.items && model !== this.modelValue) {
124
+ this.$emit('update:modelValue', model);
125
+ }
126
+ return {
127
+ groupKey: new Date().getTime(),
128
+ model: model,
129
+ hiddenValues: [],
130
+ };
131
+ },
132
+ computed: {
133
+ emptyText() {
134
+ return typeof this.empty === 'string' ? this.empty : '';
135
+ },
136
+ currentText() {
137
+ let item = this.getItem(this.model);
138
+ return item ? item[this.textName] : this.emptyText;
139
+ },
140
+ modelEqualsModelValue() {
141
+ if (Array.isArray(this.model) && Array.isArray(this.modelValue)) {
142
+ return this.model.equals(this.modelValue);
143
+ } else {
144
+ return this.model === this.modelValue;
145
+ }
146
+ },
147
+ firstEnabledItem() {
148
+ for (let item of this.items) {
149
+ if (!item.disabled) {
150
+ return item;
151
+ }
152
+ }
153
+ return undefined;
154
+ },
155
+ },
156
+ watch: {
157
+ model(newValue, oldValue) {
158
+ if (!this.modelEqualsModelValue) {
159
+ this.$emit('update:modelValue', newValue);
160
+ // 新旧值不同时为空才触发变更事件
161
+ const util = window.tnx.util;
162
+ if (util.object.isNotEmpty(newValue) || util.object.isNotEmpty(oldValue)) {
163
+ let vm = this;
164
+ // 确保变更事件在值变更应用后再触发
165
+ this.$nextTick(() => {
166
+ vm.triggerChange(newValue);
167
+ });
168
+ }
169
+ }
170
+ },
171
+ modelValue() {
172
+ this.model = this.getModel(this.items);
173
+ },
174
+ items(items) {
175
+ this.model = this.getModel(items);
176
+ },
177
+ },
178
+ methods: {
179
+ isMulti() {
180
+ return isMultiSelector(this.selector);
181
+ },
182
+ triggerChange(value) {
183
+ if (this.change) {
184
+ let item = undefined;
185
+ if (this.isMulti()) {
186
+ item = [];
187
+ if (Array.isArray(value)) {
188
+ for (let v of value) {
189
+ item.push(this.getItem(v));
190
+ }
191
+ }
192
+ } else {
193
+ item = this.getItem(value);
194
+ }
195
+ this.change(item, this.id);
196
+ } else {
197
+ this.$emit('change', value);
198
+ }
199
+ },
200
+ getItem(value) {
201
+ if (this.empty && value === this.emptyValue) {
202
+ let item = {};
203
+ item[this.valueName] = this.emptyValue;
204
+ item[this.textName] = this.emptyText;
205
+ return item;
206
+ }
207
+ if (value !== undefined && value !== null && this.items) {
208
+ for (let item of this.items) {
209
+ if ((item[this.valueName] + '') === (value + '')) {
210
+ return item;
211
+ }
212
+ }
213
+ }
214
+ return undefined;
215
+ },
216
+ getText(value) {
217
+ let item = this.getItem(value);
218
+ return item ? item[this.textName] : undefined;
219
+ },
220
+ getModel(items) {
221
+ const util = window.tnx.util;
222
+ let model = this.modelValue;
223
+ if (util.object.isNull(model)) {
224
+ model = this.defaultValue;
225
+ }
226
+ if (this.isMulti()) { // 多选时需确保值为数组
227
+ if (util.object.isNull(model)) {
228
+ return [];
229
+ }
230
+ if (!Array.isArray(model)) {
231
+ model = [model];
232
+ }
233
+ if (items && items.length) {
234
+ // 多选时,如果model中有值不在items中,则去掉
235
+ for (let i = model.length - 1; i >= 0; i--) {
236
+ let item = this.getItem(model[i]);
237
+ if (!item) {
238
+ model.splice(i, 1);
239
+ }
240
+ }
241
+ } else {
242
+ model = [];
243
+ }
244
+ return model;
245
+ }
246
+ if (util.object.isNull(model)) {
247
+ return null;
248
+ }
249
+ if (items && items.length) {
250
+ let item = this.getItem(model);
251
+ if (item) {
252
+ return item[this.valueName];
253
+ } else { // 如果当前值找不到匹配的选项,则需要考虑是设置为空还是默认选项
254
+ if (!this.empty) { // 如果不能为空,则默认选中第一个可用选项
255
+ let firstItem = this.firstEnabledItem;
256
+ if (firstItem && firstItem[this.valueName]) {
257
+ return firstItem[this.valueName];
258
+ }
259
+ }
260
+ }
261
+ }
262
+ return model;
263
+ },
264
+ filter(keyword) {
265
+ for (let item of this.items) {
266
+ let itemValue = item[this.valueName];
267
+ let hiddenIndex = this.hiddenValues.indexOf(itemValue);
268
+ if (this.matchesItem(item, keyword)) {
269
+ if (hiddenIndex >= 0) { // 匹配且原本已隐藏的,则取消隐藏
270
+ this.hiddenValues.splice(hiddenIndex, 1);
271
+ }
272
+ } else {
273
+ if (hiddenIndex < 0) { // 不匹配且原本未隐藏的,则进行隐藏
274
+ this.hiddenValues.push(itemValue);
275
+ }
276
+ }
277
+ }
278
+ },
279
+ matchesItem(item, keyword) {
280
+ return !keyword || window.tnx.util.string.matchesForEach(item[this.valueName], keyword)
281
+ || window.tnx.util.string.matchesForEach(item[this.textName], keyword)
282
+ || window.tnx.util.string.matchesForEach(item[this.indexName], keyword)
283
+ },
284
+ isSelected(value) {
285
+ if (Array.isArray(this.model)) {
286
+ return this.model.contains(value);
287
+ } else {
288
+ return this.model === value;
289
+ }
290
+ },
291
+ select(value, event) {
292
+ if (this.tagClick) {
293
+ let item = this.getItem(value);
294
+ if (item) {
295
+ if (this.tagClick(item) === false) {
296
+ return;
297
+ }
298
+ }
299
+ }
300
+ if (this.isMulti()) {
301
+ let index = this.model.indexOf(value);
302
+ if (index >= 0) {
303
+ this.model = this.model.filter((e, i) => {
304
+ return i !== index;
305
+ });
306
+ } else {
307
+ this.model = this.model.concat([value]);
308
+ }
309
+ } else {
310
+ this.model = value;
311
+ }
312
+ if (event) {
313
+ event.currentTarget.blur();
314
+ }
315
+ },
316
+ clear() {
317
+ if (this.isMulti()) {
318
+ this.model = [];
319
+ } else {
320
+ this.model = null;
321
+ }
322
+ },
323
+ disableItem(itemValue, disabled, reverseOther) {
324
+ let values = Array.isArray(itemValue) ? itemValue : [itemValue];
325
+ for (let item of this.items) {
326
+ if (values.contains(item[this.valueName])) {
327
+ item.disabled = disabled;
328
+ // 如果禁用的选项已被选择,则需从已选择值中移除该选项的值
329
+ if (this.isMulti()) {
330
+ if (this.model.includes(itemValue)) {
331
+ this.model.remove(itemValue);
332
+ }
333
+ } else {
334
+ if (this.model === itemValue) {
335
+ this.model = undefined;
336
+ }
337
+ }
338
+ } else if (reverseOther) {
339
+ item.disabled = !disabled;
340
+ }
341
+ }
342
+ this.groupKey = new Date().getTime();
343
+ },
344
+ }
345
+ }
346
+ </script>
347
+
348
+ <style>
349
+ .tnxbsv-select[variant="outline-danger"] {
350
+ border-color: var(--bs-danger-border-subtle);
351
+ color: var(--bs-danger);
352
+ }
353
+
354
+ .tnxbsv-select[variant="outline-danger"]:focus {
355
+ box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25);
356
+ }
357
+
358
+ .tnxbsv-radio-group.btn-group > .btn {
359
+ flex: none;
360
+ --bs-btn-padding-x: 1rem;
361
+ }
362
+
363
+ select.tnxbsv-select option:disabled {
364
+ color: var(--bs-tertiary-color);
365
+ }
366
+
367
+ select.tnxbsv-select.is-empty,
368
+ .tnxbsv-select .empty-item {
369
+ color: var(--bs-tertiary-color);
370
+ }
371
+
372
+ select.tnxbsv-select.is-empty option:not(.empty-item):not(:disabled) {
373
+ color: var(--bs-body-color);
374
+ }
375
+ </style>