@truenewx/tnxvue3 3.0.3 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/bootstrap-vue/button/Button.vue +18 -5
- package/src/bootstrap-vue/dialog/Dialog.vue +158 -0
- package/src/bootstrap-vue/form/Form.vue +243 -0
- package/src/bootstrap-vue/form/FormGroup.vue +42 -0
- package/src/bootstrap-vue/loading/Loading.vue +5 -2
- package/src/bootstrap-vue/tnxbsv.css +5 -0
- package/src/bootstrap-vue/tnxbsv.js +31 -5
- package/src/element-plus/dialog/Dialog.vue +4 -6
- package/src/tnxvue.js +0 -1
package/package.json
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<BButton>
|
|
3
|
-
<i :class="icon" v-if="icon"></i>
|
|
4
|
-
<
|
|
2
|
+
<BButton class="tnxbsv-button" :loading="loading" :disabled="loading">
|
|
3
|
+
<i class="me-1" :class="icon" v-if="icon"></i>
|
|
4
|
+
<div>
|
|
5
|
+
<slot></slot>
|
|
6
|
+
</div>
|
|
7
|
+
<template #loading>
|
|
8
|
+
<Loading class="me-1" theme="inherit"/>
|
|
9
|
+
<div>
|
|
10
|
+
<slot></slot>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
5
13
|
</BButton>
|
|
6
14
|
</template>
|
|
7
15
|
|
|
8
16
|
<script>
|
|
9
17
|
import {BButton} from 'bootstrap-vue-next';
|
|
18
|
+
import Loading from '../loading/Loading.vue';
|
|
10
19
|
|
|
11
20
|
export default {
|
|
12
21
|
name: 'TnxbsvButton',
|
|
13
|
-
components: {BButton},
|
|
22
|
+
components: {BButton, Loading},
|
|
14
23
|
props: {
|
|
15
24
|
icon: String,
|
|
25
|
+
loading: Boolean,
|
|
16
26
|
},
|
|
17
27
|
data() {
|
|
18
28
|
return {};
|
|
@@ -22,5 +32,8 @@ export default {
|
|
|
22
32
|
</script>
|
|
23
33
|
|
|
24
34
|
<style>
|
|
25
|
-
|
|
35
|
+
.tnxbsv-button {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
}
|
|
26
39
|
</style>
|
|
@@ -0,0 +1,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 v-html="content" v-if="typeof content === 'string'"></div>
|
|
20
|
+
<component :is="content" v-bind="contentProps" v-else></component>
|
|
21
|
+
<template #footer>
|
|
22
|
+
<TnxbsvButton v-for="(button, index) in buttons" :key="index"
|
|
23
|
+
:variant="button.type || 'outline-secondary'"
|
|
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
|
+
.tnxbsv-dialog .modal-header {
|
|
147
|
+
padding: 0.75rem 1rem;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.tnxbsv-dialog .modal-header .tnxbsv-dialog-title {
|
|
151
|
+
font-size: 1.1rem;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.tnxbsv-dialog .modal-header .btn-close {
|
|
155
|
+
--bs-btn-close-opacity: 0.25;
|
|
156
|
+
--bs-btn-close-hover-opacity: 0.5;
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<BForm :id="id" class="tnxbsv-form">
|
|
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
|
+
},
|
|
28
|
+
data() {
|
|
29
|
+
return {};
|
|
30
|
+
},
|
|
31
|
+
mounted() {
|
|
32
|
+
window.addEventListener('resize', this.updateLabelWidth);
|
|
33
|
+
this.$nextTick(() => {
|
|
34
|
+
setTimeout(() => {
|
|
35
|
+
this.initRules();
|
|
36
|
+
this.updateLabelWidth();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
beforeUnmount() {
|
|
41
|
+
window.removeEventListener('resize', this.updateLabelWidth);
|
|
42
|
+
},
|
|
43
|
+
methods: {
|
|
44
|
+
getFieldGroupElements() {
|
|
45
|
+
let elements = {};
|
|
46
|
+
let groups = this.$el.querySelectorAll(`#${this.id} .b-form-group[prop]`);
|
|
47
|
+
groups.forEach(label => {
|
|
48
|
+
let fieldName = label.getAttribute('prop');
|
|
49
|
+
if (fieldName) {
|
|
50
|
+
elements[fieldName] = label;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return elements;
|
|
54
|
+
},
|
|
55
|
+
getFieldRules(fieldName) {
|
|
56
|
+
let fieldRules = this.rules[fieldName] || [];
|
|
57
|
+
if (!Array.isArray(fieldRules)) {
|
|
58
|
+
fieldRules = [fieldRules];
|
|
59
|
+
}
|
|
60
|
+
return fieldRules;
|
|
61
|
+
},
|
|
62
|
+
initRules() {
|
|
63
|
+
let fieldGroupElements = this.getFieldGroupElements();
|
|
64
|
+
for (let fieldName in fieldGroupElements) {
|
|
65
|
+
let fieldGroupElement = fieldGroupElements[fieldName];
|
|
66
|
+
if (fieldGroupElement) {
|
|
67
|
+
let fieldRules = this.getFieldRules(fieldName);
|
|
68
|
+
if (fieldRules.some(rule => rule.required)) {
|
|
69
|
+
let label = fieldGroupElement.querySelector('.form-label');
|
|
70
|
+
if (label) {
|
|
71
|
+
label.classList.add('is-required');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
let existsChangeValidator = fieldRules.some(fieldRule => fieldRule.trigger === 'change');
|
|
75
|
+
let existsBlurValidator = fieldRules.some(fieldRule => fieldRule.trigger !== 'change');
|
|
76
|
+
if (existsChangeValidator || existsBlurValidator) {
|
|
77
|
+
let fieldElement = fieldGroupElement.querySelector('input, select, textarea');
|
|
78
|
+
if (fieldElement) {
|
|
79
|
+
if (existsChangeValidator) {
|
|
80
|
+
fieldElement.addEventListener('change', () => {
|
|
81
|
+
this.validateField(fieldName, 'change');
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
if (existsBlurValidator) {
|
|
85
|
+
fieldElement.addEventListener('blur', () => {
|
|
86
|
+
this.validateField(fieldName, 'blur');
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
updateLabelWidth() {
|
|
95
|
+
let maxWidth = 0;
|
|
96
|
+
let fieldGroupElements = this.getFieldGroupElements();
|
|
97
|
+
for (let fieldName in fieldGroupElements) {
|
|
98
|
+
let fieldGroupElement = fieldGroupElements[fieldName];
|
|
99
|
+
if (fieldGroupElement) {
|
|
100
|
+
let label = fieldGroupElement.querySelector('.form-label');
|
|
101
|
+
if (label) {
|
|
102
|
+
let width = label.offsetWidth;
|
|
103
|
+
if (width > maxWidth) {
|
|
104
|
+
maxWidth = width;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (maxWidth) {
|
|
110
|
+
this.$el.style.setProperty('--label-width', `${maxWidth}px`);
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
validate() {
|
|
114
|
+
this.clearValidate(); // 清除之前的反馈信息
|
|
115
|
+
this.$el.classList.remove('was-validated'); // 移除已验证样式
|
|
116
|
+
|
|
117
|
+
let valid = true;
|
|
118
|
+
let fieldGroupElements = this.getFieldGroupElements();
|
|
119
|
+
for (let fieldName in fieldGroupElements) {
|
|
120
|
+
if (!this.validateField(fieldName)) {
|
|
121
|
+
valid = false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!valid) {
|
|
126
|
+
this.$el.classList.add('was-validated'); // 添加已验证样式
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return valid;
|
|
130
|
+
},
|
|
131
|
+
getFieldElement(fieldName) {
|
|
132
|
+
let groupElement = this.$el.querySelector(`#${this.id} .b-form-group[prop="${fieldName}"]`);
|
|
133
|
+
if (groupElement) {
|
|
134
|
+
return groupElement.querySelector('input, textarea, select');
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
},
|
|
138
|
+
validateField(fieldName, trigger) {
|
|
139
|
+
const fieldValue = this.model[fieldName];
|
|
140
|
+
const fieldRules = this.getFieldRules(fieldName);
|
|
141
|
+
const fieldErrors = [];
|
|
142
|
+
|
|
143
|
+
fieldRules.forEach(fieldRule => {
|
|
144
|
+
if (typeof fieldRule.validator === 'function' && (!trigger || trigger === fieldRule.trigger)) {
|
|
145
|
+
fieldRule.validator(fieldRule, fieldValue, (error) => {
|
|
146
|
+
if (error) {
|
|
147
|
+
fieldErrors.push(error.message);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// 没有validator()方法,则不进行校验
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
if (fieldErrors.length > 0) {
|
|
155
|
+
this.setFieldInvalidFeedback(fieldName, fieldErrors);
|
|
156
|
+
return false;
|
|
157
|
+
} else {
|
|
158
|
+
// 清除错误状态
|
|
159
|
+
this.removeFieldInvalidFeedback(fieldName);
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
setFieldInvalidFeedback(fieldName, messages) {
|
|
164
|
+
let fieldElement = this.getFieldElement(fieldName);
|
|
165
|
+
if (fieldElement) {
|
|
166
|
+
// 查找已有的 .invalid-feedback 元素
|
|
167
|
+
let feedbackDiv = fieldElement.nextElementSibling;
|
|
168
|
+
// 如果下一个兄弟元素不是 .invalid-feedback,则创建一个新的
|
|
169
|
+
if (!feedbackDiv || !feedbackDiv.classList.contains('invalid-feedback')) {
|
|
170
|
+
feedbackDiv = document.createElement('div');
|
|
171
|
+
feedbackDiv.className = 'invalid-feedback';
|
|
172
|
+
fieldElement.parentNode.insertBefore(feedbackDiv, fieldElement.nextSibling);
|
|
173
|
+
}
|
|
174
|
+
// 更新 .invalid-feedback 的内容
|
|
175
|
+
feedbackDiv.textContent = messages.join('; ');
|
|
176
|
+
// 设置输入框为无效状态
|
|
177
|
+
fieldElement.classList.add('is-invalid');
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
removeFieldInvalidFeedback(fieldName) {
|
|
181
|
+
let fieldElement = this.getFieldElement(fieldName);
|
|
182
|
+
if (fieldElement) {
|
|
183
|
+
// 查找已有的 .invalid-feedback 元素
|
|
184
|
+
let feedbackDiv = fieldElement.nextElementSibling;
|
|
185
|
+
// 如果存在 .invalid-feedback 元素,则移除它
|
|
186
|
+
if (feedbackDiv && feedbackDiv.classList.contains('invalid-feedback')) {
|
|
187
|
+
feedbackDiv.remove();
|
|
188
|
+
}
|
|
189
|
+
// 移除无效状态
|
|
190
|
+
fieldElement.classList.remove('is-invalid');
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
clearValidate() {
|
|
194
|
+
this.$el.classList.remove('was-validated'); // 移除已验证样式
|
|
195
|
+
|
|
196
|
+
const invalidElements = this.$el.querySelectorAll('.is-invalid');
|
|
197
|
+
invalidElements.forEach(el => el.classList.remove('is-invalid'));
|
|
198
|
+
|
|
199
|
+
const invalidFeedbackElements = this.$el.querySelectorAll('.invalid-feedback');
|
|
200
|
+
invalidFeedbackElements.forEach(el => el.remove());
|
|
201
|
+
},
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
</script>
|
|
205
|
+
|
|
206
|
+
<style>
|
|
207
|
+
.tnxbsv-form .b-form-group {
|
|
208
|
+
display: flex;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.tnxbsv-form .b-form-group:not(:last-child) {
|
|
212
|
+
margin-bottom: 1rem;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.tnxbsv-form .b-form-group .form-label {
|
|
216
|
+
white-space: nowrap;
|
|
217
|
+
min-width: var(--label-width);
|
|
218
|
+
text-align: right;
|
|
219
|
+
margin-right: 0.75rem;
|
|
220
|
+
margin-bottom: 0;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.tnxbsv-form .b-form-group .form-label.is-required::before {
|
|
224
|
+
content: '*';
|
|
225
|
+
color: var(--bs-danger);
|
|
226
|
+
margin-right: 0.25rem;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.tnxbsv-form .b-form-group .invalid-feedback {
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@media (max-width: var(--bs-breakpoint-md)) {
|
|
233
|
+
.tnxbsv-form .b-form-group {
|
|
234
|
+
flex-direction: column;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.tnxbsv-form .b-form-group .form-label {
|
|
238
|
+
text-align: left;
|
|
239
|
+
margin-bottom: 0.5rem;
|
|
240
|
+
min-width: auto;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="tnxbsv-form-group b-form-group" :prop="prop">
|
|
3
|
+
<div class="tnxbsv-form-group__label-wrapper">
|
|
4
|
+
<slot name="label">
|
|
5
|
+
<label class="form-label">{{ label }}</label>
|
|
6
|
+
</slot>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="tnxbsv-form-group__content-wrapper">
|
|
9
|
+
<slot></slot>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
|
|
14
|
+
<script>
|
|
15
|
+
export default {
|
|
16
|
+
name: 'TnxbsvFormGroup',
|
|
17
|
+
props: {
|
|
18
|
+
label: String,
|
|
19
|
+
prop: String,
|
|
20
|
+
},
|
|
21
|
+
data() {
|
|
22
|
+
return {
|
|
23
|
+
model: {},
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
methods: {}
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
.tnxbsv-form-group {
|
|
32
|
+
display: flex;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.tnxbsv-form-group__label-wrapper {
|
|
36
|
+
padding-top: 0.375rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.tnxbsv-form-group__content-wrapper {
|
|
40
|
+
flex-grow: 1;
|
|
41
|
+
}
|
|
42
|
+
</style>
|
|
@@ -27,11 +27,14 @@ export default {
|
|
|
27
27
|
},
|
|
28
28
|
computed: {
|
|
29
29
|
extraClass() {
|
|
30
|
-
let extraClass = '
|
|
30
|
+
let extraClass = '';
|
|
31
|
+
if (this.theme && this.theme !== 'inherit') {
|
|
32
|
+
extraClass += ' text-' + this.theme;
|
|
33
|
+
}
|
|
31
34
|
if (this.small) {
|
|
32
35
|
extraClass += ' spinner-border-sm';
|
|
33
36
|
}
|
|
34
|
-
return extraClass;
|
|
37
|
+
return extraClass.trim();
|
|
35
38
|
},
|
|
36
39
|
},
|
|
37
40
|
methods: {}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// tnxbsv.js
|
|
2
|
+
import tnxjq from '@truenewx/tnxcore/src/tnxjq';
|
|
2
3
|
import tnxvue from '../tnxvue.js';
|
|
3
4
|
import * as BootstrapVue from 'bootstrap-vue-next';
|
|
4
5
|
import 'bootstrap/dist/css/bootstrap.css';
|
|
@@ -6,7 +7,10 @@ import 'bootstrap-vue-next/dist/bootstrap-vue-next.css';
|
|
|
6
7
|
import './tnxbsv.css';
|
|
7
8
|
|
|
8
9
|
import Button from './button/Button.vue';
|
|
10
|
+
import Dialog from './dialog/Dialog.vue';
|
|
9
11
|
import EnumSelect from './enum-select/EnumSelect.vue';
|
|
12
|
+
import Form from './form/Form.vue';
|
|
13
|
+
import FormGroup from './form/FormGroup.vue';
|
|
10
14
|
import Loading from './loading/Loading.vue';
|
|
11
15
|
import Paged from './paged/Paged.vue';
|
|
12
16
|
import QueryTable from './query-table/QueryTable.vue';
|
|
@@ -16,20 +20,42 @@ export const build = tnxvue.build;
|
|
|
16
20
|
|
|
17
21
|
export default build('tnxbsv', () => {
|
|
18
22
|
const components = Object.assign({}, tnxvue.components, {
|
|
19
|
-
Button, EnumSelect, Loading, Paged, QueryTable, Select,
|
|
23
|
+
Button, Dialog, EnumSelect, Form, FormGroup, Loading, Paged, QueryTable, Select,
|
|
20
24
|
});
|
|
21
25
|
|
|
22
|
-
const tnxbsv = Object.assign({}, tnxvue, {
|
|
26
|
+
const tnxbsv = Object.assign({}, tnxjq, tnxvue, {
|
|
27
|
+
libs: Object.assign({}, tnxjq.libs, tnxvue.libs, {BootstrapVue}),
|
|
23
28
|
components,
|
|
24
29
|
componentDefaultApp: undefined, // 组件的默认app,从服务端获取数据的组件以此为远程请求的默认app
|
|
25
30
|
_dialogs: [], // 对话框堆栈
|
|
26
31
|
dialog(content, title, buttons, options, contentProps) {
|
|
27
|
-
|
|
32
|
+
let id = new Date().getTime();
|
|
33
|
+
let containerId = 'dialog-container-' + id;
|
|
34
|
+
let componentDefinition = Object.assign({}, Dialog,);
|
|
35
|
+
let dialogVm = window.tnx.createVueInstance(componentDefinition, null, {
|
|
36
|
+
modelValue: true,
|
|
37
|
+
id: id,
|
|
38
|
+
container: '#' + containerId,
|
|
39
|
+
title,
|
|
40
|
+
content,
|
|
41
|
+
contentProps,
|
|
42
|
+
buttons,
|
|
43
|
+
});
|
|
44
|
+
const dialogContainer = document.createElement('div');
|
|
45
|
+
dialogContainer.className = 'tnxbsv-dialog-container';
|
|
46
|
+
dialogContainer.id = containerId;
|
|
47
|
+
document.body.appendChild(dialogContainer);
|
|
48
|
+
let dialog = dialogVm.mount(dialogContainer);
|
|
49
|
+
dialog.onHidden = this.util.function.after(dialog.onHidden, () => {
|
|
50
|
+
dialogVm.unmount();
|
|
51
|
+
this._dialogs.remove(dialog);
|
|
52
|
+
document.body.removeChild(dialogContainer);
|
|
53
|
+
});
|
|
54
|
+
this._dialogs.push(dialog);
|
|
55
|
+
return dialogVm;
|
|
28
56
|
},
|
|
29
57
|
});
|
|
30
58
|
|
|
31
|
-
tnxbsv.libs.BootstrapVue = BootstrapVue;
|
|
32
|
-
|
|
33
59
|
tnxbsv.install = tnxbsv.util.function.around(tnxbsv.install, function (install, vm) {
|
|
34
60
|
install.call(tnxbsv, vm);
|
|
35
61
|
vm.use(BootstrapVue.createBootstrap());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<el-dialog
|
|
3
|
-
|
|
2
|
+
<el-dialog class="tnxel-dialog"
|
|
3
|
+
:data-v-id="id"
|
|
4
4
|
v-model="visible"
|
|
5
5
|
destroy-on-close
|
|
6
6
|
append-to-body
|
|
@@ -34,8 +34,6 @@
|
|
|
34
34
|
import $ from 'cash-dom';
|
|
35
35
|
import DialogContent from './DialogContent.vue';
|
|
36
36
|
|
|
37
|
-
const util = window.tnx.util;
|
|
38
|
-
|
|
39
37
|
export default {
|
|
40
38
|
name: 'TnxelDialog',
|
|
41
39
|
components: {
|
|
@@ -168,7 +166,7 @@ export default {
|
|
|
168
166
|
});
|
|
169
167
|
|
|
170
168
|
if (observe) {
|
|
171
|
-
this.heightChangeObserver = util.dom.observeHeightChange($dialog[0], this.locate);
|
|
169
|
+
this.heightChangeObserver = window.tnx.util.dom.observeHeightChange($dialog[0], this.locate);
|
|
172
170
|
}
|
|
173
171
|
}
|
|
174
172
|
},
|
|
@@ -190,7 +188,7 @@ export default {
|
|
|
190
188
|
const vm = this;
|
|
191
189
|
this.beforeClose(function () {
|
|
192
190
|
if (typeof callback === 'function') {
|
|
193
|
-
vm.options.onClosed = util.function.around(vm.options.onClosed, function (onClosed) {
|
|
191
|
+
vm.options.onClosed = window.tnx.util.function.around(vm.options.onClosed, function (onClosed) {
|
|
194
192
|
if (onClosed) {
|
|
195
193
|
onClosed();
|
|
196
194
|
}
|
package/src/tnxvue.js
CHANGED
|
@@ -96,7 +96,6 @@ export default build('tnxvue', () => {
|
|
|
96
96
|
} else if (window.tnx.router.instance) {
|
|
97
97
|
vm.config.globalProperties.$router = window.tnx.router.instance;
|
|
98
98
|
}
|
|
99
|
-
// vm.config.unwrapInjectedRef = true;
|
|
100
99
|
window.tnx.app.eventBus = window.tnx.app.eventBus || mitt();
|
|
101
100
|
window.tnx.app.eventBus.once = function (name, handler) {
|
|
102
101
|
this.all.set(name, [handler]);
|