tang-ui-x 1.1.1 → 1.1.3

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 (91) hide show
  1. package/README.md +1003 -0
  2. package/components/TActionSheet/index.uvue +15 -2
  3. package/components/TCheckboxGroup/index.uvue +30 -11
  4. package/components/TCheckboxGroup/type.uts +10 -0
  5. package/components/TCollapse/index.uvue +1 -1
  6. package/components/TCollapse/type.uts +3 -1
  7. package/components/TCollapseItem/index.uvue +22 -26
  8. package/components/TDialog/index.uvue +19 -4
  9. package/components/TEmpty/index.uvue +28 -14
  10. package/components/TForm/index.uvue +394 -392
  11. package/components/TForm/type.uts +90 -80
  12. package/components/TInput/index.uvue +24 -5
  13. package/components/TInput/type.uts +10 -0
  14. package/components/TPicker/index.uvue +26 -6
  15. package/components/TRadioGroup/index.uvue +32 -12
  16. package/components/TRadioGroup/type.uts +7 -0
  17. package/components/TSearchBar/index.uvue +19 -4
  18. package/composables/i18n/error.uts +82 -0
  19. package/composables/i18n/index.uts +188 -0
  20. package/composables/i18n/manager-demo.uts +104 -0
  21. package/composables/i18n/manager.test.uts +182 -0
  22. package/composables/i18n/manager.uts +531 -0
  23. package/composables/i18n/register-demo.uts +125 -0
  24. package/composables/i18n/task22-verification.uts +198 -0
  25. package/composables/i18n/task23-verification.uts +343 -0
  26. package/composables/i18n/task8-demo.uts +93 -0
  27. package/composables/i18n/task8-verification.uts +98 -0
  28. package/composables/i18n/test-task23.uts +9 -0
  29. package/composables/i18n/types.uts +46 -0
  30. package/composables/i18n/useI18n-verification.uts +105 -0
  31. package/composables/i18n/validation-demo.uts +45 -0
  32. package/composables/i18n/validation-test.uts +106 -0
  33. package/composables/useI18n.uts +77 -0
  34. package/index.uts +23 -0
  35. package/locales/cross-platform-verification.uts +510 -0
  36. package/locales/en-US/actionSheet.json +3 -0
  37. package/locales/en-US/common.json +17 -0
  38. package/locales/en-US/dialog.json +5 -0
  39. package/locales/en-US/empty.json +6 -0
  40. package/locales/en-US/errorState.json +5 -0
  41. package/locales/en-US/examplePages.json +1236 -0
  42. package/locales/en-US/examples.json +218 -0
  43. package/locales/en-US/form.json +18 -0
  44. package/locales/en-US/input.json +3 -0
  45. package/locales/en-US/list.json +5 -0
  46. package/locales/en-US/loading.json +3 -0
  47. package/locales/en-US/navBar.json +4 -0
  48. package/locales/en-US/noticeBar.json +3 -0
  49. package/locales/en-US/picker.json +5 -0
  50. package/locales/en-US/searchBar.json +4 -0
  51. package/locales/en-US/textarea.json +3 -0
  52. package/locales/en-US/toast.json +6 -0
  53. package/locales/index.uts +79 -0
  54. package/locales/init-verification.uts +101 -0
  55. package/locales/loader.uts +251 -0
  56. package/locales/run-verification.uts +16 -0
  57. package/locales/zh-CN/actionSheet.json +3 -0
  58. package/locales/zh-CN/common.json +17 -0
  59. package/locales/zh-CN/dialog.json +5 -0
  60. package/locales/zh-CN/empty.json +6 -0
  61. package/locales/zh-CN/errorState.json +5 -0
  62. package/locales/zh-CN/examplePages.json +1236 -0
  63. package/locales/zh-CN/examples.json +218 -0
  64. package/locales/zh-CN/form.json +18 -0
  65. package/locales/zh-CN/input.json +3 -0
  66. package/locales/zh-CN/list.json +5 -0
  67. package/locales/zh-CN/loading.json +3 -0
  68. package/locales/zh-CN/navBar.json +4 -0
  69. package/locales/zh-CN/noticeBar.json +3 -0
  70. package/locales/zh-CN/picker.json +5 -0
  71. package/locales/zh-CN/searchBar.json +4 -0
  72. package/locales/zh-CN/textarea.json +3 -0
  73. package/locales/zh-CN/toast.json +6 -0
  74. package/locales/zh-TW/actionSheet.json +3 -0
  75. package/locales/zh-TW/common.json +15 -0
  76. package/locales/zh-TW/dialog.json +5 -0
  77. package/locales/zh-TW/empty.json +6 -0
  78. package/locales/zh-TW/errorState.json +5 -0
  79. package/locales/zh-TW/examplePages.json +705 -0
  80. package/locales/zh-TW/examples.json +218 -0
  81. package/locales/zh-TW/form.json +18 -0
  82. package/locales/zh-TW/input.json +3 -0
  83. package/locales/zh-TW/list.json +5 -0
  84. package/locales/zh-TW/loading.json +3 -0
  85. package/locales/zh-TW/navBar.json +4 -0
  86. package/locales/zh-TW/noticeBar.json +3 -0
  87. package/locales/zh-TW/picker.json +5 -0
  88. package/locales/zh-TW/searchBar.json +4 -0
  89. package/locales/zh-TW/textarea.json +3 -0
  90. package/locales/zh-TW/toast.json +6 -0
  91. package/package.json +3 -2
@@ -1,6 +1,8 @@
1
1
  <script setup lang="uts">
2
+ import { computed } from 'vue'
2
3
  import TPopup from '../TPopup/index.uvue'
3
4
  import type { ActionSheetAction, TActionSheetProps } from './type.uts'
5
+ import { useI18n } from '../../composables/useI18n.uts'
4
6
 
5
7
  /**
6
8
  * TActionSheet 动作面板组件
@@ -14,7 +16,6 @@ const props = withDefaults(defineProps<Props>(), {
14
16
  actions: () => [] as ActionSheetAction[],
15
17
  title: '',
16
18
  description: '',
17
- cancelText: '取消',
18
19
  closeOnClickAction: true,
19
20
  closeOnClickOverlay: true,
20
21
  customClass: '',
@@ -30,6 +31,18 @@ const emit = defineEmits<{
30
31
  close: []
31
32
  }>()
32
33
 
34
+ // 使用 i18n
35
+ const { $t } = useI18n()
36
+
37
+ // 优先使用用户传入的值,否则使用翻译
38
+ const displayCancelText = computed(() => {
39
+ // 如果用户明确传入了 cancelText(即使是空字符串),使用用户的值
40
+ // 否则使用翻译
41
+ return props.cancelText !== undefined && props.cancelText !== ''
42
+ ? props.cancelText
43
+ : $t('actionSheet.cancelText')
44
+ })
45
+
33
46
  const handleSelect = (action: ActionSheetAction, index: number): void => {
34
47
  if (action.disabled || action.loading) return
35
48
 
@@ -90,7 +103,7 @@ const handleClose = (): void => {
90
103
 
91
104
  <!-- 取消按钮 -->
92
105
  <view class="t-action-sheet__cancel" @click="handleCancel">
93
- <text class="t-action-sheet__cancel-text">{{ cancelText }}</text>
106
+ <text class="t-action-sheet__cancel-text">{{ displayCancelText }}</text>
94
107
  </view>
95
108
  </view>
96
109
  </TPopup>
@@ -13,6 +13,9 @@ type Props = Omit<TCheckboxGroupProps, 'modelValue'>
13
13
  const props = withDefaults(defineProps<Props>(), {
14
14
  options: () => [] as CheckboxOption[],
15
15
  direction: 'vertical',
16
+ size: 'medium',
17
+ activeColor: '#00bba7',
18
+ inactiveColor: '#c8c9cc',
16
19
  max: -1,
17
20
  disabled: false,
18
21
  customClass: '',
@@ -56,20 +59,36 @@ const handleChange = (option: CheckboxOption): void => {
56
59
  const isChecked = (option: CheckboxOption): boolean => {
57
60
  return modelValue.value.includes(option.value)
58
61
  }
62
+
63
+ // 尺寸映射
64
+ const sizeMap = {
65
+ small: { icon: 32, fontSize: 20 },
66
+ medium: { icon: 40, fontSize: 24 },
67
+ large: { icon: 48, fontSize: 28 }
68
+ }
69
+
70
+ const currentSize = computed(() => sizeMap[props.size])
71
+
72
+ const cssVars = computed(() => ({
73
+ '--icon-size': `${currentSize.value.icon}rpx`,
74
+ '--checkmark-size': `${currentSize.value.fontSize}rpx`,
75
+ '--active-color': props.activeColor,
76
+ '--inactive-color': props.inactiveColor
77
+ }))
59
78
  </script>
60
79
 
61
80
  <template>
62
81
  <view
63
82
  class="t-checkbox-group"
64
83
  :class="[`t-checkbox-group--${direction}`, customClass]"
65
- :style="customStyle"
84
+ :style="[customStyle, cssVars]"
66
85
  >
67
86
  <view
68
87
  v-for="(option, index) in options"
69
88
  :key="index"
70
89
  class="t-checkbox-group__item"
71
90
  :class="{ 't-checkbox-group__item--disabled': disabled || option.disabled }"
72
- @click="() => handleChange(option)"
91
+ @click="handleChange(option)"
73
92
  >
74
93
  <view class="t-checkbox-group__icon" :class="{ 't-checkbox-group__icon--checked': isChecked(option) }">
75
94
  <text v-if="isChecked(option)" class="t-checkbox-group__check">✓</text>
@@ -95,7 +114,7 @@ const isChecked = (option: CheckboxOption): boolean => {
95
114
 
96
115
  .t-checkbox-group__item {
97
116
  display: flex;
98
- align-items: center;
117
+ flex-direction: row;
99
118
  padding: 12px 0;
100
119
  cursor: pointer;
101
120
 
@@ -110,24 +129,24 @@ const isChecked = (option: CheckboxOption): boolean => {
110
129
  }
111
130
 
112
131
  .t-checkbox-group__icon {
113
- width: 20px;
114
- height: 20px;
115
- border: 2px solid #c8c9cc;
116
- border-radius: 4px;
117
- margin-right: 8px;
132
+ width: var(--icon-size);
133
+ height: var(--icon-size);
134
+ border: 2rpx solid var(--inactive-color);
135
+ border-radius: 8rpx;
136
+ margin-right: 16rpx;
118
137
  display: flex;
119
138
  align-items: center;
120
139
  justify-content: center;
121
140
  transition: all 0.3s;
122
141
 
123
142
  &--checked {
124
- background-color: #1890ff;
125
- border-color: #1890ff;
143
+ background-color: var(--active-color);
144
+ border-color: var(--active-color);
126
145
  }
127
146
  }
128
147
 
129
148
  .t-checkbox-group__check {
130
- font-size: 14px;
149
+ font-size: var(--checkmark-size);
131
150
  color: #fff;
132
151
  font-weight: bold;
133
152
  }
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  export type CheckboxDirection = 'horizontal' | 'vertical'
6
+ export type CheckboxSize = 'small' | 'medium' | 'large'
6
7
 
7
8
  export interface CheckboxOption {
8
9
  label: string
@@ -14,8 +15,17 @@ export interface TCheckboxGroupProps {
14
15
  modelValue?: (string | number)[]
15
16
  options?: CheckboxOption[]
16
17
  direction?: CheckboxDirection
18
+ /** 按钮尺寸 */
19
+ size?: CheckboxSize
20
+ /** 激活状态颜色 */
21
+ activeColor?: string
22
+ /** 非激活状态颜色 */
23
+ inactiveColor?: string
24
+ /** 最大可选数量 */
17
25
  max?: number
18
26
  disabled?: boolean
27
+ /** 名称属性(用于表单提交) */
28
+ name?: string
19
29
  customClass?: string
20
30
  customStyle?: string
21
31
  }
@@ -53,7 +53,7 @@ const toggle = (name: string): void => {
53
53
  * 提供给子组件的上下文
54
54
  */
55
55
  const collapseContext: TCollapseContext = {
56
- activeNames: activeNamesArray.value,
56
+ activeNames: activeNamesArray,
57
57
  accordion: props.accordion,
58
58
  toggle
59
59
  }
@@ -15,6 +15,8 @@ export type TCollapseProps = {
15
15
  border?: boolean
16
16
  }
17
17
 
18
+ import type { ComputedRef } from 'vue'
19
+
18
20
  /**
19
21
  * TCollapse 提供给子组件的上下文
20
22
  */
@@ -22,7 +24,7 @@ export type TCollapseContext = {
22
24
  /**
23
25
  * 当前展开的面板名称(数组)
24
26
  */
25
- activeNames: string[]
27
+ activeNames: ComputedRef<string[]>
26
28
 
27
29
  /**
28
30
  * 是否为手风琴模式
@@ -18,12 +18,24 @@ const props = withDefaults(defineProps<TCollapseItemProps>(), {
18
18
  // 从父组件注入上下文
19
19
  const collapseContext = inject<TCollapseContext>('TCollapse')
20
20
 
21
+ // 内容高度
22
+ const contentHeight = ref<number>(0)
23
+ const contentRef = ref<any>(null)
24
+
21
25
  // 计算是否展开
22
26
  const isActive = computed<boolean>(() => {
23
27
  if (collapseContext == null) {
24
28
  return false
25
29
  }
26
- return collapseContext.activeNames.indexOf(props.name) > -1
30
+ return collapseContext.activeNames.value.indexOf(props.name) > -1
31
+ })
32
+
33
+ // 计算内容样式
34
+ const contentStyle = computed<string>(() => {
35
+ if (isActive.value) {
36
+ return 'max-height: 2000px; opacity: 1; padding-top: 0; padding-bottom: 0;'
37
+ }
38
+ return 'max-height: 0; opacity: 0; padding-top: 0; padding-bottom: 0;'
27
39
  })
28
40
 
29
41
  /**
@@ -88,13 +100,11 @@ const arrowClass = computed<string>(() => {
88
100
  </view>
89
101
 
90
102
  <!-- 内容区域 -->
91
- <transition name="t-collapse">
92
- <view v-show="isActive" class="t-collapse-item__content">
93
- <view class="t-collapse-item__content-inner">
94
- <slot></slot>
95
- </view>
103
+ <view class="t-collapse-item__content" :style="contentStyle">
104
+ <view class="t-collapse-item__content-inner">
105
+ <slot></slot>
96
106
  </view>
97
- </transition>
107
+ </view>
98
108
  </view>
99
109
  </template>
100
110
 
@@ -147,7 +157,7 @@ const arrowClass = computed<string>(() => {
147
157
  display: flex;
148
158
  align-items: center;
149
159
  justify-content: center;
150
- transition: transform 0.3s;
160
+ transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
151
161
 
152
162
  &--active {
153
163
  transform: rotate(90deg);
@@ -164,31 +174,17 @@ const arrowClass = computed<string>(() => {
164
174
  &__content {
165
175
  background-color: #ffffff;
166
176
  overflow: hidden;
177
+ transition: max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1),
178
+ opacity 0.35s cubic-bezier(0.4, 0, 0.2, 1),
179
+ padding 0.35s cubic-bezier(0.4, 0, 0.2, 1);
167
180
 
168
181
  &-inner {
169
182
  padding: 16px 20px;
170
183
  font-size: 14px;
171
184
  color: #606266;
172
185
  line-height: 1.6;
186
+ transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
173
187
  }
174
188
  }
175
189
  }
176
-
177
- /* 折叠动画 */
178
- .t-collapse-enter-active,
179
- .t-collapse-leave-active {
180
- transition: all 0.3s ease;
181
- }
182
-
183
- .t-collapse-enter-from,
184
- .t-collapse-leave-to {
185
- opacity: 0;
186
- max-height: 0;
187
- }
188
-
189
- .t-collapse-enter-to,
190
- .t-collapse-leave-from {
191
- opacity: 1;
192
- max-height: 1000px;
193
- }
194
190
  </style>
@@ -1,6 +1,7 @@
1
1
  <script setup lang="uts" >
2
2
  import { computed } from 'vue'
3
3
  import type { TDialogProps } from './type.uts'
4
+ import { useI18n } from '../../composables/useI18n.uts'
4
5
 
5
6
  /**
6
7
  * TDialog 对话框组件
@@ -18,8 +19,6 @@ const props = withDefaults(defineProps<Props>(), {
18
19
  maskClosable: true,
19
20
  showClose: true,
20
21
  showCancel: true,
21
- confirmText: '确定',
22
- cancelText: '取消',
23
22
  confirmType: 'primary'
24
23
  })
25
24
 
@@ -33,6 +32,22 @@ const emit = defineEmits<{
33
32
  close: []
34
33
  }>()
35
34
 
35
+ // 使用 i18n
36
+ const { $t } = useI18n()
37
+
38
+ // 优先使用用户传入的值,否则使用翻译
39
+ const displayConfirmText = computed(() => {
40
+ return props.confirmText !== undefined && props.confirmText !== ''
41
+ ? props.confirmText
42
+ : $t('dialog.confirmText')
43
+ })
44
+
45
+ const displayCancelText = computed(() => {
46
+ return props.cancelText !== undefined && props.cancelText !== ''
47
+ ? props.cancelText
48
+ : $t('dialog.cancelText')
49
+ })
50
+
36
51
  /**
37
52
  * 计算对话框样式
38
53
  */
@@ -129,10 +144,10 @@ const handleClose = (): void => {
129
144
  <view class="t-dialog__footer">
130
145
  <slot name="footer">
131
146
  <view v-if="showCancel" class="t-dialog__btn t-dialog__btn--cancel" @click="handleCancel">
132
- <text class="t-dialog__btn-text">{{ cancelText }}</text>
147
+ <text class="t-dialog__btn-text">{{ displayCancelText }}</text>
133
148
  </view>
134
149
  <view :class="confirmBtnClass" @click="handleConfirm">
135
- <text class="t-dialog__btn-text t-dialog__btn-text--confirm">{{ confirmText }}</text>
150
+ <text class="t-dialog__btn-text t-dialog__btn-text--confirm">{{ displayConfirmText }}</text>
136
151
  </view>
137
152
  </slot>
138
153
  </view>
@@ -1,4 +1,8 @@
1
1
  <script setup lang="uts" >
2
+ import { computed } from 'vue'
3
+ import TButton from '../TButton/index.uvue'
4
+ import { useI18n } from '../../composables/useI18n.uts'
5
+
2
6
  interface EmptyProps {
3
7
  /** 标题 */
4
8
  title ?: string;
@@ -15,11 +19,8 @@
15
19
  }
16
20
 
17
21
  const props = withDefaults(defineProps<EmptyProps>(), {
18
- title: '暂无数据',
19
- description: '当前没有相关内容',
20
22
  image: '/static/images/empty.png',
21
23
  showAction: false,
22
- actionText: '刷新试试',
23
24
  compact: false
24
25
  });
25
26
 
@@ -27,6 +28,14 @@
27
28
  actionClick : []
28
29
  }>();
29
30
 
31
+ // 使用 i18n
32
+ const { $t } = useI18n()
33
+
34
+ // 优先使用用户传入的值,否则使用翻译
35
+ const displayTitle = computed(() => props.title || $t('empty.title'))
36
+ const displayDescription = computed(() => props.description || $t('empty.description'))
37
+ const displayActionText = computed(() => props.actionText || $t('empty.actionText'))
38
+
30
39
  const handleActionClick = () => {
31
40
  emits('actionClick');
32
41
  };
@@ -43,7 +52,7 @@
43
52
  'empty-image',
44
53
  compact ? 'w-24 h-24 mb-3' : 'w-32 h-32 mb-4'
45
54
  ]">
46
- <LazyImage :src="image" class="w-full h-full" mode="aspectFit" :show-loading="false" />
55
+ <image :src="image" class="w-full h-full" mode="aspectFit" :show-loading="false" />
47
56
  </view>
48
57
  </slot>
49
58
 
@@ -52,21 +61,25 @@
52
61
  'empty-title block text-gray-700 dark:text-gray-300 text-center',
53
62
  compact ? 'text-base font-medium' : 'text-lg font-medium',
54
63
  ]">
55
- {{ title }}
64
+ {{ displayTitle }}
56
65
  </text>
57
66
 
58
- <text v-if="description" :class="[
67
+ <text v-if="displayDescription" :class="[
59
68
  'empty-description block mt-2 text-gray-500 dark:text-gray-400',
60
69
  compact ? 'text-xs' : 'text-sm',
61
70
  ]">
62
- {{ description }}
71
+ {{ displayDescription }}
63
72
  </text>
64
73
  </view>
65
74
 
66
75
  <view v-if="showAction" class="empty-action mt-6">
67
- <ButtonX size='small' :block="true" @tap="handleActionClick">
68
- {{ actionText }}
69
- </ButtonX>
76
+ <TButton
77
+ type="primary"
78
+ size="medium"
79
+ @click="handleActionClick"
80
+ >
81
+ {{ displayActionText }}
82
+ </TButton>
70
83
  </view>
71
84
  </view>
72
85
  </template>
@@ -105,6 +118,11 @@
105
118
  max-width: 80vw;
106
119
  }
107
120
 
121
+ .empty-action {
122
+ width: 100%;
123
+ max-width: 200px;
124
+ }
125
+
108
126
  // 动画效果
109
127
  .empty-image {
110
128
  animation: fadeIn 0.5s ease;
@@ -121,8 +139,4 @@
121
139
  transform: translateY(0);
122
140
  }
123
141
  }
124
-
125
- :deep(.btn-small) {
126
- padding: 0 1rem
127
- }
128
142
  </style>