@truenewx/tnxvue3 3.0.5 → 3.0.6
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 +2 -2
- package/src/bootstrap-vue/button/Button.vue +3 -3
- package/src/bootstrap-vue/cascader/Cascader.vue +451 -0
- package/src/bootstrap-vue/dialog/Dialog.vue +163 -158
- package/src/bootstrap-vue/form/Form.vue +53 -43
- package/src/bootstrap-vue/form/FormGroup.vue +4 -0
- package/src/bootstrap-vue/{loading/Loading.vue → loading-icon/LoadingIcon.vue} +2 -2
- package/src/bootstrap-vue/loading-overlay/LoadingOverlay.vue +60 -0
- package/src/bootstrap-vue/region-cascader/RegionCascader.vue +119 -0
- package/src/bootstrap-vue/select/Select.vue +19 -6
- package/src/bootstrap-vue/tags-input/TagsInput.vue +64 -0
- package/src/bootstrap-vue/tnxbsv.css +36 -0
- package/src/bootstrap-vue/tnxbsv.js +110 -2
- package/src/element-plus/tnxel.js +2 -3
- package/src/tnxvue.js +2 -2
|
@@ -1,158 +1,163 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<BModal
|
|
3
|
-
dialog-class="tnxbsv-dialog"
|
|
4
|
-
header-class="tnxbsv-dialog-header"
|
|
5
|
-
title-class="tnxbsv-dialog-title"
|
|
6
|
-
body-class="tnxbsv-dialog-body"
|
|
7
|
-
:footer-class="buttons?.length ? 'tnxbsv-dialog-footer' : 'd-none'"
|
|
8
|
-
:data-v-id="id"
|
|
9
|
-
v-model="visible"
|
|
10
|
-
scrollable
|
|
11
|
-
no-close-on-backdrop
|
|
12
|
-
no-close-on-esc
|
|
13
|
-
:teleport-to="container"
|
|
14
|
-
@hidden="onHidden"
|
|
15
|
-
>
|
|
16
|
-
<template #title>
|
|
17
|
-
<div v-html="title" v-if="title"></div>
|
|
18
|
-
</template>
|
|
19
|
-
<div ref="content" v-html="content" v-if="typeof content === 'string'"></div>
|
|
20
|
-
<component ref="content" :is="content" v-bind="contentProps" v-else></component>
|
|
21
|
-
<template #footer>
|
|
22
|
-
<TnxbsvButton v-for="(button, index) in buttons" :key="index"
|
|
23
|
-
:type="button.type"
|
|
24
|
-
:loading="buttonLoadings[index]"
|
|
25
|
-
@click="btnClick(index)"
|
|
26
|
-
>
|
|
27
|
-
{{ button.caption || button.text }}
|
|
28
|
-
</TnxbsvButton>
|
|
29
|
-
</template>
|
|
30
|
-
</BModal>
|
|
31
|
-
</template>
|
|
32
|
-
|
|
33
|
-
<script>
|
|
34
|
-
import {BModal} from 'bootstrap-vue-next';
|
|
35
|
-
import TnxbsvButton from '../button/Button.vue'
|
|
36
|
-
|
|
37
|
-
export default {
|
|
38
|
-
name: 'TnxbsvDialog',
|
|
39
|
-
components: {BModal, TnxbsvButton,},
|
|
40
|
-
props: {
|
|
41
|
-
id: {
|
|
42
|
-
type: [String, Number],
|
|
43
|
-
default: () => window.tnx.util.string.uuid32(),
|
|
44
|
-
},
|
|
45
|
-
modelValue: {
|
|
46
|
-
type: Boolean,
|
|
47
|
-
default: false,
|
|
48
|
-
},
|
|
49
|
-
container: String,
|
|
50
|
-
title: String,
|
|
51
|
-
content: [String, Object],
|
|
52
|
-
contentProps: {
|
|
53
|
-
type: Object,
|
|
54
|
-
default: () => ({}),
|
|
55
|
-
},
|
|
56
|
-
buttons: Array,
|
|
57
|
-
theme: String,
|
|
58
|
-
width: {
|
|
59
|
-
type: [Number, String],
|
|
60
|
-
default: 512,
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
emits: ['update:modelValue', 'close'],
|
|
64
|
-
data() {
|
|
65
|
-
return {
|
|
66
|
-
visible: this.modelValue,
|
|
67
|
-
buttonLoadings: [],
|
|
68
|
-
heightChangeObserver: null,
|
|
69
|
-
};
|
|
70
|
-
},
|
|
71
|
-
watch: {
|
|
72
|
-
modelValue(newValue, oldValue) {
|
|
73
|
-
this.visible = this.modelValue;
|
|
74
|
-
if (newValue && !oldValue) { // 从隐藏到显示
|
|
75
|
-
this.$nextTick(() => {
|
|
76
|
-
this.locate(true);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
visible() {
|
|
81
|
-
this.$emit('update:modelValue', this.visible);
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
mounted() {
|
|
85
|
-
window.tnx.app.eventBus.once('tnx.error', options => {
|
|
86
|
-
this.buttonLoadings = [];
|
|
87
|
-
});
|
|
88
|
-
this.$nextTick(() => {
|
|
89
|
-
if (this.visible) {
|
|
90
|
-
this.locate(true);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
},
|
|
94
|
-
beforeUnmount() {
|
|
95
|
-
window.tnx.app.eventBus.off('tnx.error');
|
|
96
|
-
},
|
|
97
|
-
methods: {
|
|
98
|
-
locate(observe) {
|
|
99
|
-
const $ = window.tnx.libs.$;
|
|
100
|
-
let $dialog = $(`${this.container} .tnxbsv-dialog`);
|
|
101
|
-
if ($dialog.length) {
|
|
102
|
-
let top = window.tnx.util.dom.getTopVerticallyCenteredOnPage($dialog[0]);
|
|
103
|
-
let width = typeof this.width === 'number' ? this.width + 'px' : this.width;
|
|
104
|
-
$dialog.css({
|
|
105
|
-
'margin-top': top + 'px',
|
|
106
|
-
'width': width,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
if (observe) {
|
|
110
|
-
this.heightChangeObserver = window.tnx.util.dom.observeHeightChange($dialog[0], this.locate);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
btnClick(index) {
|
|
115
|
-
const button = this.buttons[index];
|
|
116
|
-
if (button && typeof button.click === 'function') {
|
|
117
|
-
let result = button.click.call(this.$refs.content, this.close);
|
|
118
|
-
if (result === 'loading') {
|
|
119
|
-
this.buttonLoadings[index] = true;
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
if (result === false) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
this.close();
|
|
127
|
-
},
|
|
128
|
-
open() {
|
|
129
|
-
this.visible = true;
|
|
130
|
-
},
|
|
131
|
-
close() {
|
|
132
|
-
this.visible = false;
|
|
133
|
-
},
|
|
134
|
-
onHidden() {
|
|
135
|
-
this.$emit('close');
|
|
136
|
-
},
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
</script>
|
|
140
|
-
|
|
141
|
-
<style>
|
|
142
|
-
.tnxbsv-dialog {
|
|
143
|
-
height: fit-content;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<BModal
|
|
3
|
+
dialog-class="tnxbsv-dialog"
|
|
4
|
+
header-class="tnxbsv-dialog-header"
|
|
5
|
+
title-class="tnxbsv-dialog-title"
|
|
6
|
+
body-class="tnxbsv-dialog-body"
|
|
7
|
+
:footer-class="buttons?.length ? 'tnxbsv-dialog-footer' : 'd-none'"
|
|
8
|
+
:data-v-id="id"
|
|
9
|
+
v-model="visible"
|
|
10
|
+
scrollable
|
|
11
|
+
no-close-on-backdrop
|
|
12
|
+
no-close-on-esc
|
|
13
|
+
:teleport-to="container"
|
|
14
|
+
@hidden="onHidden"
|
|
15
|
+
>
|
|
16
|
+
<template #title>
|
|
17
|
+
<div v-html="title" v-if="title"></div>
|
|
18
|
+
</template>
|
|
19
|
+
<div ref="content" v-html="content" v-if="typeof content === 'string'"></div>
|
|
20
|
+
<component ref="content" :is="content" v-bind="contentProps" v-else></component>
|
|
21
|
+
<template #footer>
|
|
22
|
+
<TnxbsvButton v-for="(button, index) in buttons" :key="index"
|
|
23
|
+
:type="button.type"
|
|
24
|
+
:loading="buttonLoadings[index]"
|
|
25
|
+
@click="btnClick(index)"
|
|
26
|
+
>
|
|
27
|
+
{{ button.caption || button.text }}
|
|
28
|
+
</TnxbsvButton>
|
|
29
|
+
</template>
|
|
30
|
+
</BModal>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script>
|
|
34
|
+
import {BModal} from 'bootstrap-vue-next';
|
|
35
|
+
import TnxbsvButton from '../button/Button.vue'
|
|
36
|
+
|
|
37
|
+
export default {
|
|
38
|
+
name: 'TnxbsvDialog',
|
|
39
|
+
components: {BModal, TnxbsvButton,},
|
|
40
|
+
props: {
|
|
41
|
+
id: {
|
|
42
|
+
type: [String, Number],
|
|
43
|
+
default: () => window.tnx.util.string.uuid32(),
|
|
44
|
+
},
|
|
45
|
+
modelValue: {
|
|
46
|
+
type: Boolean,
|
|
47
|
+
default: false,
|
|
48
|
+
},
|
|
49
|
+
container: String,
|
|
50
|
+
title: String,
|
|
51
|
+
content: [String, Object],
|
|
52
|
+
contentProps: {
|
|
53
|
+
type: Object,
|
|
54
|
+
default: () => ({}),
|
|
55
|
+
},
|
|
56
|
+
buttons: Array,
|
|
57
|
+
theme: String,
|
|
58
|
+
width: {
|
|
59
|
+
type: [Number, String],
|
|
60
|
+
default: 512,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
emits: ['update:modelValue', 'close'],
|
|
64
|
+
data() {
|
|
65
|
+
return {
|
|
66
|
+
visible: this.modelValue,
|
|
67
|
+
buttonLoadings: [],
|
|
68
|
+
heightChangeObserver: null,
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
watch: {
|
|
72
|
+
modelValue(newValue, oldValue) {
|
|
73
|
+
this.visible = this.modelValue;
|
|
74
|
+
if (newValue && !oldValue) { // 从隐藏到显示
|
|
75
|
+
this.$nextTick(() => {
|
|
76
|
+
this.locate(true);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
visible() {
|
|
81
|
+
this.$emit('update:modelValue', this.visible);
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
mounted() {
|
|
85
|
+
window.tnx.app.eventBus.once('tnx.error', options => {
|
|
86
|
+
this.buttonLoadings = [];
|
|
87
|
+
});
|
|
88
|
+
this.$nextTick(() => {
|
|
89
|
+
if (this.visible) {
|
|
90
|
+
this.locate(true);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
beforeUnmount() {
|
|
95
|
+
window.tnx.app.eventBus.off('tnx.error');
|
|
96
|
+
},
|
|
97
|
+
methods: {
|
|
98
|
+
locate(observe) {
|
|
99
|
+
const $ = window.tnx.libs.$;
|
|
100
|
+
let $dialog = $(`${this.container} .tnxbsv-dialog`);
|
|
101
|
+
if ($dialog.length) {
|
|
102
|
+
let top = window.tnx.util.dom.getTopVerticallyCenteredOnPage($dialog[0]);
|
|
103
|
+
let width = typeof this.width === 'number' ? this.width + 'px' : this.width;
|
|
104
|
+
$dialog.css({
|
|
105
|
+
'margin-top': top + 'px',
|
|
106
|
+
'width': width,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (observe) {
|
|
110
|
+
this.heightChangeObserver = window.tnx.util.dom.observeHeightChange($dialog[0], this.locate);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
btnClick(index) {
|
|
115
|
+
const button = this.buttons[index];
|
|
116
|
+
if (button && typeof button.click === 'function') {
|
|
117
|
+
let result = button.click.call(this.$refs.content, this.close);
|
|
118
|
+
if (result === 'loading') {
|
|
119
|
+
this.buttonLoadings[index] = true;
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (result === false) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
this.close();
|
|
127
|
+
},
|
|
128
|
+
open() {
|
|
129
|
+
this.visible = true;
|
|
130
|
+
},
|
|
131
|
+
close() {
|
|
132
|
+
this.visible = false;
|
|
133
|
+
},
|
|
134
|
+
onHidden() {
|
|
135
|
+
this.$emit('close');
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<style>
|
|
142
|
+
.tnxbsv-dialog {
|
|
143
|
+
height: fit-content;
|
|
144
|
+
margin-bottom: 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.tnxbsv-dialog.modal-dialog-scrollable .modal-content {
|
|
148
|
+
max-height: calc(100vh - 1rem);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.tnxbsv-dialog .modal-header {
|
|
152
|
+
padding: 0.75rem 1rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.tnxbsv-dialog .modal-header .tnxbsv-dialog-title {
|
|
156
|
+
font-size: 1.1rem;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.tnxbsv-dialog .modal-header .btn-close {
|
|
160
|
+
--bs-btn-close-opacity: 0.25;
|
|
161
|
+
--bs-btn-close-hover-opacity: 0.5;
|
|
162
|
+
}
|
|
163
|
+
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<BForm :id="id" class="tnxbsv-form">
|
|
2
|
+
<BForm :id="id" class="tnxbsv-form" :class="{initializing: initializing}">
|
|
3
3
|
<slot></slot>
|
|
4
4
|
</BForm>
|
|
5
5
|
</template>
|
|
@@ -28,6 +28,7 @@ export default {
|
|
|
28
28
|
},
|
|
29
29
|
data() {
|
|
30
30
|
return {
|
|
31
|
+
initializing: true,
|
|
31
32
|
fieldEventListeners: {},
|
|
32
33
|
};
|
|
33
34
|
},
|
|
@@ -46,6 +47,7 @@ export default {
|
|
|
46
47
|
this.initRules();
|
|
47
48
|
this.updateLabelWidth();
|
|
48
49
|
this.updateElementsDisabled();
|
|
50
|
+
this.initializing = false;
|
|
49
51
|
});
|
|
50
52
|
});
|
|
51
53
|
},
|
|
@@ -57,11 +59,9 @@ export default {
|
|
|
57
59
|
getFieldGroupElements() {
|
|
58
60
|
let elements = {};
|
|
59
61
|
let groups = this.$el.querySelectorAll('.b-form-group[prop]');
|
|
60
|
-
groups.forEach(
|
|
61
|
-
let fieldName =
|
|
62
|
-
|
|
63
|
-
elements[fieldName] = label;
|
|
64
|
-
}
|
|
62
|
+
groups.forEach(group => {
|
|
63
|
+
let fieldName = group.getAttribute('prop');
|
|
64
|
+
elements[fieldName] = group;
|
|
65
65
|
});
|
|
66
66
|
return elements;
|
|
67
67
|
},
|
|
@@ -133,16 +133,13 @@ export default {
|
|
|
133
133
|
},
|
|
134
134
|
updateLabelWidth() {
|
|
135
135
|
let maxWidth = 0;
|
|
136
|
-
let
|
|
137
|
-
for (let
|
|
138
|
-
let
|
|
139
|
-
if (
|
|
140
|
-
let
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
if (width > maxWidth) {
|
|
144
|
-
maxWidth = width;
|
|
145
|
-
}
|
|
136
|
+
let groupElements = this.$el.querySelectorAll('.b-form-group');
|
|
137
|
+
for (let groupElement of groupElements) {
|
|
138
|
+
let label = groupElement.querySelector('.form-label');
|
|
139
|
+
if (label) {
|
|
140
|
+
let width = label.offsetWidth;
|
|
141
|
+
if (width > maxWidth) {
|
|
142
|
+
maxWidth = width;
|
|
146
143
|
}
|
|
147
144
|
}
|
|
148
145
|
}
|
|
@@ -152,7 +149,6 @@ export default {
|
|
|
152
149
|
},
|
|
153
150
|
validate() {
|
|
154
151
|
this.clearValidate(); // 清除之前的反馈信息
|
|
155
|
-
this.$el.classList.remove('was-validated'); // 移除已验证样式
|
|
156
152
|
|
|
157
153
|
return new Promise((resolve, reject) => {
|
|
158
154
|
let errors = {};
|
|
@@ -164,18 +160,20 @@ export default {
|
|
|
164
160
|
}
|
|
165
161
|
}
|
|
166
162
|
if (Object.keys(errors).length) {
|
|
167
|
-
this.$el.classList.add('was-validated'); // 添加已验证样式
|
|
168
163
|
reject(errors);
|
|
169
164
|
}
|
|
170
165
|
resolve();
|
|
171
166
|
});
|
|
172
167
|
},
|
|
168
|
+
getFieldGroupElement(fieldName) {
|
|
169
|
+
return this.$el.querySelector(`.b-form-group[prop="${fieldName}"]`);
|
|
170
|
+
},
|
|
171
|
+
queryFieldElement(fieldGroupElement) {
|
|
172
|
+
return fieldGroupElement ? fieldGroupElement.querySelector('input, textarea, select') : null;
|
|
173
|
+
},
|
|
173
174
|
getFieldElement(fieldName) {
|
|
174
|
-
let
|
|
175
|
-
|
|
176
|
-
return groupElement.querySelector('input, textarea, select');
|
|
177
|
-
}
|
|
178
|
-
return undefined;
|
|
175
|
+
let fieldGroupElement = this.getFieldGroupElement(fieldName);
|
|
176
|
+
return this.queryFieldElement(fieldGroupElement);
|
|
179
177
|
},
|
|
180
178
|
validateField(fieldName, trigger) {
|
|
181
179
|
const fieldValue = this.model[fieldName];
|
|
@@ -202,38 +200,40 @@ export default {
|
|
|
202
200
|
return fieldErrorMessages;
|
|
203
201
|
},
|
|
204
202
|
setFieldInvalidFeedback(fieldName, messages) {
|
|
205
|
-
let
|
|
206
|
-
if (
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
// 如果下一个兄弟元素不是 .invalid-feedback,则创建一个新的
|
|
210
|
-
if (!feedbackDiv || !feedbackDiv.classList.contains('invalid-feedback')) {
|
|
203
|
+
let fieldGroupElement = this.getFieldGroupElement(fieldName);
|
|
204
|
+
if (fieldGroupElement) {
|
|
205
|
+
let feedbackDiv = fieldGroupElement.querySelector('.invalid-feedback');
|
|
206
|
+
if (!feedbackDiv) {
|
|
211
207
|
feedbackDiv = document.createElement('div');
|
|
212
208
|
feedbackDiv.className = 'invalid-feedback';
|
|
213
|
-
|
|
209
|
+
let contentWrapper = fieldGroupElement.querySelector('.tnxbsv-form-group__content-wrapper');
|
|
210
|
+
// 有分组元素的情况下,一定有contentWrapper
|
|
211
|
+
contentWrapper.appendChild(feedbackDiv);
|
|
214
212
|
}
|
|
215
213
|
// 更新 .invalid-feedback 的内容
|
|
216
214
|
feedbackDiv.textContent = messages.join('; ');
|
|
217
215
|
// 设置输入框为无效状态
|
|
218
|
-
fieldElement.
|
|
216
|
+
let fieldElement = this.queryFieldElement(fieldGroupElement);
|
|
217
|
+
if (fieldElement) {
|
|
218
|
+
fieldElement.classList.add('is-invalid');
|
|
219
|
+
}
|
|
219
220
|
}
|
|
220
221
|
},
|
|
221
222
|
removeFieldInvalidFeedback(fieldName) {
|
|
222
|
-
let
|
|
223
|
-
if (
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// 如果存在 .invalid-feedback 元素,则移除它
|
|
227
|
-
if (feedbackDiv && feedbackDiv.classList.contains('invalid-feedback')) {
|
|
223
|
+
let fieldGroupElement = this.getFieldGroupElement(fieldName);
|
|
224
|
+
if (fieldGroupElement) {
|
|
225
|
+
let feedbackDiv = fieldGroupElement.querySelector('.invalid-feedback');
|
|
226
|
+
if (feedbackDiv) {
|
|
228
227
|
feedbackDiv.remove();
|
|
229
228
|
}
|
|
230
229
|
// 移除无效状态
|
|
231
|
-
fieldElement.
|
|
230
|
+
let fieldElement = this.queryFieldElement(fieldGroupElement);
|
|
231
|
+
if (fieldElement) {
|
|
232
|
+
fieldElement.classList.remove('is-invalid');
|
|
233
|
+
}
|
|
232
234
|
}
|
|
233
235
|
},
|
|
234
236
|
clearValidate() {
|
|
235
|
-
this.$el.classList.remove('was-validated'); // 移除已验证样式
|
|
236
|
-
|
|
237
237
|
const invalidElements = this.$el.querySelectorAll('.is-invalid');
|
|
238
238
|
invalidElements.forEach(el => el.classList.remove('is-invalid'));
|
|
239
239
|
|
|
@@ -241,9 +241,14 @@ export default {
|
|
|
241
241
|
invalidFeedbackElements.forEach(el => el.remove());
|
|
242
242
|
},
|
|
243
243
|
scrollToField(fieldName) {
|
|
244
|
-
let
|
|
245
|
-
if (
|
|
246
|
-
fieldElement.
|
|
244
|
+
let fieldGroupElement = this.getFieldGroupElement(fieldName);
|
|
245
|
+
if (fieldGroupElement) {
|
|
246
|
+
let fieldElement = this.queryFieldElement(fieldGroupElement);
|
|
247
|
+
if (fieldElement) {
|
|
248
|
+
fieldElement.scrollIntoView({behavior: 'smooth', block: 'center'});
|
|
249
|
+
} else {
|
|
250
|
+
fieldGroupElement.scrollIntoView({behavior: 'smooth', block: 'center'});
|
|
251
|
+
}
|
|
247
252
|
}
|
|
248
253
|
},
|
|
249
254
|
updateElementsDisabled() {
|
|
@@ -257,6 +262,10 @@ export default {
|
|
|
257
262
|
</script>
|
|
258
263
|
|
|
259
264
|
<style>
|
|
265
|
+
.tnxbsv-form.initializing .form-label {
|
|
266
|
+
visibility: hidden;
|
|
267
|
+
}
|
|
268
|
+
|
|
260
269
|
.tnxbsv-form .b-form-group {
|
|
261
270
|
display: flex;
|
|
262
271
|
}
|
|
@@ -280,6 +289,7 @@ export default {
|
|
|
280
289
|
}
|
|
281
290
|
|
|
282
291
|
.tnxbsv-form .b-form-group .invalid-feedback {
|
|
292
|
+
display: block;
|
|
283
293
|
}
|
|
284
294
|
|
|
285
295
|
@media (max-width: var(--bs-breakpoint-md)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<span class="spinner-border" :class="extraClass" role="status">
|
|
2
|
+
<span class="tnxbsv-loading-icon spinner-border" :class="extraClass" role="status">
|
|
3
3
|
<span class="visually-hidden">{{ text }}</span>
|
|
4
4
|
</span>
|
|
5
5
|
</template>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<script>
|
|
8
8
|
|
|
9
9
|
export default {
|
|
10
|
-
name: '
|
|
10
|
+
name: 'TnxbsvLoadingIcon',
|
|
11
11
|
props: {
|
|
12
12
|
small: {
|
|
13
13
|
type: Boolean,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="loading-overlay">
|
|
3
|
+
<LoadingIcon theme="primary" :small="false" :text="message"/>
|
|
4
|
+
<span class="loading-message text-primary" v-if="message">{{ message }}</span>
|
|
5
|
+
</div>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
import LoadingIcon from "../loading-icon/LoadingIcon.vue";
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
name: 'TnxbsvLoadingMask',
|
|
13
|
+
components: {LoadingIcon},
|
|
14
|
+
props: {
|
|
15
|
+
message: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: '',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
data() {
|
|
21
|
+
return {};
|
|
22
|
+
},
|
|
23
|
+
mounted() {
|
|
24
|
+
document.body.classList.add('no-scroll')
|
|
25
|
+
},
|
|
26
|
+
beforeUnmount() {
|
|
27
|
+
document.body.classList.remove('no-scroll');
|
|
28
|
+
},
|
|
29
|
+
methods: {}
|
|
30
|
+
}
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
.no-scroll {
|
|
35
|
+
overflow: hidden !important;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.loading-overlay {
|
|
39
|
+
position: fixed;
|
|
40
|
+
top: 0;
|
|
41
|
+
left: 0;
|
|
42
|
+
width: 100%;
|
|
43
|
+
height: 100%;
|
|
44
|
+
background-color: rgba(var(--bs-white-rgb), 0.8);
|
|
45
|
+
display: flex;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
align-items: center;
|
|
48
|
+
flex-direction: column;
|
|
49
|
+
z-index: 2147483647; /* 浏览器支持的最大值 */
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.loading-overlay .spinner-border {
|
|
53
|
+
opacity: 0.7;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.loading-overlay .loading-message {
|
|
57
|
+
margin-top: 0.25rem;
|
|
58
|
+
opacity: 0.7;
|
|
59
|
+
}
|
|
60
|
+
</style>
|