@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,320 +1,320 @@
1
- <template>
2
- <BForm :id="id" class="tnxbsv-form" :class="{initializing: initializing, 'form-inline': inline}">
3
- <slot></slot>
4
- </BForm>
5
- </template>
6
-
7
- <script>
8
- import {BForm} from 'bootstrap-vue-next';
9
-
10
- export default {
11
- name: 'TnxbsvForm',
12
- components: {BForm},
13
- props: {
14
- id: {
15
- type: String,
16
- default: () => 'tnxbsv-form-' + new Date().getTime(),
17
- },
18
- model: {
19
- type: Object,
20
- default: () => ({}),
21
- },
22
- inline: Boolean,
23
- rules: {
24
- type: Object, // key: 字段名,value: 验证规则数组
25
- default: () => ({}),
26
- },
27
- disabled: Boolean,
28
- },
29
- data() {
30
- return {
31
- initializing: true,
32
- fieldEventListeners: {},
33
- };
34
- },
35
- watch: {
36
- rules() {
37
- this.initRules();
38
- },
39
- disabled() {
40
- this.updateElementsDisabled();
41
- },
42
- },
43
- mounted() {
44
- window.addEventListener('resize', this.updateLabelWidth);
45
- this.$nextTick(() => {
46
- setTimeout(() => {
47
- this.initRules();
48
- this.updateLabelWidth();
49
- this.updateElementsDisabled();
50
- this.initializing = false;
51
- });
52
- });
53
- },
54
- beforeUnmount() {
55
- window.removeEventListener('resize', this.updateLabelWidth);
56
- this.clearEventListeners();
57
- },
58
- methods: {
59
- getFieldGroupElements() {
60
- let elements = {};
61
- let groups = this.$el.querySelectorAll('.b-form-group[prop]');
62
- groups.forEach(group => {
63
- let fieldName = group.getAttribute('prop');
64
- elements[fieldName] = group;
65
- });
66
- return elements;
67
- },
68
- getFieldRules(fieldName) {
69
- let fieldRules = this.rules[fieldName] || [];
70
- if (!Array.isArray(fieldRules)) {
71
- fieldRules = [fieldRules];
72
- }
73
- return fieldRules;
74
- },
75
- addFieldEventListener(fieldName, element, eventType, handler) {
76
- if (!this.fieldEventListeners[fieldName]) {
77
- this.fieldEventListeners[fieldName] = {};
78
- }
79
- if (!this.fieldEventListeners[fieldName][eventType]) {
80
- this.fieldEventListeners[fieldName][eventType] = [];
81
- }
82
- element.addEventListener(eventType, handler);
83
- this.fieldEventListeners[fieldName][eventType].push(handler);
84
- },
85
- clearEventListeners() {
86
- for (let fieldName in this.fieldEventListeners) {
87
- let listeners = this.fieldEventListeners[fieldName];
88
- for (let eventType in listeners) {
89
- let handlers = listeners[eventType];
90
- let fieldElement = this.getFieldElement(fieldName);
91
- if (fieldElement) {
92
- handlers.forEach(handler => {
93
- fieldElement.removeEventListener(eventType, handler);
94
- });
95
- }
96
- }
97
- }
98
- this.fieldEventListeners = {}; // 清空映射表
99
- },
100
- initRules() {
101
- this.clearEventListeners();
102
-
103
- let fieldGroupElements = this.getFieldGroupElements();
104
- for (let fieldName in fieldGroupElements) {
105
- let fieldGroupElement = fieldGroupElements[fieldName];
106
- if (fieldGroupElement) {
107
- let fieldRules = this.getFieldRules(fieldName);
108
- if (fieldRules.some(rule => rule.required)) {
109
- let label = fieldGroupElement.querySelector('.form-label');
110
- if (label) {
111
- label.classList.add('is-required');
112
- }
113
- }
114
- let existsChangeValidator = fieldRules.some(fieldRule => fieldRule.trigger === 'change');
115
- let existsBlurValidator = fieldRules.some(fieldRule => fieldRule.trigger !== 'change');
116
- if (existsChangeValidator || existsBlurValidator) {
117
- let fieldElement = fieldGroupElement.querySelector('input, select, textarea');
118
- if (fieldElement) {
119
- if (existsChangeValidator) {
120
- this.addFieldEventListener(fieldName, fieldElement, 'change', () => {
121
- this.validateField(fieldName, 'change');
122
- });
123
- }
124
- if (existsBlurValidator) {
125
- this.addFieldEventListener(fieldName, fieldElement, 'blur', () => {
126
- this.validateField(fieldName, 'blur');
127
- });
128
- }
129
- }
130
- }
131
- }
132
- }
133
- },
134
- updateLabelWidth() {
135
- if (this.inline) {
136
- return;
137
- }
138
- let maxWidth = 0;
139
- let groupElements = this.$el.querySelectorAll('.b-form-group');
140
- for (let groupElement of groupElements) {
141
- let label = groupElement.querySelector('.form-label');
142
- if (label) {
143
- let width = label.offsetWidth;
144
- if (width > maxWidth) {
145
- maxWidth = width;
146
- }
147
- }
148
- }
149
- if (maxWidth) {
150
- this.$el.style.setProperty('--label-width', `${maxWidth}px`);
151
- }
152
- },
153
- validate() {
154
- this.clearValidate(); // 清除之前的反馈信息
155
-
156
- return new Promise((resolve, reject) => {
157
- let errors = {};
158
- let fieldGroupElements = this.getFieldGroupElements();
159
- for (let fieldName in fieldGroupElements) {
160
- let fieldErrorMessages = this.validateField(fieldName);
161
- if (fieldErrorMessages.length) {
162
- errors[fieldName] = fieldErrorMessages;
163
- }
164
- }
165
- if (Object.keys(errors).length) {
166
- reject(errors);
167
- }
168
- resolve();
169
- });
170
- },
171
- getFieldGroupElement(fieldName) {
172
- return this.$el.querySelector(`.b-form-group[prop="${fieldName}"]`);
173
- },
174
- queryFieldElement(fieldGroupElement) {
175
- return fieldGroupElement ? fieldGroupElement.querySelector('input, textarea, select') : null;
176
- },
177
- getFieldElement(fieldName) {
178
- let fieldGroupElement = this.getFieldGroupElement(fieldName);
179
- return this.queryFieldElement(fieldGroupElement);
180
- },
181
- validateField(fieldName, trigger) {
182
- const fieldValue = this.model[fieldName];
183
- const fieldRules = this.getFieldRules(fieldName);
184
- const fieldErrorMessages = [];
185
-
186
- fieldRules.forEach(fieldRule => {
187
- if (typeof fieldRule.validator === 'function' && (!trigger || trigger === fieldRule.trigger)) {
188
- fieldRule.validator(fieldRule, fieldValue, (error) => {
189
- if (error) {
190
- fieldErrorMessages.push(error.message);
191
- }
192
- });
193
- }
194
- // 没有validator()方法,则不进行校验
195
- });
196
-
197
- if (fieldErrorMessages.length) {
198
- this.setFieldInvalidFeedback(fieldName, fieldErrorMessages);
199
- } else {
200
- // 清除错误状态
201
- this.removeFieldInvalidFeedback(fieldName);
202
- }
203
- return fieldErrorMessages;
204
- },
205
- setFieldInvalidFeedback(fieldName, messages) {
206
- let fieldGroupElement = this.getFieldGroupElement(fieldName);
207
- if (fieldGroupElement) {
208
- let feedbackDiv = fieldGroupElement.querySelector('.invalid-feedback');
209
- if (!feedbackDiv) {
210
- feedbackDiv = document.createElement('div');
211
- feedbackDiv.className = 'invalid-feedback';
212
- let contentWrapper = fieldGroupElement.querySelector('.tnxbsv-form-group__content-wrapper');
213
- // 有分组元素的情况下,一定有contentWrapper
214
- contentWrapper.appendChild(feedbackDiv);
215
- }
216
- // 更新 .invalid-feedback 的内容
217
- feedbackDiv.textContent = messages.join('; ');
218
- // 设置输入框为无效状态
219
- let fieldElement = this.queryFieldElement(fieldGroupElement);
220
- if (fieldElement) {
221
- // 如果字段元素或其上级元素中不包含.ignore-input-feedback样式,则加入.is-invalid样式
222
- if (!fieldElement.classList.contains('ignore-input-feedback')
223
- && !fieldElement.closest('.ignore-input-feedback')) {
224
- fieldElement.classList.add('is-invalid');
225
- }
226
- }
227
- }
228
- },
229
- removeFieldInvalidFeedback(fieldName) {
230
- let fieldGroupElement = this.getFieldGroupElement(fieldName);
231
- if (fieldGroupElement) {
232
- let feedbackDiv = fieldGroupElement.querySelector('.invalid-feedback');
233
- if (feedbackDiv) {
234
- feedbackDiv.remove();
235
- }
236
- // 移除无效状态
237
- let fieldElement = this.queryFieldElement(fieldGroupElement);
238
- if (fieldElement) {
239
- fieldElement.classList.remove('is-invalid');
240
- }
241
- }
242
- },
243
- clearValidate() {
244
- const invalidElements = this.$el.querySelectorAll('.is-invalid');
245
- invalidElements.forEach(el => el.classList.remove('is-invalid'));
246
-
247
- const invalidFeedbackElements = this.$el.querySelectorAll('.invalid-feedback');
248
- invalidFeedbackElements.forEach(el => el.remove());
249
- },
250
- scrollToField(fieldName) {
251
- let fieldGroupElement = this.getFieldGroupElement(fieldName);
252
- if (fieldGroupElement) {
253
- let fieldElement = this.queryFieldElement(fieldGroupElement);
254
- if (fieldElement) {
255
- fieldElement.scrollIntoView({behavior: 'smooth', block: 'center'});
256
- } else {
257
- fieldGroupElement.scrollIntoView({behavior: 'smooth', block: 'center'});
258
- }
259
- }
260
- },
261
- updateElementsDisabled() {
262
- let elements = this.$el.querySelectorAll('input, textarea, select, button');
263
- elements.forEach(element => {
264
- element.disabled = this.disabled;
265
- });
266
- },
267
- }
268
- }
269
- </script>
270
-
271
- <style>
272
- .tnxbsv-form.initializing .form-label {
273
- visibility: hidden;
274
- }
275
-
276
- .tnxbsv-form .b-form-group {
277
- display: flex;
278
- }
279
-
280
- .tnxbsv-form .b-form-group:not(:last-child) {
281
- margin-bottom: 1rem;
282
- }
283
-
284
- .tnxbsv-form .b-form-group .form-label {
285
- white-space: nowrap;
286
- min-width: var(--label-width);
287
- text-align: right;
288
- margin-right: 0.75rem;
289
- margin-bottom: 0;
290
- }
291
-
292
- .tnxbsv-form .b-form-group .form-label.is-required::before {
293
- content: '*';
294
- color: var(--bs-danger);
295
- margin-right: 0.25rem;
296
- }
297
-
298
- .tnxbsv-form .b-form-group .invalid-feedback {
299
- display: block;
300
- }
301
-
302
- .tnxbsv-form .inline-feedback .invalid-feedback {
303
- margin-top: 0;
304
- margin-left: 0.5rem;
305
- width: auto;
306
- flex-grow: 1;
307
- }
308
-
309
- @media (max-width: var(--bs-breakpoint-md)) {
310
- .tnxbsv-form .b-form-group {
311
- flex-direction: column;
312
- }
313
-
314
- .tnxbsv-form .b-form-group .form-label {
315
- text-align: left;
316
- margin-bottom: 0.5rem;
317
- min-width: auto;
318
- }
319
- }
320
- </style>
1
+ <template>
2
+ <BForm :id="id" class="tnxbsv-form" :class="{initializing: initializing, 'form-inline': inline}">
3
+ <slot></slot>
4
+ </BForm>
5
+ </template>
6
+
7
+ <script>
8
+ import {BForm} from 'bootstrap-vue-next';
9
+
10
+ export default {
11
+ name: 'TnxbsvForm',
12
+ components: {BForm},
13
+ props: {
14
+ id: {
15
+ type: String,
16
+ default: () => 'tnxbsv-form-' + new Date().getTime(),
17
+ },
18
+ model: {
19
+ type: Object,
20
+ default: () => ({}),
21
+ },
22
+ inline: Boolean,
23
+ rules: {
24
+ type: Object, // key: 字段名,value: 验证规则数组
25
+ default: () => ({}),
26
+ },
27
+ disabled: Boolean,
28
+ },
29
+ data() {
30
+ return {
31
+ initializing: true,
32
+ fieldEventListeners: {},
33
+ };
34
+ },
35
+ watch: {
36
+ rules() {
37
+ this.initRules();
38
+ },
39
+ disabled() {
40
+ this.updateElementsDisabled();
41
+ },
42
+ },
43
+ mounted() {
44
+ window.addEventListener('resize', this.updateLabelWidth);
45
+ this.$nextTick(() => {
46
+ setTimeout(() => {
47
+ this.initRules();
48
+ this.updateLabelWidth();
49
+ this.updateElementsDisabled();
50
+ this.initializing = false;
51
+ });
52
+ });
53
+ },
54
+ beforeUnmount() {
55
+ window.removeEventListener('resize', this.updateLabelWidth);
56
+ this.clearEventListeners();
57
+ },
58
+ methods: {
59
+ getFieldGroupElements() {
60
+ let elements = {};
61
+ let groups = this.$el.querySelectorAll('.b-form-group[prop]');
62
+ groups.forEach(group => {
63
+ let fieldName = group.getAttribute('prop');
64
+ elements[fieldName] = group;
65
+ });
66
+ return elements;
67
+ },
68
+ getFieldRules(fieldName) {
69
+ let fieldRules = this.rules[fieldName] || [];
70
+ if (!Array.isArray(fieldRules)) {
71
+ fieldRules = [fieldRules];
72
+ }
73
+ return fieldRules;
74
+ },
75
+ addFieldEventListener(fieldName, element, eventType, handler) {
76
+ if (!this.fieldEventListeners[fieldName]) {
77
+ this.fieldEventListeners[fieldName] = {};
78
+ }
79
+ if (!this.fieldEventListeners[fieldName][eventType]) {
80
+ this.fieldEventListeners[fieldName][eventType] = [];
81
+ }
82
+ element.addEventListener(eventType, handler);
83
+ this.fieldEventListeners[fieldName][eventType].push(handler);
84
+ },
85
+ clearEventListeners() {
86
+ for (let fieldName in this.fieldEventListeners) {
87
+ let listeners = this.fieldEventListeners[fieldName];
88
+ for (let eventType in listeners) {
89
+ let handlers = listeners[eventType];
90
+ let fieldElement = this.getFieldElement(fieldName);
91
+ if (fieldElement) {
92
+ handlers.forEach(handler => {
93
+ fieldElement.removeEventListener(eventType, handler);
94
+ });
95
+ }
96
+ }
97
+ }
98
+ this.fieldEventListeners = {}; // 清空映射表
99
+ },
100
+ initRules() {
101
+ this.clearEventListeners();
102
+
103
+ let fieldGroupElements = this.getFieldGroupElements();
104
+ for (let fieldName in fieldGroupElements) {
105
+ let fieldGroupElement = fieldGroupElements[fieldName];
106
+ if (fieldGroupElement) {
107
+ let fieldRules = this.getFieldRules(fieldName);
108
+ if (fieldRules.some(rule => rule.required)) {
109
+ let label = fieldGroupElement.querySelector('.form-label');
110
+ if (label) {
111
+ label.classList.add('is-required');
112
+ }
113
+ }
114
+ let existsChangeValidator = fieldRules.some(fieldRule => fieldRule.trigger === 'change');
115
+ let existsBlurValidator = fieldRules.some(fieldRule => fieldRule.trigger !== 'change');
116
+ if (existsChangeValidator || existsBlurValidator) {
117
+ let fieldElement = fieldGroupElement.querySelector('input, select, textarea');
118
+ if (fieldElement) {
119
+ if (existsChangeValidator) {
120
+ this.addFieldEventListener(fieldName, fieldElement, 'change', () => {
121
+ this.validateField(fieldName, 'change');
122
+ });
123
+ }
124
+ if (existsBlurValidator) {
125
+ this.addFieldEventListener(fieldName, fieldElement, 'blur', () => {
126
+ this.validateField(fieldName, 'blur');
127
+ });
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ },
134
+ updateLabelWidth() {
135
+ if (this.inline) {
136
+ return;
137
+ }
138
+ let maxWidth = 0;
139
+ let groupElements = this.$el.querySelectorAll('.b-form-group');
140
+ for (let groupElement of groupElements) {
141
+ let label = groupElement.querySelector('.form-label');
142
+ if (label) {
143
+ let width = label.offsetWidth;
144
+ if (width > maxWidth) {
145
+ maxWidth = width;
146
+ }
147
+ }
148
+ }
149
+ if (maxWidth) {
150
+ this.$el.style.setProperty('--label-width', `${maxWidth}px`);
151
+ }
152
+ },
153
+ validate() {
154
+ this.clearValidate(); // 清除之前的反馈信息
155
+
156
+ return new Promise((resolve, reject) => {
157
+ let errors = {};
158
+ let fieldGroupElements = this.getFieldGroupElements();
159
+ for (let fieldName in fieldGroupElements) {
160
+ let fieldErrorMessages = this.validateField(fieldName);
161
+ if (fieldErrorMessages.length) {
162
+ errors[fieldName] = fieldErrorMessages;
163
+ }
164
+ }
165
+ if (Object.keys(errors).length) {
166
+ reject(errors);
167
+ }
168
+ resolve();
169
+ });
170
+ },
171
+ getFieldGroupElement(fieldName) {
172
+ return this.$el.querySelector(`.b-form-group[prop="${fieldName}"]`);
173
+ },
174
+ queryFieldElement(fieldGroupElement) {
175
+ return fieldGroupElement ? fieldGroupElement.querySelector('input, textarea, select') : null;
176
+ },
177
+ getFieldElement(fieldName) {
178
+ let fieldGroupElement = this.getFieldGroupElement(fieldName);
179
+ return this.queryFieldElement(fieldGroupElement);
180
+ },
181
+ validateField(fieldName, trigger) {
182
+ const fieldValue = this.model[fieldName];
183
+ const fieldRules = this.getFieldRules(fieldName);
184
+ const fieldErrorMessages = [];
185
+
186
+ fieldRules.forEach(fieldRule => {
187
+ if (typeof fieldRule.validator === 'function' && (!trigger || trigger === fieldRule.trigger)) {
188
+ fieldRule.validator(fieldRule, fieldValue, (error) => {
189
+ if (error) {
190
+ fieldErrorMessages.push(error.message);
191
+ }
192
+ });
193
+ }
194
+ // 没有validator()方法,则不进行校验
195
+ });
196
+
197
+ if (fieldErrorMessages.length) {
198
+ this.setFieldInvalidFeedback(fieldName, fieldErrorMessages);
199
+ } else {
200
+ // 清除错误状态
201
+ this.removeFieldInvalidFeedback(fieldName);
202
+ }
203
+ return fieldErrorMessages;
204
+ },
205
+ setFieldInvalidFeedback(fieldName, messages) {
206
+ let fieldGroupElement = this.getFieldGroupElement(fieldName);
207
+ if (fieldGroupElement) {
208
+ let feedbackDiv = fieldGroupElement.querySelector('.invalid-feedback');
209
+ if (!feedbackDiv) {
210
+ feedbackDiv = document.createElement('div');
211
+ feedbackDiv.className = 'invalid-feedback';
212
+ let contentWrapper = fieldGroupElement.querySelector('.tnxbsv-form-group__content-wrapper');
213
+ // 有分组元素的情况下,一定有contentWrapper
214
+ contentWrapper.appendChild(feedbackDiv);
215
+ }
216
+ // 更新 .invalid-feedback 的内容
217
+ feedbackDiv.textContent = messages.join('; ');
218
+ // 设置输入框为无效状态
219
+ let fieldElement = this.queryFieldElement(fieldGroupElement);
220
+ if (fieldElement) {
221
+ // 如果字段元素或其上级元素中不包含.ignore-input-feedback样式,则加入.is-invalid样式
222
+ if (!fieldElement.classList.contains('ignore-input-feedback')
223
+ && !fieldElement.closest('.ignore-input-feedback')) {
224
+ fieldElement.classList.add('is-invalid');
225
+ }
226
+ }
227
+ }
228
+ },
229
+ removeFieldInvalidFeedback(fieldName) {
230
+ let fieldGroupElement = this.getFieldGroupElement(fieldName);
231
+ if (fieldGroupElement) {
232
+ let feedbackDiv = fieldGroupElement.querySelector('.invalid-feedback');
233
+ if (feedbackDiv) {
234
+ feedbackDiv.remove();
235
+ }
236
+ // 移除无效状态
237
+ let fieldElement = this.queryFieldElement(fieldGroupElement);
238
+ if (fieldElement) {
239
+ fieldElement.classList.remove('is-invalid');
240
+ }
241
+ }
242
+ },
243
+ clearValidate() {
244
+ const invalidElements = this.$el.querySelectorAll('.is-invalid');
245
+ invalidElements.forEach(el => el.classList.remove('is-invalid'));
246
+
247
+ const invalidFeedbackElements = this.$el.querySelectorAll('.invalid-feedback');
248
+ invalidFeedbackElements.forEach(el => el.remove());
249
+ },
250
+ scrollToField(fieldName) {
251
+ let fieldGroupElement = this.getFieldGroupElement(fieldName);
252
+ if (fieldGroupElement) {
253
+ let fieldElement = this.queryFieldElement(fieldGroupElement);
254
+ if (fieldElement) {
255
+ fieldElement.scrollIntoView({behavior: 'smooth', block: 'center'});
256
+ } else {
257
+ fieldGroupElement.scrollIntoView({behavior: 'smooth', block: 'center'});
258
+ }
259
+ }
260
+ },
261
+ updateElementsDisabled() {
262
+ let elements = this.$el.querySelectorAll('input, textarea, select, button');
263
+ elements.forEach(element => {
264
+ element.disabled = this.disabled;
265
+ });
266
+ },
267
+ }
268
+ }
269
+ </script>
270
+
271
+ <style>
272
+ .tnxbsv-form.initializing .form-label {
273
+ visibility: hidden;
274
+ }
275
+
276
+ .tnxbsv-form .b-form-group {
277
+ display: flex;
278
+ }
279
+
280
+ .tnxbsv-form .b-form-group:not(:last-child) {
281
+ margin-bottom: 1rem;
282
+ }
283
+
284
+ .tnxbsv-form .b-form-group .form-label {
285
+ white-space: nowrap;
286
+ min-width: var(--label-width);
287
+ text-align: right;
288
+ margin-right: 0.75rem;
289
+ margin-bottom: 0;
290
+ }
291
+
292
+ .tnxbsv-form .b-form-group .form-label.is-required::before {
293
+ content: '*';
294
+ color: var(--bs-danger);
295
+ margin-right: 0.25rem;
296
+ }
297
+
298
+ .tnxbsv-form .b-form-group .invalid-feedback {
299
+ display: block;
300
+ }
301
+
302
+ .tnxbsv-form .inline-feedback .invalid-feedback {
303
+ margin-top: 0;
304
+ margin-left: 0.5rem;
305
+ width: auto;
306
+ flex-grow: 1;
307
+ }
308
+
309
+ @media (max-width: var(--bs-breakpoint-md)) {
310
+ .tnxbsv-form .b-form-group {
311
+ flex-direction: column;
312
+ }
313
+
314
+ .tnxbsv-form .b-form-group .form-label {
315
+ text-align: left;
316
+ margin-bottom: 0.5rem;
317
+ min-width: auto;
318
+ }
319
+ }
320
+ </style>