@truenewx/tnxvue3 3.4.0 → 3.4.2

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 (65) hide show
  1. package/package.json +23 -42
  2. package/src/bootstrap-vue/alert/Alert.vue +79 -79
  3. package/src/bootstrap-vue/button/Button.vue +40 -40
  4. package/src/bootstrap-vue/enum-select/EnumSelect.vue +4 -5
  5. package/src/bootstrap-vue/form/Form.vue +320 -320
  6. package/src/bootstrap-vue/form/FormGroup.vue +73 -73
  7. package/src/bootstrap-vue/loading-icon/LoadingIcon.vue +46 -46
  8. package/src/bootstrap-vue/paged/Paged.vue +119 -119
  9. package/src/bootstrap-vue/progress/Progress.vue +58 -58
  10. package/src/bootstrap-vue/query-table/QueryTable.vue +84 -84
  11. package/src/bootstrap-vue/region-cascader/RegionCascader.vue +119 -119
  12. package/src/bootstrap-vue/select/Select.vue +375 -375
  13. package/src/bootstrap-vue/submit-form/SubmitForm.vue +180 -176
  14. package/src/bootstrap-vue/tags-input/TagsInput.vue +64 -64
  15. package/src/bootstrap-vue/tnxbsv.css +107 -107
  16. package/src/bootstrap-vue/tnxbsv.js +109 -92
  17. package/src/bootstrap-vue/upload/Upload.vue +173 -173
  18. package/src/{aj-captcha → element-plus/aj-captcha}/Verify/VerifySlide.vue +9 -9
  19. package/src/element-plus/aj-captcha/api/index.js +11 -0
  20. package/src/{aj-captcha → element-plus/aj-captcha}/utils/ase.js +1 -1
  21. package/src/element-plus/avatar/Avatar.vue +8 -9
  22. package/src/element-plus/button/Button.vue +2 -2
  23. package/src/element-plus/curd/Curd.vue +20 -23
  24. package/src/element-plus/date-picker/DatePicker.vue +1 -1
  25. package/src/element-plus/detail-form/DetailForm.vue +1 -1
  26. package/src/element-plus/dialog/Dialog.vue +18 -20
  27. package/src/element-plus/drawer/Drawer.vue +10 -9
  28. package/src/element-plus/edit-table/EditTable.vue +3 -3
  29. package/src/element-plus/enum-select/EnumSelect.vue +3 -3
  30. package/src/element-plus/enum-view/EnumView.vue +41 -41
  31. package/src/element-plus/fetch-cascader/FetchCascader.vue +3 -4
  32. package/src/element-plus/fetch-select/FetchSelect.vue +10 -11
  33. package/src/element-plus/fetch-tags/FetchTags.vue +4 -5
  34. package/src/element-plus/fss-upload/FssUpload.vue +18 -18
  35. package/src/element-plus/fss-view/FssView.vue +1 -1
  36. package/src/element-plus/icon/Icon.vue +6 -0
  37. package/src/element-plus/input-dropdown/InputDropdown.vue +74 -74
  38. package/src/element-plus/query-form/QueryForm.vue +1 -1
  39. package/src/element-plus/query-table/QueryTable.vue +22 -26
  40. package/src/element-plus/region-cascader/RegionCascader.vue +4 -5
  41. package/src/element-plus/select/Select.vue +11 -7
  42. package/src/element-plus/steps-nav/StepsNav.vue +3 -4
  43. package/src/element-plus/submit-form/SubmitForm.vue +36 -39
  44. package/src/element-plus/tabs/Tabs.vue +1 -1
  45. package/src/element-plus/tnxel.css +22 -0
  46. package/src/element-plus/tnxel.js +105 -94
  47. package/src/element-plus/toolbar/ToolBarItem.js +15 -15
  48. package/src/element-plus/toolbar/Toolbar.vue +56 -56
  49. package/src/element-plus/transfer/Transfer.vue +8 -9
  50. package/src/element-plus/upload/Upload.vue +24 -24
  51. package/src/tdesign/desktop/tnxtdd.ts +16 -0
  52. package/src/tdesign/foundation/validator.ts +367 -0
  53. package/src/tdesign/mobile/tnxtdm.ts +16 -0
  54. package/src/tdesign/tnxtd.ts +20 -0
  55. package/src/{tnxvue-router.js → tnxvue-router.ts} +66 -48
  56. package/src/tnxvue.ts +248 -0
  57. package/src/types.d.ts +7 -0
  58. package/tsconfig.json +21 -0
  59. package/index.html +0 -12
  60. package/src/aj-captcha/api/index.js +0 -19
  61. package/src/tnxvue-validator.js +0 -365
  62. package/src/tnxvue.js +0 -439
  63. /package/src/{aj-captcha → element-plus/aj-captcha}/Verify/VerifyPoints.vue +0 -0
  64. /package/src/{aj-captcha → element-plus/aj-captcha}/Verify.vue +0 -0
  65. /package/src/{aj-captcha → element-plus/aj-captcha}/utils/util.js +0 -0
@@ -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>