tang-ui-x 1.1.2 → 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.
@@ -1,84 +1,90 @@
1
- /**
2
- * 表单选项类型
3
- */
4
- export type FormOption = {
5
- label: string
6
- value: string | number
7
- }
8
-
9
- /**
10
- * 组件通用属性
11
- */
12
- export type ComponentProps = {
13
- /** 占位符 */
14
- placeholder?: string
15
- /** 是否禁用 */
16
- disabled?: boolean
17
- /** 选项列表(用于 select、radio、checkbox) */
18
- options?: FormOption[]
19
- /** 输入类型(用于 input) */
20
- type?: string
21
- /** 最大长度(用于 input、textarea) */
22
- maxlength?: number
23
- /** 最小值(用于 number、slider) */
24
- min?: number
25
- /** 最大值(用于 number、slider、rate) */
26
- max?: number
27
- /** 步长(用于 slider) */
28
- step?: number
29
- /** 是否自动聚焦(用于 input、textarea) */
30
- focus?: boolean
31
- /** 确认按钮文字(用于 input) */
32
- confirmType?: string
33
- /** 是否自动高度(用于 textarea) */
34
- autoHeight?: boolean
35
- /** 是否显示确认栏(用于 textarea) */
36
- showConfirmBar?: boolean
37
- /** 开始日期(用于 date) */
38
- start?: string
39
- /** 结束日期(用于 date) */
40
- end?: string
41
- /** 激活颜色(用于 switch) */
42
- color?: string
43
- /** 是否允许半星(用于 rate) */
44
- allowHalf?: boolean
45
- /** 是否显示值(用于 slider) */
46
- showValue?: boolean
47
- /** 其他扩展属性 */
48
- [key: string]: any
49
- }
50
-
51
- /**
52
- * 表单字段配置
53
- */
54
- export type FormSchema = {
55
- /** 字段名 */
56
- field: string
57
- /** 标签文本 */
58
- label: string
59
- /** 组件类型 */
60
- component: 'Input' | 'Textarea' | 'Select' | 'Date' | 'Time' | 'Radio' | 'Checkbox' | 'Switch' | 'Rate' | 'Slider' | 'InputNumber'
61
- /** 是否必填 */
62
- required?: boolean
63
- /** 组件属性 */
64
- componentProps?: ComponentProps
65
- }
66
-
67
- /**
68
- * 表单组件属性
69
- */
70
- export type TFormProps = {
71
- /** 表单配置 */
72
- schemas: FormSchema[]
73
- /** 标签宽度 */
74
- labelWidth?: string
75
- /** 是否隐藏默认按钮 */
76
- hideButtons?: boolean
77
- /** 提交按钮文本 */
78
- submitText?: string
79
- /** 重置按钮文本 */
80
- resetText?: string
81
- }
82
-
83
- // 导出所有类型
84
- export { FormOption, ComponentProps, FormSchema, TFormProps }
1
+ /**
2
+ * 表单选项类型
3
+ */
4
+ export type FormOption = {
5
+ label: string
6
+ value: string | number
7
+ }
8
+
9
+ /**
10
+ * 组件通用属性
11
+ */
12
+ export type ComponentProps = {
13
+ /** 占位符 */
14
+ placeholder?: string
15
+ /** 是否禁用 */
16
+ disabled?: boolean
17
+ /** 选项列表(用于 select、radio、checkbox) */
18
+ options?: FormOption[]
19
+ /** 输入类型(用于 input) */
20
+ type?: string
21
+ /** 最大长度(用于 input、textarea) */
22
+ maxlength?: number
23
+ /** 最小值(用于 number、slider) */
24
+ min?: number
25
+ /** 最大值(用于 number、slider、rate) */
26
+ max?: number
27
+ /** 步长(用于 slider) */
28
+ step?: number
29
+ /** 是否自动聚焦(用于 input、textarea) */
30
+ focus?: boolean
31
+ /** 确认按钮文字(用于 input) */
32
+ confirmType?: string
33
+ /** 是否自动高度(用于 textarea) */
34
+ autoHeight?: boolean
35
+ /** 是否显示确认栏(用于 textarea) */
36
+ showConfirmBar?: boolean
37
+ /** 开始日期(用于 date) */
38
+ start?: string
39
+ /** 结束日期(用于 date) */
40
+ end?: string
41
+ /** 激活颜色(用于 switch) */
42
+ color?: string
43
+ /** 是否允许半星(用于 rate) */
44
+ allowHalf?: boolean
45
+ /** 是否显示值(用于 slider) */
46
+ showValue?: boolean
47
+ /** 其他扩展属性 */
48
+ [key: string]: any
49
+ }
50
+
51
+ /**
52
+ * 表单字段配置
53
+ */
54
+ export type FormSchema = {
55
+ /** 字段名 */
56
+ field: string
57
+ /** 标签文本 */
58
+ label: string
59
+ /** 组件类型 */
60
+ component: 'Input' | 'Textarea' | 'Select' | 'Date' | 'Time' | 'Radio' | 'Checkbox' | 'Switch' | 'Rate' | 'Slider' | 'InputNumber'
61
+ /** 是否必填 */
62
+ required?: boolean
63
+ /** 组件属性 */
64
+ componentProps?: ComponentProps
65
+ /** 单个配置项的布局方向(优先级高于整体 layout) */
66
+ layout?: 'horizontal' | 'vertical'
67
+ /** 单个配置项的标签宽度(优先级高于整体 labelWidth) */
68
+ labelWidth?: string
69
+ }
70
+
71
+ /**
72
+ * 表单组件属性
73
+ */
74
+ export type TFormProps = {
75
+ /** 表单配置 */
76
+ schemas: FormSchema[]
77
+ /** 标签宽度 */
78
+ labelWidth?: string
79
+ /** 表单项布局方向 */
80
+ layout?: 'horizontal' | 'vertical'
81
+ /** 是否隐藏默认按钮 */
82
+ hideButtons?: boolean
83
+ /** 提交按钮文本 */
84
+ submitText?: string
85
+ /** 重置按钮文本 */
86
+ resetText?: string
87
+ }
88
+
89
+ // 导出所有类型
90
+ export { FormOption, ComponentProps, FormSchema, TFormProps }
@@ -12,6 +12,9 @@ type Props = Omit<TRadioGroupProps, 'modelValue'>
12
12
  const props = withDefaults(defineProps<Props>(), {
13
13
  options: () => [] as RadioOption[],
14
14
  direction: 'vertical',
15
+ size: 'medium',
16
+ activeColor: '#00bba7',
17
+ inactiveColor: '#c8c9cc',
15
18
  disabled: false,
16
19
  customClass: '',
17
20
  customStyle: ''
@@ -34,20 +37,37 @@ const handleChange = (option: RadioOption): void => {
34
37
  const isChecked = (option: RadioOption): boolean => {
35
38
  return modelValue.value === option.value
36
39
  }
40
+
41
+ // 尺寸映射
42
+ const sizeMap = {
43
+ small: { icon: 28, dot: 14, fontSize: 24 },
44
+ medium: { icon: 36, dot: 20, fontSize: 28 },
45
+ large: { icon: 44, dot: 26, fontSize: 32 }
46
+ }
47
+
48
+ const currentSize = computed(() => sizeMap[props.size])
49
+
50
+ const cssVars = computed(() => ({
51
+ '--icon-size': `${currentSize.value.icon}rpx`,
52
+ '--dot-size': `${currentSize.value.dot}rpx`,
53
+ '--font-size': `${currentSize.value.fontSize}rpx`,
54
+ '--active-color': props.activeColor,
55
+ '--inactive-color': props.inactiveColor
56
+ }))
37
57
  </script>
38
58
 
39
59
  <template>
40
60
  <view
41
61
  class="t-radio-group"
42
62
  :class="[`t-radio-group--${direction}`, customClass]"
43
- :style="customStyle"
63
+ :style="[customStyle, cssVars]"
44
64
  >
45
65
  <view
46
66
  v-for="(option, index) in options"
47
67
  :key="index"
48
68
  class="t-radio-group__item"
49
69
  :class="{ 't-radio-group__item--disabled': disabled || option.disabled }"
50
- @click="() => handleChange(option)"
70
+ @click="handleChange(option)"
51
71
  >
52
72
  <view class="t-radio-group__icon" :class="{ 't-radio-group__icon--checked': isChecked(option) }">
53
73
  <view v-if="isChecked(option)" class="t-radio-group__dot"></view>
@@ -73,7 +93,7 @@ const isChecked = (option: RadioOption): boolean => {
73
93
 
74
94
  .t-radio-group__item {
75
95
  display: flex;
76
- align-items: center;
96
+ flex-direction: row;
77
97
  padding: 12px 0;
78
98
  cursor: pointer;
79
99
 
@@ -88,30 +108,30 @@ const isChecked = (option: RadioOption): boolean => {
88
108
  }
89
109
 
90
110
  .t-radio-group__icon {
91
- width: 20px;
92
- height: 20px;
93
- border: 2px solid #c8c9cc;
111
+ width: var(--icon-size);
112
+ height: var(--icon-size);
113
+ border: 2rpx solid var(--inactive-color);
94
114
  border-radius: 50%;
95
- margin-right: 8px;
115
+ margin-right: 16rpx;
96
116
  display: flex;
97
117
  align-items: center;
98
118
  justify-content: center;
99
119
  transition: all 0.3s;
100
120
 
101
121
  &--checked {
102
- border-color: #1890ff;
122
+ border-color: var(--active-color);
103
123
  }
104
124
  }
105
125
 
106
126
  .t-radio-group__dot {
107
- width: 10px;
108
- height: 10px;
109
- background-color: #1890ff;
127
+ width: var(--dot-size);
128
+ height: var(--dot-size);
129
+ background-color: var(--active-color);
110
130
  border-radius: 50%;
111
131
  }
112
132
 
113
133
  .t-radio-group__label {
114
- font-size: 14px;
134
+ font-size: var(--font-size);
115
135
  color: #323233;
116
136
  }
117
137
  </style>
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  export type RadioDirection = 'horizontal' | 'vertical'
6
+ export type RadioSize = 'small' | 'medium' | 'large'
6
7
 
7
8
  export interface RadioOption {
8
9
  label: string
@@ -14,6 +15,12 @@ export interface TRadioGroupProps {
14
15
  modelValue?: string | number
15
16
  options?: RadioOption[]
16
17
  direction?: RadioDirection
18
+ /** 按钮尺寸 */
19
+ size?: RadioSize
20
+ /** 活动状态颜色 */
21
+ activeColor?: string
22
+ /** 非活动状态颜色 */
23
+ inactiveColor?: string
17
24
  disabled?: boolean
18
25
  customClass?: string
19
26
  customStyle?: string
@@ -230,10 +230,121 @@ export class I18nManager {
230
230
  /**
231
231
  * 注册或更新语言包(默认使用合并模式)
232
232
  * @param locale 语言代码
233
- * @param messages 模块化语言包
233
+ * @param messages 模块化语言包或语言包目录路径
234
234
  */
235
- registerMessages(locale: string, messages: ModularLocaleMessages): void {
236
- this.registerMessagesWithMode(locale, messages, 'merge')
235
+ registerMessages(locale: string, messages: ModularLocaleMessages | string): void {
236
+ // 如果传入的是字符串路径,则加载该路径下的所有语言包
237
+ if (typeof messages === 'string') {
238
+ this.registerMessagesFromPath(locale, messages)
239
+ } else {
240
+ this.registerMessagesWithMode(locale, messages, 'merge')
241
+ }
242
+ }
243
+
244
+ /**
245
+ * 注册或更新语言包(异步版本)
246
+ * @param locale 语言代码
247
+ * @param messages 模块化语言包或语言包目录路径
248
+ */
249
+ async registerMessagesAsync(locale: string, messages: ModularLocaleMessages | string): Promise<void> {
250
+ // 如果传入的是字符串路径,则加载该路径下的所有语言包
251
+ if (typeof messages === 'string') {
252
+ await this.registerMessagesFromPathAsync(locale, messages)
253
+ } else {
254
+ this.registerMessagesWithMode(locale, messages, 'merge')
255
+ }
256
+ }
257
+
258
+ /**
259
+ * 从路径加载语言包(异步版本)
260
+ * @param locale 语言代码
261
+ * @param basePath 语言包目录路径(例如:'locales/ja-JP')
262
+ */
263
+ private async registerMessagesFromPathAsync(locale: string, basePath: string): Promise<void> {
264
+ // 定义核心模块列表(只加载常用的模块,减少 404 请求)
265
+ const coreModules = [
266
+ 'common',
267
+ 'form',
268
+ 'input',
269
+ 'dialog',
270
+ 'empty',
271
+ 'loading'
272
+ ]
273
+
274
+ // 定义可选模块列表
275
+ const optionalModules = [
276
+ 'actionSheet',
277
+ 'errorState',
278
+ 'examplePages',
279
+ 'examples',
280
+ 'list',
281
+ 'navBar',
282
+ 'noticeBar',
283
+ 'picker',
284
+ 'searchBar',
285
+ 'textarea',
286
+ 'toast'
287
+ ]
288
+
289
+ const loadedMessages: ModularLocaleMessages = {}
290
+
291
+ // 先加载核心模块
292
+ await Promise.all(
293
+ coreModules.map(async (moduleName) => {
294
+ try {
295
+ // 构建文件路径
296
+ const filePath = `${basePath}/${moduleName}.json`
297
+
298
+ // 使用动态 import 加载 JSON 文件(支持 H5、App、小程序)
299
+ const module = await import(/* @vite-ignore */ filePath)
300
+ const moduleData = module.default || module
301
+
302
+ if (moduleData && typeof moduleData === 'object') {
303
+ loadedMessages[moduleName] = moduleData
304
+ }
305
+ } catch (e) {
306
+ // 核心模块加载失败时输出警告
307
+ console.warn(`[I18n] Failed to load core module ${moduleName} for locale ${locale}`)
308
+ }
309
+ })
310
+ )
311
+
312
+ // 尝试加载可选模块(静默失败)
313
+ await Promise.allSettled(
314
+ optionalModules.map(async (moduleName) => {
315
+ try {
316
+ const filePath = `${basePath}/${moduleName}.json`
317
+ const module = await import(/* @vite-ignore */ filePath)
318
+ const moduleData = module.default || module
319
+
320
+ if (moduleData && typeof moduleData === 'object') {
321
+ loadedMessages[moduleName] = moduleData
322
+ }
323
+ } catch (e) {
324
+ // 可选模块加载失败时静默忽略
325
+ }
326
+ })
327
+ )
328
+
329
+ // 如果成功加载了至少一个模块,则注册语言包
330
+ if (Object.keys(loadedMessages).length > 0) {
331
+ this.registerMessagesWithMode(locale, loadedMessages, 'merge')
332
+ } else {
333
+ I18nError.warnLocaleNotFound(locale)
334
+ }
335
+ }
336
+
337
+ /**
338
+ * 从路径加载语言包(同步版本,仅用于兼容)
339
+ * @param locale 语言代码
340
+ * @param basePath 语言包目录路径
341
+ */
342
+ private registerMessagesFromPath(locale: string, basePath: string): void {
343
+ // 调用异步版本,但不等待结果
344
+ this.registerMessagesFromPathAsync(locale, basePath).catch((e) => {
345
+ console.error(`[I18n] Failed to load locale ${locale} from path ${basePath}:`, e)
346
+ I18nError.warnLocaleNotFound(locale)
347
+ })
237
348
  }
238
349
 
239
350
  /**
@@ -333,4 +444,88 @@ export class I18nManager {
333
444
  getCurrentLocale(): string {
334
445
  return this.currentLocale.value
335
446
  }
447
+
448
+ /**
449
+ * 注册新语言(简化方法)
450
+ * 支持传入语言包对象或语言包目录路径
451
+ * @param locale 语言代码(例如:'ja-JP')
452
+ * @param messagesOrPath 语言包对象或语言包目录路径
453
+ *
454
+ * @example
455
+ * // 方式一:传入语言包对象
456
+ * i18nManager.registerLocale('ja-JP', {
457
+ * common: { confirm: '確認', cancel: 'キャンセル' },
458
+ * button: { submit: '送信' }
459
+ * })
460
+ *
461
+ * // 方式二:传入语言包目录路径
462
+ * i18nManager.registerLocale('ja-JP', '@/locales/ja-JP')
463
+ */
464
+ registerLocale(locale: string, messagesOrPath: ModularLocaleMessages | string): void {
465
+ this.registerMessages(locale, messagesOrPath)
466
+ }
467
+
468
+ /**
469
+ * 注册新语言(异步方法)
470
+ * 支持传入语言包对象或语言包目录路径
471
+ * @param locale 语言代码(例如:'ja-JP')
472
+ * @param messagesOrPath 语言包对象或语言包目录路径
473
+ *
474
+ * @example
475
+ * // 方式一:传入语言包对象
476
+ * await i18nManager.registerLocaleAsync('ja-JP', {
477
+ * common: { confirm: '確認', cancel: 'キャンセル' },
478
+ * button: { submit: '送信' }
479
+ * })
480
+ *
481
+ * // 方式二:传入语言包目录路径(推荐使用异步版本)
482
+ * await i18nManager.registerLocaleAsync('ja-JP', '@/locales/ja-JP')
483
+ */
484
+ async registerLocaleAsync(locale: string, messagesOrPath: ModularLocaleMessages | string): Promise<void> {
485
+ await this.registerMessagesAsync(locale, messagesOrPath)
486
+ }
487
+
488
+ /**
489
+ * 扩展现有语言包
490
+ * 用于添加自定义翻译到已有语言
491
+ * @param locale 语言代码
492
+ * @param messages 要添加的语言包
493
+ *
494
+ * @example
495
+ * i18nManager.extendLocale('zh-CN', {
496
+ * myApp: {
497
+ * welcome: '欢迎使用我的应用',
498
+ * logout: '退出登录'
499
+ * }
500
+ * })
501
+ */
502
+ extendLocale(locale: string, messages: ModularLocaleMessages): void {
503
+ this.registerMessagesWithMode(locale, messages, 'merge')
504
+ }
505
+
506
+ /**
507
+ * 获取语言代码(别名方法)
508
+ * @returns 当前语言代码
509
+ */
510
+ getLocale(): string {
511
+ return this.currentLocale.value
512
+ }
513
+
514
+ /**
515
+ * 翻译函数(别名方法)
516
+ * @param key 翻译键
517
+ * @param params 插值参数
518
+ * @param defaultValue 默认值
519
+ * @returns 翻译后的文本
520
+ */
521
+ t(key: string, params?: TranslateParams, defaultValue?: string): string {
522
+ const result = this.translate(key, params)
523
+
524
+ // 如果翻译结果等于键本身,且提供了默认值,则返回默认值
525
+ if (result === key && defaultValue !== undefined) {
526
+ return defaultValue
527
+ }
528
+
529
+ return result
530
+ }
336
531
  }
@@ -3,8 +3,15 @@
3
3
  "cancel": "Cancel",
4
4
  "ok": "OK",
5
5
  "close": "Close",
6
+ "save": "Save",
7
+ "delete": "Delete",
8
+ "edit": "Edit",
9
+ "submit": "Submit",
10
+ "reset": "Reset",
11
+ "search": "Search",
6
12
  "loading": "Loading...",
7
13
  "noData": "No Data",
14
+ "error": "Error",
8
15
  "language": "Language",
9
16
  "languageSwitch": "Switch Language"
10
17
  }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "title": "No Data",
3
+ "noData": "No Data",
3
4
  "description": "No relevant content available",
4
5
  "actionText": "Try Refresh"
5
6
  }
@@ -3,6 +3,13 @@
3
3
  "selectPlaceholder": "Please select",
4
4
  "datePlaceholder": "Select date",
5
5
  "timePlaceholder": "Select time",
6
+ "required": "This field is required",
7
+ "invalid": "Invalid format",
8
+ "minLength": "Length must be at least {min} characters",
9
+ "maxLength": "Length must not exceed {max} characters",
10
+ "email": "Please enter a valid email address",
11
+ "phone": "Please enter a valid phone number",
12
+ "url": "Please enter a valid URL",
6
13
  "requiredError": "Please enter {label}",
7
14
  "submitButton": "Submit",
8
15
  "resetButton": "Reset",
@@ -3,8 +3,15 @@
3
3
  "cancel": "取消",
4
4
  "ok": "好的",
5
5
  "close": "关闭",
6
+ "save": "保存",
7
+ "delete": "删除",
8
+ "edit": "编辑",
9
+ "submit": "提交",
10
+ "reset": "重置",
11
+ "search": "搜索",
6
12
  "loading": "加载中...",
7
13
  "noData": "暂无数据",
14
+ "error": "出错了",
8
15
  "language": "语言",
9
16
  "languageSwitch": "切换语言"
10
17
  }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "title": "暂无数据",
3
+ "noData": "暂无数据",
3
4
  "description": "当前没有相关内容",
4
5
  "actionText": "刷新试试"
5
6
  }
@@ -3,6 +3,13 @@
3
3
  "selectPlaceholder": "请选择",
4
4
  "datePlaceholder": "请选择日期",
5
5
  "timePlaceholder": "请选择时间",
6
+ "required": "此项为必填项",
7
+ "invalid": "格式不正确",
8
+ "minLength": "长度不能少于{min}个字符",
9
+ "maxLength": "长度不能超过{max}个字符",
10
+ "email": "请输入有效的邮箱地址",
11
+ "phone": "请输入有效的手机号码",
12
+ "url": "请输入有效的网址",
6
13
  "requiredError": "请输入{label}",
7
14
  "submitButton": "提交",
8
15
  "resetButton": "重置",
@@ -3,6 +3,13 @@
3
3
  "cancel": "取消",
4
4
  "ok": "好的",
5
5
  "close": "關閉",
6
+ "save": "儲存",
7
+ "delete": "刪除",
8
+ "edit": "編輯",
9
+ "submit": "提交",
10
+ "reset": "重置",
11
+ "search": "搜尋",
6
12
  "loading": "載入中...",
7
- "noData": "暫無資料"
13
+ "noData": "暫無資料",
14
+ "error": "出錯了"
8
15
  }
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "title": "暫無資料",
3
+ "noData": "暫無資料",
3
4
  "description": "目前沒有相關內容",
4
5
  "actionText": "重新整理"
5
6
  }
@@ -3,6 +3,13 @@
3
3
  "selectPlaceholder": "請選擇",
4
4
  "datePlaceholder": "請選擇日期",
5
5
  "timePlaceholder": "請選擇時間",
6
+ "required": "此項為必填項",
7
+ "invalid": "格式不正確",
8
+ "minLength": "長度不能少於{min}個字符",
9
+ "maxLength": "長度不能超過{max}個字符",
10
+ "email": "請輸入有效的郵箱地址",
11
+ "phone": "請輸入有效的手機號碼",
12
+ "url": "請輸入有效的網址",
6
13
  "requiredError": "請輸入{label}",
7
14
  "submitButton": "提交",
8
15
  "resetButton": "重設",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tang-ui-x",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "UniApp X UI 组件库 - 基于 uni-app x 的移动端 UI 组件库",
5
5
  "main": "index.uts",
6
6
  "module": "index.uts",
@@ -47,4 +47,4 @@
47
47
  "build": "echo 'Build completed'",
48
48
  "version": "pnpm run build"
49
49
  }
50
- }
50
+ }