@truenewx/tnxvue3 3.4.4 → 3.4.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.
Files changed (35) hide show
  1. package/package.json +18 -17
  2. package/src/bootstrap-vue/dialog/Dialog.vue +22 -10
  3. package/src/bootstrap-vue/select/Select.vue +3 -4
  4. package/src/components/ScrollView.vue +69 -0
  5. package/src/css.d.ts +4 -0
  6. package/src/element-plus/aj-captcha/utils/ase.js +4 -4
  7. package/src/element-plus/dialog/Dialog.vue +19 -7
  8. package/src/element-plus/drawer/Drawer.vue +20 -3
  9. package/src/element-plus/fss-upload/FssUpload.vue +1 -1
  10. package/src/element-plus/icon/Icon.vue +3 -0
  11. package/src/element-plus/select/Select.vue +3 -4
  12. package/src/element-plus/tnxel.css +0 -8
  13. package/src/element-plus/tnxel.ts +58 -72
  14. package/src/tdesign/mobile/calendar/Calendar.vue +121 -0
  15. package/src/tdesign/mobile/date-time-picker/DateTimePicker.vue +147 -0
  16. package/src/tdesign/mobile/dialog/Dialog.vue +179 -0
  17. package/src/tdesign/mobile/dialog/DialogContent.vue +13 -0
  18. package/src/tdesign/mobile/drawer/Drawer.vue +197 -0
  19. package/src/tdesign/mobile/drawer/DrawerContent.vue +13 -0
  20. package/src/tdesign/mobile/enum-select/EnumSelect.vue +164 -0
  21. package/src/tdesign/mobile/popup/Popup.vue +106 -0
  22. package/src/tdesign/mobile/region-picker/RegionPicker.vue +223 -0
  23. package/src/tdesign/mobile/select/Select.vue +479 -0
  24. package/src/tdesign/mobile/slide-radio-group/SlideRadioGroup.vue +397 -0
  25. package/src/tdesign/mobile/tnxtdm.css +125 -0
  26. package/src/tdesign/mobile/tnxtdm.ts +305 -1
  27. package/src/tdesign/tnxtd-validator.ts +14 -13
  28. package/src/tdesign/tnxtd.css +98 -0
  29. package/src/tdesign/tnxtd.ts +4 -0
  30. package/src/tnxvue-router.ts +59 -5
  31. package/src/tnxvue.ts +32 -11
  32. package/src/vue.d.ts +6 -0
  33. package/tsconfig.json +4 -7
  34. /package/src/{percent → components}/Percent.vue +0 -0
  35. /package/src/{text → components}/Text.vue +0 -0
@@ -0,0 +1,397 @@
1
+ <template>
2
+ <div :class="containerClasses" :style="containerStyle" v-if="normalizedItems.length">
3
+ <div class="tnxtdm-slide-radio-group__glider" :style="gliderStyle"></div>
4
+ <div v-for="(item, index) in normalizedItems"
5
+ :key="item.value"
6
+ class="tnxtdm-slide-radio-group__item"
7
+ :class="{'is-active': modelValue === item.value}"
8
+ :data-key="item.key"
9
+ @click="onItemClick(item.value)"
10
+ :ref="el => { if (el) itemRefs[index] = el }">
11
+ <t-icon class="tnxtdm-slide-radio-group__item-icon" :name="item.icon" size="1rem" v-if="item.icon"/>
12
+ <span class="tnxtdm-slide-radio-group__item-text">{{ item.text }}</span>
13
+ </div>
14
+ </div>
15
+ </template>
16
+
17
+ <script>
18
+ export default {
19
+ name: 'TnxtdmSlideRadioGroup',
20
+ props: {
21
+ modelValue: [String, Number, Boolean],
22
+ items: {
23
+ type: Array,
24
+ default: () => [],
25
+ },
26
+ size: {
27
+ type: String,
28
+ default: 'medium',
29
+ validator: (value) => ['small', 'medium', 'large'].includes(value),
30
+ },
31
+ shape: {
32
+ type: String,
33
+ default: 'rectangle',
34
+ validator: (value) => ['capsule', 'rectangle'].includes(value),
35
+ },
36
+ theme: {
37
+ type: String,
38
+ default: 'default',
39
+ },
40
+ valueName: {
41
+ type: String,
42
+ default: 'value',
43
+ },
44
+ textName: {
45
+ type: String,
46
+ default: 'text',
47
+ },
48
+ iconName: {
49
+ type: String,
50
+ default: 'icon',
51
+ },
52
+ defaultValue: {
53
+ type: [String, Number, Boolean],
54
+ default: undefined,
55
+ },
56
+ width: {
57
+ type: String,
58
+ default: 'auto',
59
+ },
60
+ disabled: Boolean,
61
+ },
62
+ emits: ['update:modelValue', 'change'],
63
+ data() {
64
+ return {
65
+ gliderStyle: {
66
+ width: '0px',
67
+ transform: 'translateX(0px)',
68
+ },
69
+ formDisabled: false,
70
+ };
71
+ },
72
+ computed: {
73
+ isDisabled() {
74
+ return this.disabled || this.formDisabled;
75
+ },
76
+ normalizedItems() {
77
+ if (!this.items) {
78
+ return [];
79
+ }
80
+ return this.items.map(item => {
81
+ if (typeof item === 'object' && item !== null) {
82
+ return {
83
+ text: item[this.textName],
84
+ value: item[this.valueName],
85
+ icon: item[this.iconName],
86
+ };
87
+ }
88
+ return {
89
+ text: String(item),
90
+ value: item,
91
+ };
92
+ });
93
+ },
94
+ currentValue: {
95
+ get() {
96
+ return this.modelValue;
97
+ },
98
+ set(val) {
99
+ this.$emit('update:modelValue', val);
100
+ let selectedItem = val;
101
+ if (this.items && this.items.length > 0) {
102
+ const index = this.normalizedItems.findIndex(item => item.value === val);
103
+ if (index !== -1) {
104
+ selectedItem = this.items[index];
105
+ }
106
+ }
107
+ this.$emit('change', selectedItem);
108
+ }
109
+ },
110
+ containerClasses() {
111
+ return [
112
+ 'tnxtdm-slide-radio-group',
113
+ `tnxtdm-slide-radio-group--${this.shape}`,
114
+ `tnxtdm-slide-radio-group--${this.size}`,
115
+ `tnxtdm-slide-radio-group--theme-${this.theme}`,
116
+ {
117
+ 'tnxtdm-slide-radio-group--fixed': !['auto', 'fit-content'].includes(this.width),
118
+ 'tnxtdm-slide-radio-group--disabled': this.isDisabled,
119
+ }
120
+ ];
121
+ },
122
+ containerStyle() {
123
+ if (['auto', 'fit-content'].includes(this.width)) {
124
+ return {};
125
+ }
126
+ return {
127
+ width: this.width,
128
+ };
129
+ },
130
+ },
131
+ watch: {
132
+ modelValue: {
133
+ handler() {
134
+ this.ensureValue(); // 监听 modelValue 变化,确保其有效
135
+ this.$nextTick(this.updateGlider);
136
+ },
137
+ immediate: true,
138
+ },
139
+ items: {
140
+ handler() {
141
+ this.itemRefs = [];
142
+ this.ensureValue();
143
+ this.$nextTick(() => {
144
+ this.setupObserver();
145
+ this.updateGlider();
146
+ });
147
+ },
148
+ deep: true,
149
+ immediate: true, // items 初始化时也需要检查
150
+ },
151
+ },
152
+ mounted() {
153
+ this.updateGlider();
154
+ window.addEventListener('resize', this.updateGlider);
155
+ this.setupObserver();
156
+ // 兼容某些场景下初始渲染时宽度计算不准确的问题
157
+ setTimeout(this.updateGlider, 50);
158
+ setTimeout(this.updateGlider, 200);
159
+ this.initFormDisabled();
160
+ },
161
+ beforeUnmount() {
162
+ window.removeEventListener('resize', this.updateGlider);
163
+ if (this.resizeObserver) {
164
+ this.resizeObserver.disconnect();
165
+ }
166
+ },
167
+ methods: {
168
+ initFormDisabled() {
169
+ let parent = this.$parent;
170
+ while (parent) {
171
+ if (parent.$options && (parent.$options.name === 't-form' || parent.$options.name === 'TForm')) {
172
+ this.$watch(() => parent.disabled || (parent.$props && parent.$props.disabled), (val) => {
173
+ this.formDisabled = val;
174
+ }, {immediate: true});
175
+ break;
176
+ }
177
+ parent = parent.$parent;
178
+ }
179
+ },
180
+ setupObserver() {
181
+ if (this.$el instanceof Element && typeof ResizeObserver !== 'undefined') {
182
+ if (!this.resizeObserver) {
183
+ this.resizeObserver = new ResizeObserver(() => {
184
+ this.updateGlider();
185
+ });
186
+ }
187
+ this.resizeObserver.disconnect();
188
+ this.resizeObserver.observe(this.$el);
189
+ }
190
+ },
191
+ ensureValue() {
192
+ if (this.normalizedItems.length === 0) {
193
+ return;
194
+ }
195
+
196
+ const exists = this.normalizedItems.some(item => item.value === this.currentValue);
197
+ if (exists) {
198
+ return;
199
+ }
200
+
201
+ if (this.defaultValue !== undefined && this.defaultValue !== null) {
202
+ const defaultExists = this.normalizedItems.some(item => item.value === this.defaultValue);
203
+ if (defaultExists) {
204
+ this.currentValue = this.defaultValue;
205
+ return;
206
+ }
207
+ }
208
+
209
+ this.currentValue = this.normalizedItems[0].value;
210
+ },
211
+ onItemClick(value) {
212
+ if (this.isDisabled) {
213
+ return;
214
+ }
215
+ if (value !== this.currentValue) {
216
+ this.currentValue = value;
217
+ }
218
+ },
219
+ updateGlider() {
220
+ const index = this.normalizedItems.findIndex(item => item.value === this.currentValue);
221
+ if (index === -1 || !this.itemRefs[index]) {
222
+ this.gliderStyle = {
223
+ width: '0px',
224
+ opacity: 0,
225
+ left: '4px',
226
+ };
227
+ return;
228
+ }
229
+
230
+ const el = this.itemRefs[index];
231
+ if (this.size === 'small') {
232
+ const inset = 1;
233
+ const width = Math.max(el.offsetWidth - inset * 2, 0);
234
+ this.gliderStyle = {
235
+ width: `${width}px`,
236
+ transform: `translateX(${el.offsetLeft}px)`,
237
+ opacity: 1,
238
+ left: `${inset}px`,
239
+ };
240
+ } else {
241
+ const container = this.$el;
242
+ const paddingLeft = container ? parseFloat(getComputedStyle(container).paddingLeft || '0') : 0;
243
+ const width = el.offsetWidth;
244
+ this.gliderStyle = {
245
+ width: `${width}px`,
246
+ transform: `translateX(${el.offsetLeft - paddingLeft}px)`,
247
+ opacity: 1,
248
+ left: `${paddingLeft}px`,
249
+ };
250
+ }
251
+ },
252
+ },
253
+ }
254
+ </script>
255
+
256
+ <style>
257
+ .tnxtdm-slide-radio-group {
258
+ position: relative;
259
+ display: flex;
260
+ background-color: var(--td-gray-color-1);
261
+ padding: 0 0.25rem;
262
+ box-sizing: border-box;
263
+ user-select: none;
264
+ width: fit-content;
265
+ border: 1px solid var(--td-gray-color-2);
266
+ height: 2rem;
267
+ }
268
+
269
+ .tnxtdm-slide-radio-group--capsule {
270
+ border-radius: var(--td-radius-round);
271
+ }
272
+
273
+ .tnxtdm-slide-radio-group--rectangle {
274
+ border-radius: var(--td-radius-default);
275
+ }
276
+
277
+ .tnxtdm-slide-radio-group--small {
278
+ height: 1.5rem;
279
+ padding: 0;
280
+ }
281
+
282
+ .tnxtdm-slide-radio-group--small.tnxtdm-slide-radio-group--rectangle {
283
+ border-radius: 4px;
284
+ }
285
+
286
+ .tnxtdm-slide-radio-group--small.tnxtdm-slide-radio-group--rectangle .tnxtdm-slide-radio-group__glider {
287
+ border-radius: 4px;
288
+ }
289
+
290
+ .tnxtdm-slide-radio-group--small .tnxtdm-slide-radio-group__glider {
291
+ top: 1px;
292
+ bottom: 1px;
293
+ }
294
+
295
+ .tnxtdm-slide-radio-group__item {
296
+ padding: 0 12px;
297
+ font-size: 12px;
298
+ position: relative;
299
+ display: flex;
300
+ align-items: center;
301
+ justify-content: center;
302
+ font-weight: normal;
303
+ color: var(--td-text-color-secondary);
304
+ cursor: pointer;
305
+ z-index: 1;
306
+ transition: color 0.2s;
307
+ text-align: center;
308
+ white-space: nowrap;
309
+ }
310
+
311
+ .tnxtdm-slide-radio-group__item.is-active {
312
+ color: var(--td-text-color-primary);
313
+ font-weight: normal;
314
+ }
315
+
316
+ .tnxtdm-slide-radio-group--small .tnxtdm-slide-radio-group__item {
317
+ padding: 0 10px;
318
+ font-size: 10px;
319
+ }
320
+
321
+ .tnxtdm-slide-radio-group--medium .tnxtdm-slide-radio-group__glider {
322
+ top: 3px;
323
+ bottom: 3px;
324
+ }
325
+
326
+ .tnxtdm-slide-radio-group--large {
327
+ height: 2.5rem;
328
+ padding: 0 5px;
329
+ }
330
+
331
+ .tnxtdm-slide-radio-group--large .tnxtdm-slide-radio-group__item {
332
+ padding: 0 14px;
333
+ font-size: 14px;
334
+ }
335
+
336
+ .tnxtdm-slide-radio-group--large .tnxtdm-slide-radio-group__glider {
337
+ top: 5px;
338
+ bottom: 5px;
339
+ }
340
+
341
+ .tnxtdm-slide-radio-group--fixed .tnxtdm-slide-radio-group__item {
342
+ flex: 1;
343
+ white-space: normal;
344
+ }
345
+
346
+ .tnxtdm-slide-radio-group__glider {
347
+ position: absolute;
348
+ top: 2px;
349
+ bottom: 2px;
350
+ left: 0.25rem; /* Default left, will be overridden by style binding */
351
+ background-color: var(--td-bg-color-container);
352
+ transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
353
+ box-shadow: var(--td-shadow-1, 0 1px 2px rgba(0, 0, 0, 0.1));
354
+ z-index: 0;
355
+ }
356
+
357
+ .tnxtdm-slide-radio-group--capsule .tnxtdm-slide-radio-group__glider {
358
+ border-radius: var(--td-radius-round);
359
+ }
360
+
361
+ .tnxtdm-slide-radio-group--rectangle .tnxtdm-slide-radio-group__glider {
362
+ border-radius: var(--td-radius-default);
363
+ }
364
+
365
+ .tnxtdm-slide-radio-group--theme-primary .tnxtdm-slide-radio-group__glider {
366
+ background-color: var(--td-brand-color);
367
+ }
368
+
369
+ .tnxtdm-slide-radio-group--theme-success .tnxtdm-slide-radio-group__glider {
370
+ background-color: var(--td-success-color);
371
+ }
372
+
373
+ .tnxtdm-slide-radio-group--theme-warning .tnxtdm-slide-radio-group__glider {
374
+ background-color: var(--td-warning-color);
375
+ }
376
+
377
+ .tnxtdm-slide-radio-group--theme-danger .tnxtdm-slide-radio-group__glider {
378
+ background-color: var(--td-error-color);
379
+ }
380
+
381
+ .tnxtdm-slide-radio-group--theme-primary .tnxtdm-slide-radio-group__item.is-active,
382
+ .tnxtdm-slide-radio-group--theme-success .tnxtdm-slide-radio-group__item.is-active,
383
+ .tnxtdm-slide-radio-group--theme-warning .tnxtdm-slide-radio-group__item.is-active,
384
+ .tnxtdm-slide-radio-group--theme-danger .tnxtdm-slide-radio-group__item.is-active {
385
+ color: #ffffff;
386
+ }
387
+
388
+ .tnxtdm-slide-radio-group__item-icon {
389
+ margin-right: 4px;
390
+ font-size: 16px;
391
+ }
392
+
393
+ .tnxtdm-slide-radio-group--disabled {
394
+ opacity: 0.6;
395
+ cursor: not-allowed;
396
+ }
397
+ </style>
@@ -0,0 +1,125 @@
1
+ /**
2
+ * tnxtdm.css
3
+ */
4
+ :root {
5
+ --max-page-width: 442px; /* 手机像素的最大值,IPhone 17 Pro */
6
+ --td-tag-medium-padding: 1px 8px;
7
+ --td-tag-large-padding: 2px 10px;
8
+ }
9
+
10
+ .t-loading__gradient-conic {
11
+ transform: none !important;
12
+ transform-origin: 50% 50% !important;
13
+ }
14
+
15
+ .t-input.border {
16
+ padding: 11px 1rem;
17
+ border-radius: var(--td-radius-default);
18
+
19
+ &::after {
20
+ height: 0;
21
+ }
22
+ }
23
+
24
+ .t-input.bg {
25
+ padding: 12px 1rem;
26
+ border-radius: var(--td-radius-default);
27
+ background-color: var(--td-bg-color-page);
28
+
29
+ &::after {
30
+ height: 0;
31
+ }
32
+ }
33
+
34
+ .t-input.rounded {
35
+ border-radius: var(--td-radius-round);
36
+ padding: 0.75rem 1rem;
37
+ }
38
+
39
+ .t-input__control {
40
+ height: 24px;
41
+ padding-bottom: 2px;
42
+ }
43
+
44
+ .t-input__wrap--suffix {
45
+ border-left: 1px solid var(--td-border-level-2-color);
46
+ margin-left: var(--td-spacer-1, 12px);
47
+ }
48
+
49
+ /* 修复密码输入框占位符间距过大的问题 */
50
+ .t-input__control[type="password"] {
51
+ letter-spacing: normal;
52
+ }
53
+
54
+ .t-input[size="small"] {
55
+ padding: 0.5rem 0.75rem;
56
+ }
57
+
58
+ .t-input[size="small"] .t-input__icon--prefix {
59
+ font-size: 20px;
60
+ }
61
+
62
+ .t-input[size="small"] .t-input__icon--prefix:not(:empty) + .t-input__label:empty {
63
+ margin-right: var(--td-spacer-1, 12px);
64
+ }
65
+
66
+ .t-input[size="small"] .t-input__control {
67
+ height: 20px;
68
+ font-size: 14px;
69
+ }
70
+
71
+ .t-dialog__body,
72
+ .t-drawer__sidebar {
73
+ overflow-y: auto;
74
+ }
75
+
76
+ .t-dialog__header + .t-dialog__body {
77
+ margin-top: 1rem;
78
+ }
79
+
80
+ .t-drawer__sidebar-item-icon {
81
+ display: flex;
82
+ align-items: center;
83
+ justify-content: center;
84
+ }
85
+
86
+ .t-form__label--top {
87
+ padding-right: 0;
88
+ }
89
+
90
+ .t-popover__wrapper {
91
+ display: flex;
92
+ align-items: center;
93
+ }
94
+
95
+ .t-button-group {
96
+ display: flex;
97
+ flex-direction: row;
98
+ align-items: center;
99
+ border-radius: var(--td-button-border-radius, var(--td-radius-default, 6px));
100
+ }
101
+
102
+ .t-button-group .t-button {
103
+ width: fit-content;
104
+ }
105
+
106
+ .t-button-group .t-button:first-child:not(:last-child),
107
+ .t-button-group .t-button:first-child:not(:last-child)::after {
108
+ border-top-right-radius: 0;
109
+ border-bottom-right-radius: 0;
110
+ border-right: 1px solid var(--td-bg-color-component-active);
111
+ }
112
+
113
+ .t-button-group .t-button:last-child:not(:first-child),
114
+ .t-button-group .t-button:last-child:not(:first-child)::after {
115
+ border-top-left-radius: 0;
116
+ border-bottom-left-radius: 0;
117
+ }
118
+
119
+ .t-dialog__body {
120
+ text-align: unset;
121
+ }
122
+
123
+ .t-image__img {
124
+ width: auto;
125
+ }