@aquiferre/ui-kit 0.4.4 → 0.5.0

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.
@@ -5,7 +5,7 @@
5
5
  <div class="fixed inset-0 bg-bg-overlay" :data-testid="testId ? `${testId}-mask` : undefined" @click="cancel" />
6
6
  <div class="panel-glass dialog max-w-sm">
7
7
  <div class="px-6 py-4">
8
- <h3 class="dialog-title">{{ title }}</h3>
8
+ <h3 class="dialog-title">{{ titleText }}</h3>
9
9
  <p v-if="content" class="text-text-secondary text-sm mt-2">{{ content }}</p>
10
10
  </div>
11
11
  <div class="dialog-actions px-6 py-4 border-t border-border-primary bg-bg-secondary/50 rounded-b-2xl">
@@ -23,7 +23,10 @@
23
23
  </template>
24
24
 
25
25
  <script setup lang="ts">
26
- withDefaults(defineProps<{
26
+ import { computed } from 'vue'
27
+ import { useI18n } from 'vue-i18n'
28
+
29
+ const props = withDefaults(defineProps<{
27
30
  visible: boolean
28
31
  title?: string
29
32
  content?: string
@@ -35,14 +38,16 @@ withDefaults(defineProps<{
35
38
  confirmClass?: string
36
39
  testId?: string
37
40
  }>(), {
38
- title: '确认删除',
39
- confirmText: '确认删除',
40
- cancelText: '取消',
41
41
  showCancel: true,
42
- loadingText: '删除中...',
43
42
  confirmClass: 'btn-danger',
44
43
  })
45
44
 
45
+ const { t } = useI18n()
46
+ const titleText = computed(() => props.title ?? t('ui.confirmDialog.title'))
47
+ const confirmText = computed(() => props.confirmText ?? t('ui.confirmDialog.confirmText'))
48
+ const cancelText = computed(() => props.cancelText ?? t('ui.common.cancel'))
49
+ const loadingText = computed(() => props.loadingText ?? t('ui.confirmDialog.loadingText'))
50
+
46
51
  const emit = defineEmits<{
47
52
  confirm: []
48
53
  cancel: []
@@ -30,7 +30,7 @@
30
30
  <tbody class="table-body">
31
31
  <tr v-if="!loading && data.length === 0" class="table-row">
32
32
  <td :colspan="colspan" class="table-empty">
33
- {{ emptyText }}
33
+ {{ emptyTextText }}
34
34
  </td>
35
35
  </tr>
36
36
  <tr
@@ -61,7 +61,7 @@
61
61
  <div v-if="loading" class="absolute inset-0 flex items-center justify-center bg-bg-elevated/60 backdrop-blur-[1px] z-10">
62
62
  <div class="flex items-center space-x-2 text-text-secondary">
63
63
  <div class="spinner"></div>
64
- <span class="text-sm">加载中...</span>
64
+ <span class="text-sm">{{ loadingText }}</span>
65
65
  </div>
66
66
  </div>
67
67
  </div>
@@ -69,8 +69,11 @@
69
69
 
70
70
  <script setup lang="ts">
71
71
  import { computed, useAttrs } from 'vue'
72
+ import { useI18n } from 'vue-i18n'
72
73
  import type { Column } from '../types'
73
74
 
75
+ const { t } = useI18n()
76
+
74
77
  const props = withDefaults(defineProps<{
75
78
  columns: Column[]
76
79
  data: any[]
@@ -85,7 +88,6 @@ const props = withDefaults(defineProps<{
85
88
  scrollable?: boolean
86
89
  }>(), {
87
90
  loading: false,
88
- emptyText: '暂无数据',
89
91
  selectable: false,
90
92
  selected: () => [],
91
93
  rowKey: 'id',
@@ -94,6 +96,9 @@ const props = withDefaults(defineProps<{
94
96
  scrollable: false,
95
97
  })
96
98
 
99
+ const loadingText = computed(() => t('ui.dataTable.loading'))
100
+ const emptyTextText = computed(() => props.emptyText ?? t('ui.dataTable.empty'))
101
+
97
102
  const attrs = useAttrs()
98
103
  const baseTestId = computed<string | undefined>(() => props.testId ?? (attrs['data-testid'] as string | undefined))
99
104
  const rowPrefix = computed<string | undefined>(() => props.rowTestIdPrefix ?? baseTestId.value)
@@ -37,7 +37,7 @@
37
37
  <div class="flex items-center justify-between mb-2">
38
38
  <div class="relative">
39
39
  <button type="button" class="btn-sm btn-ghost text-xs font-semibold" @click="yearDropdownOpen = !yearDropdownOpen">
40
- {{ viewYear }} &#x25BE;
40
+ {{ t('ui.dateTimePicker.year', { year: viewYear }) }} &#x25BE;
41
41
  </button>
42
42
  <div
43
43
  v-if="yearDropdownOpen"
@@ -61,12 +61,12 @@
61
61
  </div>
62
62
  <div class="flex items-center gap-1">
63
63
  <button type="button" class="btn-icon text-text-secondary hover:text-text-primary" @click="prevMonth">&#x25C0;</button>
64
- <span class="text-xs font-semibold text-text-primary min-w-[3rem] text-center">{{ viewMonth }}月</span>
64
+ <span class="text-xs font-semibold text-text-primary min-w-[3rem] text-center">{{ t('ui.dateTimePicker.month', { month: viewMonth }) }}</span>
65
65
  <button type="button" class="btn-icon text-text-secondary hover:text-text-primary" @click="nextMonth">&#x25B6;</button>
66
66
  </div>
67
67
  </div>
68
68
  <div class="grid grid-cols-7 gap-0.5 mb-1">
69
- <div v-for="d in weekDays" :key="d" class="text-center text-xs text-text-tertiary py-0.5 font-medium">{{ d }}</div>
69
+ <div v-for="d in weekdayKeys" :key="d" class="text-center text-xs text-text-tertiary py-0.5 font-medium">{{ t('ui.dateTimePicker.week.' + d) }}</div>
70
70
  </div>
71
71
  <div class="grid grid-cols-7 gap-0.5">
72
72
  <button
@@ -98,20 +98,20 @@
98
98
  </div>
99
99
  <div class="w-px bg-border-primary" />
100
100
  <div>
101
- <div class="mb-1.5 px-1"><span class="text-xs text-text-secondary">月份</span></div>
101
+ <div class="mb-1.5 px-1"><span class="text-xs text-text-secondary">{{ t('ui.dateTimePicker.monthSection') }}</span></div>
102
102
  <div class="grid grid-cols-2 gap-1">
103
103
  <button
104
104
  v-for="m in 12" :key="m" type="button"
105
105
  class="px-2 py-1 text-xs rounded-md transition-colors min-w-[2.5rem]"
106
106
  :class="m === viewMonth ? 'bg-accent-primary text-white' : 'bg-bg-secondary text-text-primary hover:bg-bg-hover'"
107
107
  @click="selectMonth(m)"
108
- >{{ m }}月</button>
108
+ >{{ t('ui.dateTimePicker.month', { month: m }) }}</button>
109
109
  </div>
110
110
  </div>
111
111
  <div class="w-px bg-border-primary" />
112
112
  <div>
113
113
  <div class="grid grid-cols-7 gap-0.5 mb-1">
114
- <div v-for="d in weekDays" :key="d" class="text-center text-xs text-text-tertiary py-0.5 font-medium">{{ d }}</div>
114
+ <div v-for="d in weekdayKeys" :key="d" class="text-center text-xs text-text-tertiary py-0.5 font-medium">{{ t('ui.dateTimePicker.week.' + d) }}</div>
115
115
  </div>
116
116
  <div class="grid grid-cols-7 gap-0.5">
117
117
  <button
@@ -145,7 +145,7 @@
145
145
 
146
146
  <!-- Confirm button -->
147
147
  <div v-if="mode !== 'date'" class="flex justify-end mt-3 pt-2 border-t border-border-primary">
148
- <button type="button" class="btn-sm btn-primary" @click="confirm">确认</button>
148
+ <button type="button" class="btn-sm btn-primary" @click="confirm">{{ t('ui.common.confirm') }}</button>
149
149
  </div>
150
150
  </div>
151
151
  </Teleport>
@@ -154,6 +154,7 @@
154
154
 
155
155
  <script setup lang="ts">
156
156
  import { ref, computed, watch, onMounted, onUnmounted, nextTick, useAttrs } from 'vue'
157
+ import { useI18n } from 'vue-i18n'
157
158
  import type { DateTimePreset } from '../types'
158
159
 
159
160
  const props = withDefaults(defineProps<{
@@ -180,6 +181,8 @@ const props = withDefaults(defineProps<{
180
181
  clearable: true,
181
182
  })
182
183
 
184
+ const { t } = useI18n()
185
+
183
186
  const emit = defineEmits<{
184
187
  'update:modelValue': [value: string | [string, string]]
185
188
  'change': [value: string | [string, string]]
@@ -210,7 +213,8 @@ const minutes = ref(0)
210
213
  const seconds = ref(0)
211
214
  const editingTime = ref<'h' | 'm' | 's' | null>(null)
212
215
 
213
- const weekDays = ['日', '一', '二', '三', '四', '五', '六']
216
+ // 星期表头 key 数组(周日~周六),文案在模板中按词典渲染
217
+ const weekdayKeys = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
214
218
 
215
219
  const decadeYears = computed(() => Array.from({ length: 10 }, (_, i) => decadeStart.value + i))
216
220
 
@@ -11,7 +11,7 @@
11
11
  />
12
12
 
13
13
  <button @click="triggerFileInput" class="btn-primary btn-sm inline-flex items-center gap-1" :data-testid="baseTestId ? `${baseTestId}-trigger` : undefined">
14
- 📎 选择文件
14
+ 📎 {{ t('ui.fileUploader.select') }}
15
15
  </button>
16
16
 
17
17
  <div v-if="uploadQueue.length > 0" class="mt-3 space-y-2">
@@ -31,9 +31,9 @@
31
31
 
32
32
  <div class="text-xs mt-1">
33
33
  <span v-if="item.status === 'uploading'" class="text-accent-primary">
34
- 上传中 {{ item.progress.toFixed(2) }}%
34
+ {{ t('ui.fileUploader.uploading', { progress: item.progress.toFixed(2) }) }}
35
35
  </span>
36
- <span v-else-if="item.status === 'success'" class="text-accent-success">✓ 完成</span>
36
+ <span v-else-if="item.status === 'success'" class="text-accent-success">✓ {{ t('ui.fileUploader.done') }}</span>
37
37
  <span v-else-if="item.status === 'error'" class="text-accent-danger">✗ {{ item.error }}</span>
38
38
  </div>
39
39
  </div>
@@ -43,6 +43,7 @@
43
43
 
44
44
  <script setup lang="ts">
45
45
  import { ref, reactive, computed, useAttrs } from 'vue'
46
+ import { useI18n } from 'vue-i18n'
46
47
 
47
48
  interface UploadItem {
48
49
  id: string
@@ -61,6 +62,8 @@ const props = withDefaults(defineProps<{
61
62
  acceptTypes: 'image/*,video/*'
62
63
  })
63
64
 
65
+ const { t } = useI18n()
66
+
64
67
  const attrs = useAttrs()
65
68
  const baseTestId = computed<string | undefined>(() => props.testId ?? (attrs['data-testid'] as string | undefined))
66
69
 
@@ -113,7 +116,7 @@ async function handleFileSelect(event: Event) {
113
116
  }, 1500)
114
117
  } catch (error: any) {
115
118
  item.status = 'error'
116
- item.error = error.message || '上传失败'
119
+ item.error = error.message || t('ui.fileUploader.failed')
117
120
  }
118
121
  }
119
122
 
@@ -5,7 +5,7 @@
5
5
  :data-testid="baseTestId"
6
6
  @change="$emit('update:modelValue', ($event.target as HTMLSelectElement).value)"
7
7
  >
8
- <option v-if="showAll" value="">{{ placeholder || '全部' }}</option>
8
+ <option v-if="showAll" value="">{{ allText }}</option>
9
9
  <option v-for="opt in options" :key="opt.value" :value="opt.value" :data-testid="baseTestId ? `${baseTestId}-option-${opt.value}` : undefined">
10
10
  {{ opt.label }}
11
11
  </option>
@@ -14,6 +14,7 @@
14
14
 
15
15
  <script setup lang="ts">
16
16
  import { computed, useAttrs } from 'vue'
17
+ import { useI18n } from 'vue-i18n'
17
18
 
18
19
  const props = withDefaults(defineProps<{
19
20
  modelValue: string
@@ -25,6 +26,10 @@ const props = withDefaults(defineProps<{
25
26
  showAll: true
26
27
  })
27
28
 
29
+ const { t } = useI18n()
30
+ // placeholder 显式传入时优先(既有调用约定),缺省走词典
31
+ const allText = computed(() => props.placeholder ?? t('ui.filterDropdown.all'))
32
+
28
33
  defineEmits<{
29
34
  'update:modelValue': [value: string]
30
35
  }>()
@@ -17,7 +17,7 @@
17
17
  :data-testid="testId ? `${testId}-cancel` : undefined"
18
18
  @click="cancel"
19
19
  >
20
- 取消
20
+ {{ cancelText }}
21
21
  </button>
22
22
  <button
23
23
  :disabled="loading"
@@ -25,7 +25,7 @@
25
25
  :data-testid="testId ? `${testId}-submit` : 'submit-btn'"
26
26
  @click="confirm"
27
27
  >
28
- {{ loading ? '提交中...' : (confirmText || '确认') }}
28
+ {{ loading ? submittingText : confirmTextText }}
29
29
  </button>
30
30
  </div>
31
31
  </div>
@@ -35,7 +35,10 @@
35
35
  </template>
36
36
 
37
37
  <script setup lang="ts">
38
- defineProps<{
38
+ import { computed } from 'vue'
39
+ import { useI18n } from 'vue-i18n'
40
+
41
+ const props = defineProps<{
39
42
  visible: boolean
40
43
  title: string
41
44
  loading?: boolean
@@ -44,6 +47,11 @@ defineProps<{
44
47
  testId?: string
45
48
  }>()
46
49
 
50
+ const { t } = useI18n()
51
+ const cancelText = computed(() => t('ui.common.cancel'))
52
+ const submittingText = computed(() => t('ui.common.submitting'))
53
+ const confirmTextText = computed(() => props.confirmText ?? t('ui.formDialog.confirmText'))
54
+
47
55
  const emit = defineEmits<{
48
56
  confirm: []
49
57
  cancel: []
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="flex items-center justify-between px-6 py-2" :data-testid="baseTestId">
3
3
  <div class="text-xs text-text-tertiary">
4
- {{ total }} 条,第 {{ startItem }} - {{ endItem }} 条
4
+ {{ totalText }}
5
5
  </div>
6
6
  <div class="flex items-center space-x-1">
7
7
  <button
@@ -10,7 +10,7 @@
10
10
  :data-testid="baseTestId ? `${baseTestId}-prev` : undefined"
11
11
  @click="changePage(page - 1)"
12
12
  >
13
- 上一页
13
+ {{ prevText }}
14
14
  </button>
15
15
  <template v-for="p in visiblePages" :key="p">
16
16
  <span v-if="p === '...'" class="btn-glass btn-sm pointer-events-none">…</span>
@@ -29,17 +29,15 @@
29
29
  :data-testid="baseTestId ? `${baseTestId}-next` : undefined"
30
30
  @click="changePage(page + 1)"
31
31
  >
32
- 下一页
32
+ {{ nextText }}
33
33
  </button>
34
34
  <select
35
- :value="pageSize"
35
+ :value="selectedPageSize"
36
36
  class="select ml-2"
37
37
  :data-testid="baseTestId ? `${baseTestId}-size-select` : undefined"
38
38
  @change="changePageSize(Number(($event.target as HTMLSelectElement).value))"
39
39
  >
40
- <option :value="10">10条/页</option>
41
- <option :value="20">20条/页</option>
42
- <option :value="50">50条/页</option>
40
+ <option v-for="size in sizeOptions" :key="size" :value="size">{{ pageSizeText(size) }}</option>
43
41
  </select>
44
42
  </div>
45
43
  </div>
@@ -48,13 +46,22 @@
48
46
  <script setup lang="ts">
49
47
  // @ts-nocheck
50
48
  import { computed, useAttrs } from 'vue'
49
+ import { useI18n } from 'vue-i18n'
51
50
 
52
- const props = defineProps<{
51
+ // 默认页容量选项(自定义值通过 pageSizeOptions 排在其前)
52
+ const DEFAULT_PAGE_SIZES = [20, 50, 100, 200, 500, 1000, 5000]
53
+
54
+ const { t } = useI18n()
55
+
56
+ const props = withDefaults(defineProps<{
53
57
  page: number
54
58
  pageSize: number
55
59
  total: number
60
+ pageSizeOptions?: number[]
56
61
  testId?: string
57
- }>()
62
+ }>(), {
63
+ pageSizeOptions: () => [],
64
+ })
58
65
 
59
66
  const attrs = useAttrs()
60
67
  const baseTestId = computed<string | undefined>(() => props.testId ?? (attrs['data-testid'] as string | undefined))
@@ -65,11 +72,33 @@ const emit = defineEmits<{
65
72
  change: [page: number, pageSize: number]
66
73
  }>()
67
74
 
75
+ const sizeOptions = computed(() => {
76
+ const custom = (props.pageSizeOptions || []).filter(size => Number.isInteger(size) && size > 0)
77
+ const merged = [...custom]
78
+ for (const size of DEFAULT_PAGE_SIZES) {
79
+ if (!merged.includes(size)) merged.push(size)
80
+ }
81
+ return merged
82
+ })
83
+
84
+ const selectedPageSize = computed(() =>
85
+ sizeOptions.value.includes(props.pageSize) ? props.pageSize : sizeOptions.value[0]
86
+ )
87
+
68
88
  const totalPages = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)))
69
89
 
70
90
  const startItem = computed(() => (props.page - 1) * props.pageSize + 1)
71
91
  const endItem = computed(() => Math.min(props.page * props.pageSize, props.total))
72
92
 
93
+ const totalText = computed(() =>
94
+ t('ui.pagination.total', { total: props.total, startItem: startItem.value, endItem: endItem.value })
95
+ )
96
+ const prevText = computed(() => t('ui.pagination.prev'))
97
+ const nextText = computed(() => t('ui.pagination.next'))
98
+ function pageSizeText(size: number) {
99
+ return t('ui.pagination.pageSize', { size })
100
+ }
101
+
73
102
  /**
74
103
  * 生成可见页码数组,当前页 ±2 范围内显示,其余用 '...' 省略
75
104
  * 始终显示首页和末页
@@ -7,7 +7,7 @@
7
7
  <input
8
8
  ref="inputRef"
9
9
  :value="inputValue"
10
- :placeholder="displayLabel || placeholder"
10
+ :placeholder="displayLabel || placeholderText"
11
11
  :disabled="disabled"
12
12
  class="flex-1 min-w-0 bg-transparent border-none outline-none text-input-text placeholder:text-input-placeholder"
13
13
  :data-testid="baseTestId ? `${baseTestId}-input` : undefined"
@@ -35,7 +35,7 @@
35
35
  v-if="open"
36
36
  class="absolute top-full left-0 mt-1 w-full max-h-60 overflow-auto card shadow-lg z-20 py-1"
37
37
  >
38
- <div v-if="filtered.length === 0" class="px-4 py-2 text-sm text-text-tertiary">无匹配项</div>
38
+ <div v-if="filtered.length === 0" class="px-4 py-2 text-sm text-text-tertiary">{{ noMatchText }}</div>
39
39
  <div
40
40
  v-for="opt in filtered"
41
41
  :key="opt.value"
@@ -52,6 +52,7 @@
52
52
 
53
53
  <script setup lang="ts">
54
54
  import { ref, computed, onMounted, onUnmounted, watch, useAttrs } from 'vue'
55
+ import { useI18n } from 'vue-i18n'
55
56
  import { match } from 'pinyin-pro'
56
57
  import type { SelectOption } from '../types'
57
58
 
@@ -64,12 +65,16 @@ const props = withDefaults(defineProps<{
64
65
  optionTestIdPrefix?: string
65
66
  testId?: string
66
67
  }>(), {
67
- placeholder: '请选择',
68
68
  clearable: true,
69
69
  disabled: false,
70
70
  optionTestIdPrefix: '',
71
71
  })
72
72
 
73
+ const { t } = useI18n()
74
+ // 占位/空态兜底文案:显式传入优先,缺省走词典
75
+ const placeholderText = computed(() => props.placeholder ?? t('ui.searchSelect.placeholder'))
76
+ const noMatchText = computed(() => t('ui.common.noMatch'))
77
+
73
78
  const attrs = useAttrs()
74
79
  const baseTestId = computed<string | undefined>(() => props.testId ?? (attrs['data-testid'] as string | undefined))
75
80
  const optionPrefix = computed<string | undefined>(() => props.optionTestIdPrefix || baseTestId.value)
@@ -4,7 +4,7 @@
4
4
  <button
5
5
  v-if="collapsible"
6
6
  class="sidebar-collapse-btn text-xs w-5 h-5 flex items-center justify-center rounded hover:bg-white/10 transition-colors"
7
- :title="collapsed ? '展开菜单' : '收起菜单'"
7
+ :title="collapsed ? expandText : collapseText"
8
8
  @click="collapsed = !collapsed"
9
9
  >{{ collapsed ? '▶' : '◀' }}</button>
10
10
  </div>
@@ -15,10 +15,10 @@
15
15
  class="sidebar-item text-xs"
16
16
  exact-active-class="sidebar-item-active"
17
17
  :data-testid="baseTestId ? `${baseTestId}-item-home` : undefined"
18
- :title="collapsed ? '首页' : undefined"
18
+ :title="collapsed ? homeText : undefined"
19
19
  >
20
20
  <span class="w-5 text-center text-sm">🏠</span>
21
- <span v-if="!collapsed" class="ml-2">首页</span>
21
+ <span v-if="!collapsed" class="ml-2">{{ homeText }}</span>
22
22
  </router-link>
23
23
 
24
24
  <template v-for="group in menuGroups" :key="group.key">
@@ -42,6 +42,7 @@
42
42
 
43
43
  <script setup lang="ts">
44
44
  import { computed, ref, useAttrs } from 'vue'
45
+ import { useI18n } from 'vue-i18n'
45
46
  import type { MenuGroup } from '../types'
46
47
 
47
48
  const props = withDefaults(defineProps<{
@@ -51,6 +52,11 @@ const props = withDefaults(defineProps<{
51
52
  showHome?: boolean
52
53
  }>(), { collapsible: false, showHome: true })
53
54
 
55
+ const { t } = useI18n()
56
+ const expandText = computed(() => t('ui.sideMenu.expand'))
57
+ const collapseText = computed(() => t('ui.sideMenu.collapse'))
58
+ const homeText = computed(() => t('ui.sideMenu.home'))
59
+
54
60
  const collapsed = ref(false)
55
61
 
56
62
  const attrs = useAttrs()
@@ -6,6 +6,7 @@
6
6
 
7
7
  <script setup lang="ts">
8
8
  import { computed, useAttrs } from 'vue'
9
+ import { useI18n } from 'vue-i18n'
9
10
 
10
11
  const props = withDefaults(defineProps<{
11
12
  status: string | number
@@ -15,22 +16,31 @@ const props = withDefaults(defineProps<{
15
16
  statusMap: () => ({})
16
17
  })
17
18
 
19
+ const { t } = useI18n()
18
20
  const attrs = useAttrs()
19
21
  const baseTestId = computed<string | undefined>(() => props.testId ?? (attrs['data-testid'] as string | undefined))
20
22
 
21
- const defaultMap: Record<string, { label: string; cls: string }> = {
22
- active: { label: '正常', cls: 'badge-success' },
23
- locked: { label: '已锁定', cls: 'badge-danger' },
24
- deleted: { label: '已删除', cls: 'badge-danger' },
25
- '0': { label: '已停用', cls: 'badge-warning' },
26
- '1': { label: '正常', cls: 'badge-success' },
23
+ // 内置状态文案走词典(key 前缀 ui.status.);调用方显式传入 statusMap 时优先
24
+ const defaultMap: Record<string, { labelKey: string; cls: string }> = {
25
+ active: { labelKey: 'ui.status.active', cls: 'badge-success' },
26
+ locked: { labelKey: 'ui.status.locked', cls: 'badge-danger' },
27
+ deleted: { labelKey: 'ui.status.deleted', cls: 'badge-danger' },
28
+ '0': { labelKey: 'ui.status.disabled', cls: 'badge-warning' },
29
+ '1': { labelKey: 'ui.status.active', cls: 'badge-success' },
27
30
  }
28
31
 
29
- const entry = computed(() => {
32
+ const label = computed(() => {
30
33
  const key = String(props.status)
31
- return props.statusMap[key] || props.statusMap[props.status] || defaultMap[key] || { label: key, cls: 'badge-danger' }
34
+ const custom = props.statusMap[key] || props.statusMap[props.status]
35
+ if (custom) return custom.label
36
+ const def = defaultMap[key]
37
+ return def ? t(def.labelKey) : key
32
38
  })
33
39
 
34
- const label = computed(() => entry.value.label)
35
- const cls = computed(() => entry.value.cls)
40
+ const cls = computed(() => {
41
+ const key = String(props.status)
42
+ const custom = props.statusMap[key] || props.statusMap[props.status]
43
+ if (custom) return custom.cls
44
+ return (defaultMap[key] || { cls: 'badge-danger' }).cls
45
+ })
36
46
  </script>
@@ -4,7 +4,7 @@
4
4
  <input
5
5
  ref="inputRef"
6
6
  :value="modelValue"
7
- :placeholder="placeholder"
7
+ :placeholder="placeholderText"
8
8
  :disabled="disabled"
9
9
  class="flex-1 min-w-0 bg-transparent border-none outline-none text-input-text placeholder:text-input-placeholder"
10
10
  :data-testid="baseTestId ? `${baseTestId}-input` : undefined"
@@ -29,7 +29,7 @@
29
29
  :data-testid="baseTestId ? `${baseTestId}-load` : undefined"
30
30
  @click.stop="onLoadBtnClick"
31
31
  tabindex="-1"
32
- >{{ loading ? '获取中...' : loadBtnText }}</button>
32
+ >{{ loading ? loadingText : loadBtnTextText }}</button>
33
33
  <!-- 无 loadBtn 时提供展开按钮(配合外部 options 模式) -->
34
34
  <button
35
35
  v-if="!loadBtn && loading"
@@ -54,14 +54,14 @@
54
54
  :data-testid="baseTestId ? `${baseTestId}-dropdown` : undefined"
55
55
  >
56
56
  <!-- 初始加载中 -->
57
- <div v-if="initialLoading" class="px-4 py-2 text-sm text-text-tertiary">获取中...</div>
57
+ <div v-if="initialLoading" class="px-4 py-2 text-sm text-text-tertiary">{{ loadingText }}</div>
58
58
  <!-- 加载失败 -->
59
59
  <div v-else-if="loadError" class="px-4 py-2">
60
60
  <span class="text-sm text-accent-danger">{{ loadError }}</span>
61
- <button class="ml-2 text-xs text-accent-primary hover:text-accent-primary-hover" @click="retryLoad">重试</button>
61
+ <button class="ml-2 text-xs text-accent-primary hover:text-accent-primary-hover" @click="retryLoad">{{ retryText }}</button>
62
62
  </div>
63
63
  <!-- 无选项 -->
64
- <div v-else-if="filtered.length === 0" class="px-4 py-2 text-sm text-text-tertiary">无匹配项</div>
64
+ <div v-else-if="filtered.length === 0" class="px-4 py-2 text-sm text-text-tertiary">{{ noMatchText }}</div>
65
65
  <!-- 选项列表 -->
66
66
  <div
67
67
  v-for="opt in filtered"
@@ -79,6 +79,7 @@
79
79
 
80
80
  <script setup lang="ts">
81
81
  import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
82
+ import { useI18n } from 'vue-i18n'
82
83
  import { match } from 'pinyin-pro'
83
84
  import type { SelectOption } from '../types'
84
85
 
@@ -94,13 +95,19 @@ const props = withDefaults(defineProps<{
94
95
  loadBtnText?: string
95
96
  }>(), {
96
97
  options: () => [],
97
- placeholder: '请输入或选择',
98
98
  disabled: false,
99
99
  clearable: true,
100
100
  loadBtn: false,
101
- loadBtnText: '获取',
102
101
  })
103
102
 
103
+ const { t } = useI18n()
104
+ // 兜底文案:props 显式传入优先,缺省走词典
105
+ const placeholderText = computed(() => props.placeholder ?? t('ui.suggestInput.placeholder'))
106
+ const loadBtnTextText = computed(() => props.loadBtnText ?? t('ui.suggestInput.loadBtnText'))
107
+ const loadingText = computed(() => t('ui.suggestInput.loading'))
108
+ const retryText = computed(() => t('ui.common.retry'))
109
+ const noMatchText = computed(() => t('ui.common.noMatch'))
110
+
104
111
  const emit = defineEmits<{
105
112
  'update:modelValue': [value: string]
106
113
  }>()
@@ -145,7 +152,7 @@ async function loadAsync() {
145
152
  const result = await props.loadOptions()
146
153
  innerOptions.value = result
147
154
  } catch (err: any) {
148
- loadError.value = err?.message || '获取失败'
155
+ loadError.value = err?.message || t('ui.common.fetchFailed')
149
156
  } finally {
150
157
  initialLoading.value = false
151
158
  loading.value = false
@@ -7,7 +7,7 @@
7
7
  <path d="M12 2L2 7l10 5 10-5-10-5z"/>
8
8
  </svg>
9
9
  </div>
10
- <span class="navbar-brand">{{ title }}</span>
10
+ <span class="navbar-brand">{{ displayTitle }}</span>
11
11
  </slot>
12
12
  <span v-if="tenantLabel" class="text-xs text-text-tertiary">| {{ tenantLabel }}</span>
13
13
 
@@ -28,11 +28,39 @@
28
28
  </div>
29
29
 
30
30
  <div class="navbar-user">
31
+ <!-- 语言切换 -->
32
+ <div v-if="showLocaleSwitch" class="relative" ref="localeDropdownRef">
33
+ <button
34
+ @click="showLocaleDropdown = !showLocaleDropdown"
35
+ :title="languageTitle"
36
+ class="btn-icon text-text-secondary hover:text-text-primary"
37
+ :data-testid="testId ? `${testId}-locale-toggle` : undefined"
38
+ >
39
+ 🌐
40
+ </button>
41
+
42
+ <Transition name="dropdown">
43
+ <div v-if="showLocaleDropdown" class="absolute right-0 mt-2 w-40 card shadow-lg z-50 py-1">
44
+ <button
45
+ v-for="opt in SUPPORTED_LOCALES"
46
+ :key="opt.code"
47
+ class="w-full px-4 py-2 text-left text-sm flex items-center gap-2 hover:bg-bg-tertiary"
48
+ :class="localeStore.locale === opt.code ? 'text-accent-primary' : 'text-text-secondary'"
49
+ :data-testid="testId ? `${testId}-locale-${opt.code}` : undefined"
50
+ @click="selectLocale(opt.code)"
51
+ >
52
+ <span>{{ opt.code === localeStore.locale ? '✓' : '' }}</span>
53
+ <span>{{ opt.label }}</span>
54
+ </button>
55
+ </div>
56
+ </Transition>
57
+ </div>
58
+
31
59
  <!-- 主题切换 -->
32
60
  <div class="relative" ref="themeDropdownRef">
33
- <button
61
+ <button
34
62
  @click="showThemeDropdown = !showThemeDropdown"
35
- :title="`当前主题: ${themeStore.currentThemeInfo?.label}`"
63
+ :title="themeTooltip"
36
64
  class="btn-icon text-text-secondary hover:text-text-primary"
37
65
  :data-testid="testId ? `${testId}-theme-toggle` : undefined"
38
66
  >
@@ -42,15 +70,15 @@
42
70
  <Transition name="dropdown">
43
71
  <div v-if="showThemeDropdown" class="absolute right-0 mt-2 w-40 card shadow-lg z-50 py-1">
44
72
  <button
45
- v-for="t in themeStore.themeList"
46
- :key="t.mode"
73
+ v-for="opt in themeStore.themeList"
74
+ :key="opt.mode"
47
75
  class="w-full px-4 py-2 text-left text-sm flex items-center gap-2 hover:bg-bg-tertiary"
48
- :class="themeStore.theme === t.mode ? 'text-accent-primary' : 'text-text-secondary'"
49
- :data-testid="testId ? `${testId}-theme-${t.mode}` : undefined"
50
- @click="selectTheme(t.mode)"
76
+ :class="themeStore.theme === opt.mode ? 'text-accent-primary' : 'text-text-secondary'"
77
+ :data-testid="testId ? `${testId}-theme-${opt.mode}` : undefined"
78
+ @click="selectTheme(opt.mode)"
51
79
  >
52
- <span>{{ t.icon }}</span>
53
- <span>{{ t.label }}</span>
80
+ <span>{{ opt.icon }}</span>
81
+ <span>{{ t(opt.labelKey) }}</span>
54
82
  </button>
55
83
  </div>
56
84
  </Transition>
@@ -100,7 +128,9 @@
100
128
 
101
129
  <script setup lang="ts">
102
130
  import { computed, ref, onMounted, onUnmounted } from 'vue'
103
- import { useThemeStore } from '@aquiferre/theme-kit'
131
+ import { useI18n } from 'vue-i18n'
132
+ import { useThemeStore, useLocaleStore, SUPPORTED_LOCALES } from '@aquiferre/theme-kit'
133
+ import type { LocaleCode } from '@aquiferre/theme-kit'
104
134
  import type { NavModule, DropdownItem } from '../types'
105
135
 
106
136
  const props = withDefaults(defineProps<{
@@ -110,23 +140,35 @@ const props = withDefaults(defineProps<{
110
140
  modules?: NavModule[]
111
141
  activeModule?: string
112
142
  userDropdownItems?: DropdownItem[]
143
+ showLocaleSwitch?: boolean
113
144
  testId?: string
114
145
  }>(), {
115
- title: '医疗设备智能平台',
116
146
  avatarText: 'U',
117
147
  tenantLabel: '',
118
148
  modules: () => [],
119
149
  activeModule: '',
120
- userDropdownItems: () => []
150
+ userDropdownItems: () => [],
151
+ showLocaleSwitch: true,
121
152
  })
122
153
 
154
+ const { t } = useI18n()
123
155
  const themeStore = useThemeStore()
156
+ const localeStore = useLocaleStore()
124
157
  const showUserDropdown = ref(false)
125
158
  const showThemeDropdown = ref(false)
159
+ const showLocaleDropdown = ref(false)
126
160
  const userDropdownRef = ref<HTMLElement | null>(null)
127
161
  const themeDropdownRef = ref<HTMLElement | null>(null)
162
+ const localeDropdownRef = ref<HTMLElement | null>(null)
128
163
 
164
+ const displayTitle = computed(() => props.title ?? t('ui.navbar.title'))
129
165
  const themeIcon = computed(() => themeStore.currentThemeInfo?.icon || '🎨')
166
+ const themeTooltip = computed(() =>
167
+ t('ui.navbar.currentTheme', {
168
+ theme: themeStore.currentThemeInfo ? t(themeStore.currentThemeInfo.labelKey) : '',
169
+ })
170
+ )
171
+ const languageTitle = computed(() => t('ui.navbar.language'))
130
172
  const avatarInitial = computed(() => props.avatarText.charAt(0).toUpperCase())
131
173
 
132
174
  function selectTheme(mode: 'light' | 'dark' | 'neutral') {
@@ -134,6 +176,11 @@ function selectTheme(mode: 'light' | 'dark' | 'neutral') {
134
176
  showThemeDropdown.value = false
135
177
  }
136
178
 
179
+ function selectLocale(code: LocaleCode) {
180
+ localeStore.setLocale(code)
181
+ showLocaleDropdown.value = false
182
+ }
183
+
137
184
  function handleItemClick(item: DropdownItem) {
138
185
  if (item.onClick) item.onClick()
139
186
  showUserDropdown.value = false
@@ -146,6 +193,9 @@ function handleClickOutside(event: MouseEvent) {
146
193
  if (themeDropdownRef.value && !themeDropdownRef.value.contains(event.target as Node)) {
147
194
  showThemeDropdown.value = false
148
195
  }
196
+ if (localeDropdownRef.value && !localeDropdownRef.value.contains(event.target as Node)) {
197
+ showLocaleDropdown.value = false
198
+ }
149
199
  }
150
200
 
151
201
  onMounted(() => document.addEventListener('click', handleClickOutside))
@@ -5,12 +5,12 @@
5
5
  v-model="keyword"
6
6
  type="text"
7
7
  class="tree-search"
8
- :placeholder="searchPlaceholder"
8
+ :placeholder="searchPlaceholderText"
9
9
  :data-testid="testId ? `${testId}-search` : undefined"
10
10
  />
11
11
  </div>
12
12
  <div v-if="visibleRoot.length === 0" class="tree-empty">
13
- <p>{{ emptyText }}</p>
13
+ <p>{{ emptyTextText }}</p>
14
14
  </div>
15
15
  <div v-else class="tree-scroll">
16
16
  <TreeNodeItem
@@ -32,6 +32,7 @@
32
32
 
33
33
  <script setup lang="ts">
34
34
  import { ref, computed, watch } from 'vue'
35
+ import { useI18n } from 'vue-i18n'
35
36
  import TreeNodeItem from './TreeNodeItem.vue'
36
37
 
37
38
  /** 通用树节点:业务字段(type 等)可透传附加;children 由组件懒加载后写入 */
@@ -59,9 +60,14 @@ const props = withDefaults(
59
60
  emptyText?: string
60
61
  testId?: string
61
62
  }>(),
62
- { showSearch: false, searchPlaceholder: '搜索...', emptyText: '暂无数据' }
63
+ { showSearch: false }
63
64
  )
64
65
 
66
+ const { t } = useI18n()
67
+ // 搜索占位与空态文案:显式传入优先,缺省走词典
68
+ const searchPlaceholderText = computed(() => props.searchPlaceholder ?? t('ui.tree.searchPlaceholder'))
69
+ const emptyTextText = computed(() => props.emptyText ?? t('ui.common.noData'))
70
+
65
71
  const emit = defineEmits<{
66
72
  (e: 'select', node: TreeNode | null): void
67
73
  }>()
@@ -4,7 +4,7 @@
4
4
  <input
5
5
  class="input w-full"
6
6
  v-model="keyword"
7
- placeholder="搜索用户..."
7
+ :placeholder="t('ui.userPicker.searchPlaceholder')"
8
8
  :data-testid="baseTestId ? `${baseTestId}-search` : undefined"
9
9
  @input="handleSearch"
10
10
  />
@@ -12,7 +12,7 @@
12
12
 
13
13
  <div v-if="loading" class="text-center py-4 text-text-secondary">
14
14
  <div class="spinner mx-auto mb-2"></div>
15
- <p>加载中...</p>
15
+ <p>{{ loadingText }}</p>
16
16
  </div>
17
17
 
18
18
  <div v-else-if="users.length > 0" class="user-list space-y-2 max-h-64 overflow-y-auto">
@@ -40,13 +40,14 @@
40
40
  </div>
41
41
 
42
42
  <div v-else class="text-center py-4 text-text-tertiary">
43
- <p>未找到符合条件的用户</p>
43
+ <p>{{ emptyText }}</p>
44
44
  </div>
45
45
  </div>
46
46
  </template>
47
47
 
48
48
  <script setup lang="ts">
49
49
  import { ref, onMounted, computed, useAttrs } from 'vue'
50
+ import { useI18n } from 'vue-i18n'
50
51
 
51
52
  interface UserBasicInfo {
52
53
  id: string
@@ -61,6 +62,10 @@ const props = defineProps<{
61
62
  testId?: string
62
63
  }>()
63
64
 
65
+ const { t } = useI18n()
66
+ const loadingText = computed(() => t('ui.common.loading'))
67
+ const emptyText = computed(() => t('ui.userPicker.empty'))
68
+
64
69
  const attrs = useAttrs()
65
70
  const baseTestId = computed<string | undefined>(() => props.testId ?? (attrs['data-testid'] as string | undefined))
66
71
 
package/index.ts CHANGED
@@ -19,4 +19,5 @@ export { default as DateTimePicker } from './components/DateTimePicker.vue'
19
19
  export { default as Tree } from './components/Tree.vue'
20
20
  export type { TreeNode } from './components/Tree.vue'
21
21
  export { useToast } from './composables/useToast'
22
+ export { uiKitMessages, uiKitMessagesZhCN, uiKitMessagesEnUS } from './messages'
22
23
  export type { Column, SelectOption, MenuItem, MenuGroup, NavModule, DropdownItem, ToastType, DateTimePreset } from './types'
@@ -0,0 +1,84 @@
1
+ // ui-kit 词典(en-US):扁平 key 结构,key 前缀 ui.,由 app 在 createI18n 时与自身词典合并。
2
+ // 覆盖 ui-kit 组件内置文案的默认值;组件 props 显式传入的文案始终优先。
3
+ export const uiKitMessagesEnUS: Record<string, string> = {
4
+ // Common
5
+ 'ui.common.confirm': 'Confirm',
6
+ 'ui.common.cancel': 'Cancel',
7
+ 'ui.common.loading': 'Loading...',
8
+ 'ui.common.submitting': 'Submitting...',
9
+ 'ui.common.retry': 'Retry',
10
+ 'ui.common.noMatch': 'No matches found',
11
+ 'ui.common.noData': 'No data',
12
+ 'ui.common.fetchFailed': 'Failed to load',
13
+ 'ui.common.search': 'Search',
14
+
15
+ // ConfirmDialog
16
+ 'ui.confirmDialog.title': 'Confirm deletion',
17
+ 'ui.confirmDialog.confirmText': 'Delete',
18
+ 'ui.confirmDialog.loadingText': 'Deleting...',
19
+
20
+ // DataTable
21
+ 'ui.dataTable.loading': 'Loading...',
22
+ 'ui.dataTable.empty': 'No data',
23
+
24
+ // Pagination
25
+ 'ui.pagination.total': 'Total {total} items, {startItem} - {endItem}',
26
+ 'ui.pagination.prev': 'Previous',
27
+ 'ui.pagination.next': 'Next',
28
+ 'ui.pagination.pageSize': '{size}/page',
29
+
30
+ // StatusTag (status value → label, matching StatusTag internal mapping keys)
31
+ 'ui.status.active': 'Active',
32
+ 'ui.status.locked': 'Locked',
33
+ 'ui.status.deleted': 'Deleted',
34
+ 'ui.status.disabled': 'Disabled',
35
+
36
+ // FormDialog
37
+ 'ui.formDialog.confirmText': 'Confirm',
38
+
39
+ // FilterDropdown
40
+ 'ui.filterDropdown.all': 'All',
41
+
42
+ // SearchSelect
43
+ 'ui.searchSelect.placeholder': 'Please select',
44
+
45
+ // SuggestInput
46
+ 'ui.suggestInput.placeholder': 'Type or select',
47
+ 'ui.suggestInput.loadBtnText': 'Load',
48
+ 'ui.suggestInput.loading': 'Loading...',
49
+
50
+ // Tree
51
+ 'ui.tree.searchPlaceholder': 'Search...',
52
+
53
+ // SideMenu
54
+ 'ui.sideMenu.expand': 'Expand menu',
55
+ 'ui.sideMenu.collapse': 'Collapse menu',
56
+ 'ui.sideMenu.home': 'Home',
57
+
58
+ // UserPicker
59
+ 'ui.userPicker.searchPlaceholder': 'Search user...',
60
+ 'ui.userPicker.empty': 'No matching users found',
61
+
62
+ // FileUploader
63
+ 'ui.fileUploader.select': 'Choose file',
64
+ 'ui.fileUploader.uploading': 'Uploading {progress}%',
65
+ 'ui.fileUploader.done': 'Done',
66
+ 'ui.fileUploader.failed': 'Upload failed',
67
+
68
+ // DateTimePicker
69
+ 'ui.dateTimePicker.year': '{year}',
70
+ 'ui.dateTimePicker.month': '{month}',
71
+ 'ui.dateTimePicker.monthSection': 'Month',
72
+ 'ui.dateTimePicker.week.sun': 'Su',
73
+ 'ui.dateTimePicker.week.mon': 'Mo',
74
+ 'ui.dateTimePicker.week.tue': 'Tu',
75
+ 'ui.dateTimePicker.week.wed': 'We',
76
+ 'ui.dateTimePicker.week.thu': 'Th',
77
+ 'ui.dateTimePicker.week.fri': 'Fr',
78
+ 'ui.dateTimePicker.week.sat': 'Sa',
79
+
80
+ // TopNavbar
81
+ 'ui.navbar.title': 'Medical Device Intelligent Platform',
82
+ 'ui.navbar.currentTheme': 'Theme: {theme}',
83
+ 'ui.navbar.language': 'Language',
84
+ }
@@ -0,0 +1,14 @@
1
+ import { uiKitMessagesZhCN } from './zh-CN'
2
+ import { uiKitMessagesEnUS } from './en-US'
3
+
4
+ export { uiKitMessagesZhCN } from './zh-CN'
5
+ export { uiKitMessagesEnUS } from './en-US'
6
+
7
+ /** 各语言词典聚合:app 侧 createI18n 时展开合并 */
8
+ export const uiKitMessages = {
9
+ 'zh-CN': uiKitMessagesZhCN,
10
+ 'en-US': uiKitMessagesEnUS,
11
+ }
12
+
13
+ export type UiKitMessagesZh = typeof uiKitMessagesZhCN
14
+ export type UiKitMessagesEn = typeof uiKitMessagesEnUS
@@ -0,0 +1,84 @@
1
+ // ui-kit 词典(zh-CN):扁平 key 结构,key 前缀 ui.,由 app 在 createI18n 时与自身词典合并。
2
+ // 覆盖 ui-kit 组件内置文案的默认值;组件 props 显式传入的文案始终优先。
3
+ export const uiKitMessagesZhCN: Record<string, string> = {
4
+ // 通用
5
+ 'ui.common.confirm': '确认',
6
+ 'ui.common.cancel': '取消',
7
+ 'ui.common.loading': '加载中...',
8
+ 'ui.common.submitting': '提交中...',
9
+ 'ui.common.retry': '重试',
10
+ 'ui.common.noMatch': '无匹配项',
11
+ 'ui.common.noData': '暂无数据',
12
+ 'ui.common.fetchFailed': '获取失败',
13
+ 'ui.common.search': '搜索',
14
+
15
+ // ConfirmDialog
16
+ 'ui.confirmDialog.title': '确认删除',
17
+ 'ui.confirmDialog.confirmText': '确认删除',
18
+ 'ui.confirmDialog.loadingText': '删除中...',
19
+
20
+ // DataTable
21
+ 'ui.dataTable.loading': '加载中...',
22
+ 'ui.dataTable.empty': '暂无数据',
23
+
24
+ // Pagination
25
+ 'ui.pagination.total': '共 {total} 条,第 {startItem} - {endItem} 条',
26
+ 'ui.pagination.prev': '上一页',
27
+ 'ui.pagination.next': '下一页',
28
+ 'ui.pagination.pageSize': '{size}条/页',
29
+
30
+ // StatusTag(状态值→文案,同 StatusTag 内部映射 key)
31
+ 'ui.status.active': '正常',
32
+ 'ui.status.locked': '已锁定',
33
+ 'ui.status.deleted': '已删除',
34
+ 'ui.status.disabled': '已停用',
35
+
36
+ // FormDialog
37
+ 'ui.formDialog.confirmText': '确认',
38
+
39
+ // FilterDropdown
40
+ 'ui.filterDropdown.all': '全部',
41
+
42
+ // SearchSelect
43
+ 'ui.searchSelect.placeholder': '请选择',
44
+
45
+ // SuggestInput
46
+ 'ui.suggestInput.placeholder': '请输入或选择',
47
+ 'ui.suggestInput.loadBtnText': '获取',
48
+ 'ui.suggestInput.loading': '获取中...',
49
+
50
+ // Tree
51
+ 'ui.tree.searchPlaceholder': '搜索...',
52
+
53
+ // SideMenu
54
+ 'ui.sideMenu.expand': '展开菜单',
55
+ 'ui.sideMenu.collapse': '收起菜单',
56
+ 'ui.sideMenu.home': '首页',
57
+
58
+ // UserPicker
59
+ 'ui.userPicker.searchPlaceholder': '搜索用户...',
60
+ 'ui.userPicker.empty': '未找到符合条件的用户',
61
+
62
+ // FileUploader
63
+ 'ui.fileUploader.select': '选择文件',
64
+ 'ui.fileUploader.uploading': '上传中 {progress}%',
65
+ 'ui.fileUploader.done': '完成',
66
+ 'ui.fileUploader.failed': '上传失败',
67
+
68
+ // DateTimePicker
69
+ 'ui.dateTimePicker.year': '{year}年',
70
+ 'ui.dateTimePicker.month': '{month}月',
71
+ 'ui.dateTimePicker.monthSection': '月份',
72
+ 'ui.dateTimePicker.week.sun': '日',
73
+ 'ui.dateTimePicker.week.mon': '一',
74
+ 'ui.dateTimePicker.week.tue': '二',
75
+ 'ui.dateTimePicker.week.wed': '三',
76
+ 'ui.dateTimePicker.week.thu': '四',
77
+ 'ui.dateTimePicker.week.fri': '五',
78
+ 'ui.dateTimePicker.week.sat': '六',
79
+
80
+ // TopNavbar
81
+ 'ui.navbar.title': '医疗设备智能平台',
82
+ 'ui.navbar.currentTheme': '当前主题: {theme}',
83
+ 'ui.navbar.language': '语言',
84
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aquiferre/ui-kit",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -11,15 +11,17 @@
11
11
  "*.ts",
12
12
  "*.vue",
13
13
  "components/**/*.vue",
14
- "composables/**/*.ts"
14
+ "composables/**/*.ts",
15
+ "messages/*.ts"
15
16
  ],
16
17
  "dependencies": {
17
18
  "pinyin-pro": "^3.28.1"
18
19
  },
19
20
  "peerDependencies": {
20
- "@aquiferre/theme-kit": ">=0.1.0",
21
+ "@aquiferre/theme-kit": ">=0.2.0",
21
22
  "pinia": ">=2.0.0",
22
- "vue": ">=3.0.0"
23
+ "vue": ">=3.0.0",
24
+ "vue-i18n": "^11.0.0"
23
25
  },
24
26
  "publishConfig": {
25
27
  "access": "public"