vome-core 0.3.10 → 0.3.12

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 (120) hide show
  1. package/dist/admin/api/client.js +9 -8
  2. package/dist/admin/components/ui/avatar/index.js +1 -1
  3. package/dist/admin/components/ui/badge/index.d.ts +1 -1
  4. package/dist/admin/components/ui/button/index.d.ts +1 -1
  5. package/dist/admin/components/ui/checkbox/index.js +1 -1
  6. package/dist/admin/components/ui/dialog/index.js +1 -1
  7. package/dist/admin/components/ui/dropdown-menu/index.js +1 -1
  8. package/dist/admin/components/ui/input/Input.vue +2 -1
  9. package/dist/admin/components/ui/input/index.js +1 -1
  10. package/dist/admin/components/ui/label/index.js +1 -1
  11. package/dist/admin/components/ui/scroll-area/index.js +1 -1
  12. package/dist/admin/components/ui/select/index.js +1 -1
  13. package/dist/admin/components/ui/separator/index.js +1 -1
  14. package/dist/admin/components/ui/sheet/index.js +1 -1
  15. package/dist/admin/components/ui/sonner/index.js +1 -1
  16. package/dist/admin/components/ui/table/index.js +1 -1
  17. package/dist/admin/components/ui/tabs/index.js +1 -1
  18. package/dist/admin/components/ui/tooltip/index.js +1 -1
  19. package/dist/admin/components/vm-aside.vue +8 -6
  20. package/dist/admin/components/vm-auto-perm-dialog.vue +12 -11
  21. package/dist/admin/components/vm-check-tree.vue +3 -2
  22. package/dist/admin/components/vm-dept-tree.vue +31 -29
  23. package/dist/admin/components/vm-empty.vue +16 -2
  24. package/dist/admin/components/vm-eps-perm-picker.vue +3 -2
  25. package/dist/admin/components/vm-form-dialog.vue +4 -3
  26. package/dist/admin/components/vm-group-cascader.vue +5 -4
  27. package/dist/admin/components/vm-icon-picker.vue +24 -15
  28. package/dist/admin/components/vm-inline-search.vue +3 -2
  29. package/dist/admin/components/vm-loading.vue +4 -3
  30. package/dist/admin/components/vm-markdown.vue +6 -5
  31. package/dist/admin/components/vm-role-picker.vue +6 -5
  32. package/dist/admin/components/vm-view-path-picker.vue +3 -2
  33. package/dist/admin/crud/auto-fields.js +1 -1
  34. package/dist/admin/crud/components/vm-avatar.vue +2 -1
  35. package/dist/admin/crud/components/vm-color-picker.vue +8 -8
  36. package/dist/admin/crud/components/vm-column-custom.vue +6 -5
  37. package/dist/admin/crud/components/vm-copy-btn.vue +4 -3
  38. package/dist/admin/crud/components/vm-date-calendar-panel.vue +30 -27
  39. package/dist/admin/crud/components/vm-date-picker.vue +6 -5
  40. package/dist/admin/crud/components/vm-date-range.vue +5 -4
  41. package/dist/admin/crud/components/vm-datetime-field.vue +4 -3
  42. package/dist/admin/crud/components/vm-json-code.vue +3 -2
  43. package/dist/admin/crud/components/vm-json-editor.vue +3 -2
  44. package/dist/admin/crud/components/vm-json-kv-editor.vue +13 -12
  45. package/dist/admin/crud/components/vm-multi-select.vue +5 -4
  46. package/dist/admin/crud/components/vm-number-range.vue +5 -4
  47. package/dist/admin/crud/components/vm-preview-viewer.vue +33 -29
  48. package/dist/admin/crud/components/vm-richtext-extensions.js +1 -1
  49. package/dist/admin/crud/components/vm-richtext.vue +33 -30
  50. package/dist/admin/crud/components/vm-select.vue +4 -3
  51. package/dist/admin/crud/components/vm-status-tag.vue +2 -1
  52. package/dist/admin/crud/components/vm-switch.vue +5 -4
  53. package/dist/admin/crud/components/vm-text-link.vue +3 -2
  54. package/dist/admin/crud/components/vm-tree-select.vue +3 -2
  55. package/dist/admin/crud/components/vm-upload-item.vue +6 -4
  56. package/dist/admin/crud/components/vm-upload.vue +6 -5
  57. package/dist/admin/crud/config.js +1 -1
  58. package/dist/admin/crud/confirm.js +1 -1
  59. package/dist/admin/crud/crud-page-request.js +1 -1
  60. package/dist/admin/crud/data-i18n.js +1 -1
  61. package/dist/admin/crud/dict.d.ts +1 -1
  62. package/dist/admin/crud/dict.js +1 -1
  63. package/dist/admin/crud/export-fetch.js +1 -1
  64. package/dist/admin/crud/key.js +1 -1
  65. package/dist/admin/crud/plugins.js +1 -1
  66. package/dist/admin/crud/span.js +1 -1
  67. package/dist/admin/crud/style.js +1 -1
  68. package/dist/admin/crud/validate.js +1 -1
  69. package/dist/admin/crud/vm-crud.vue +15 -15
  70. package/dist/admin/crud/vm-data-i18n-btn.vue +1 -1
  71. package/dist/admin/crud/vm-export-btn.vue +7 -7
  72. package/dist/admin/crud/vm-import-btn.vue +33 -27
  73. package/dist/admin/crud/vm-table.vue +7 -2
  74. package/dist/admin/crud/vm-upsert.vue +1 -0
  75. package/dist/admin/directives/perm.js +1 -1
  76. package/dist/admin/hooks/useUpload.js +1 -1
  77. package/dist/admin/lib/browser.js +1 -1
  78. package/dist/admin/lib/cn.js +1 -1
  79. package/dist/admin/lib/dialog-float.js +1 -1
  80. package/dist/admin/lib/export-excel.js +1 -1
  81. package/dist/admin/lib/file-preview.js +8 -6
  82. package/dist/admin/lib/import-excel.js +1 -1
  83. package/dist/admin/lib/json.d.ts +0 -1
  84. package/dist/admin/lib/json.js +1 -1
  85. package/dist/admin/lib/menu.js +1 -1
  86. package/dist/admin/lib/micro-locale.js +1 -1
  87. package/dist/admin/lib/micro-theme.js +1 -1
  88. package/dist/admin/lib/shell.js +1 -1
  89. package/dist/admin/lib/theme-color-options.d.ts +1 -2
  90. package/dist/admin/lib/theme-color-options.js +1 -1
  91. package/dist/admin/lib/tree.js +1 -1
  92. package/dist/admin/lib/upload.d.ts +5 -0
  93. package/dist/admin/lib/upload.js +1 -1
  94. package/dist/admin/lib/video-frame.js +1 -1
  95. package/dist/admin/router/create.js +1 -1
  96. package/dist/admin/router/guards.js +1 -1
  97. package/dist/ai/index.js +1 -1
  98. package/dist/client/ai-model-registry.js +1 -1
  99. package/dist/client/base.js +1 -1
  100. package/dist/client/configure.js +1 -1
  101. package/dist/client/dict.js +1 -1
  102. package/dist/client/eps-payload.js +1 -1
  103. package/dist/client/eps.js +1 -1
  104. package/dist/client/index.js +1 -1
  105. package/dist/client/locale-store.js +1 -1
  106. package/dist/client/service.js +1 -1
  107. package/dist/client/vite-admin.js +1 -1
  108. package/dist/client/vite-auto-import.js +1 -1
  109. package/dist/client/vite-micro-proxy.js +1 -1
  110. package/dist/client/vite-plugin-eps.js +1 -1
  111. package/dist/client/vite-resolve.js +1 -1
  112. package/dist/config/index.js +1 -1
  113. package/dist/index.js +1 -1
  114. package/dist/server/index.js +1 -1
  115. package/dist/shared/datetime-picker.js +1 -1
  116. package/dist/shared/excel.js +1 -1
  117. package/dist/shared/index.js +1 -1
  118. package/dist/shared/locale-json.js +1 -1
  119. package/dist/shared/tree.js +1 -1
  120. package/package.json +1 -1
@@ -1,10 +1,10 @@
1
1
  <template>
2
2
  <vm-aside
3
- :title="onlyTrashed ? '部门回收站' : '组织架构'"
3
+ :title="onlyTrashed ? crudLabel('deptTrash') : crudLabel('deptOrg')"
4
4
  :empty="emptyText"
5
5
  :loading="loading"
6
6
  :add="canAdd && !onlyTrashed"
7
- add-title="新增顶级部门"
7
+ :add-title="crudLabel('deptAddRoot')"
8
8
  @refresh="emit('refresh')"
9
9
  @add="openAdd(null)"
10
10
  >
@@ -15,7 +15,7 @@
15
15
  @click="select(null)"
16
16
  >
17
17
  <i class="ri-building-2-line" />
18
- <span>{{ rootLabel }}</span>
18
+ <span>{{ rootLabel ?? crudLabel('all') }}</span>
19
19
  </button>
20
20
 
21
21
  <div
@@ -40,7 +40,7 @@
40
40
  <div v-if="onlyTrashed && canTrashOps" class="vm-dept-tree__ops" @click.stop>
41
41
  <button
42
42
  type="button"
43
- title="恢复"
43
+ :title="crudLabel('restore')"
44
44
  :disabled="trashBusyId === row.id"
45
45
  @click="restoreDept(row)"
46
46
  >
@@ -48,7 +48,7 @@
48
48
  </button>
49
49
  <button
50
50
  type="button"
51
- title="彻底删除"
51
+ :title="crudLabel('forceDelete')"
52
52
  :disabled="trashBusyId === row.id"
53
53
  @click="forceDeleteDept(row)"
54
54
  >
@@ -56,13 +56,13 @@
56
56
  </button>
57
57
  </div>
58
58
  <div v-else-if="!onlyTrashed && canManage" class="vm-dept-tree__ops" @click.stop>
59
- <button v-if="canAdd" type="button" title="新增子部门" @click="openAdd(row.id)">
59
+ <button v-if="canAdd" type="button" :title="crudLabel('deptAddChild')" @click="openAdd(row.id)">
60
60
  <i class="ri-add-line" />
61
61
  </button>
62
- <button v-if="canUpdate" type="button" title="编辑" @click="openEdit(row)">
62
+ <button v-if="canUpdate" type="button" :title="crudLabel('update')" @click="openEdit(row)">
63
63
  <i class="ri-edit-line" />
64
64
  </button>
65
- <button v-if="canDelete" type="button" title="删除" @click="remove(row)">
65
+ <button v-if="canDelete" type="button" :title="crudLabel('delete')" @click="remove(row)">
66
66
  <i class="ri-delete-bin-line" />
67
67
  </button>
68
68
  </div>
@@ -72,7 +72,7 @@
72
72
  <vm-form-dialog
73
73
  v-model:open="formOpen"
74
74
  v-model="formData"
75
- :title="formMode === 'add' ? '新增部门' : '编辑部门'"
75
+ :title="formMode === 'add' ? crudLabel('deptAddTitle') : crudLabel('deptEditTitle')"
76
76
  :items="dialogItems"
77
77
  @submit="onFormSubmit"
78
78
  />
@@ -82,6 +82,7 @@
82
82
  import { ref, computed, watch } from 'vue'
83
83
  import { flattenTree } from '../lib/tree'
84
84
  import { vmConfirm } from '../crud/confirm'
85
+ import { crudFormat, crudLabel } from '../crud/style'
85
86
  import { mergeByProp } from '../crud/auto-fields'
86
87
  import type { CrudFormItem } from '../../../typings/admin/comm/crud'
87
88
  import type { VmFormDialogSubmitHelpers } from './vm-form-dialog.vue'
@@ -137,7 +138,7 @@ const props = withDefaults(
137
138
  data: () => [],
138
139
  loading: false,
139
140
  modelValue: null,
140
- rootLabel: '全部',
141
+ rootLabel: undefined,
141
142
  onlyTrashed: false,
142
143
  canAdd: false,
143
144
  canUpdate: false,
@@ -172,20 +173,20 @@ const canTrashOps = computed(() => props.canRestore || props.canDelete)
172
173
  const builtinFormItems = computed((): CrudFormItem[] => [
173
174
  {
174
175
  prop: 'parentLabel',
175
- label: '上级部门',
176
+ label: crudLabel('deptParent'),
176
177
  span: 12,
177
178
  disabled: true,
178
179
  },
179
180
  {
180
181
  prop: 'name',
181
- label: '名称',
182
+ label: crudLabel('fieldName'),
182
183
  span: 12,
183
184
  required: true,
184
- placeholder: '部门名称',
185
+ placeholder: crudLabel('deptNamePh'),
185
186
  },
186
187
  {
187
188
  prop: 'orderNum',
188
- label: '排序',
189
+ label: crudLabel('orderNum'),
189
190
  span: 12,
190
191
  type: 'number',
191
192
  value: 0,
@@ -247,7 +248,7 @@ const visibleRows = computed((): FlatRow[] => {
247
248
 
248
249
  const emptyText = computed(() => {
249
250
  if (props.loading || visibleRows.value.length) return ''
250
- return props.onlyTrashed ? '回收站为空' : '暂无部门'
251
+ return props.onlyTrashed ? crudLabel('trashEmpty') : crudLabel('deptEmpty')
251
252
  })
252
253
 
253
254
  function resolveFilterIds(id: number | null) {
@@ -257,7 +258,8 @@ function resolveFilterIds(id: number | null) {
257
258
 
258
259
  function select(id: number | null) {
259
260
  emit('update:modelValue', id)
260
- const name = id == null ? props.rootLabel : idNameMap.value.get(id) || ''
261
+ const name =
262
+ id == null ? (props.rootLabel ?? '') : idNameMap.value.get(id) || ''
261
263
  emit('change', id, { name, ids: resolveFilterIds(id) })
262
264
  }
263
265
 
@@ -291,7 +293,7 @@ watch(
291
293
  )
292
294
 
293
295
  function parentLabelOf(parentId: number | null) {
294
- return parentId == null ? '顶级' : idNameMap.value.get(parentId) || String(parentId)
296
+ return parentId == null ? crudLabel('topLevel') : idNameMap.value.get(parentId) || String(parentId)
295
297
  }
296
298
 
297
299
  function openAdd(parentId: number | null) {
@@ -339,10 +341,10 @@ function onFormSubmit(data: Record<string, unknown>, helpers: VmFormDialogSubmit
339
341
 
340
342
  async function remove(row: FlatRow) {
341
343
  const ok = await vmConfirm({
342
- title: '删除部门',
343
- message: `确定删除「${row.label}」?`,
344
- confirmText: '删除',
345
- cancelText: '取消',
344
+ title: crudLabel('deptDeleteTitle'),
345
+ message: crudFormat(crudLabel('deptDeleteMsg'), row.label),
346
+ confirmText: crudLabel('delete'),
347
+ cancelText: crudLabel('close'),
346
348
  })
347
349
  if (!ok) return
348
350
  if (props.modelValue === row.id) select(null)
@@ -352,10 +354,10 @@ async function remove(row: FlatRow) {
352
354
  async function restoreDept(row: FlatRow) {
353
355
  if (trashBusyId.value === row.id) return
354
356
  const ok = await vmConfirm({
355
- title: '恢复部门',
356
- message: `确定恢复「${row.label}」?`,
357
- confirmText: '恢复',
358
- cancelText: '取消',
357
+ title: crudLabel('deptRestoreTitle'),
358
+ message: crudFormat(crudLabel('deptRestoreMsg'), row.label),
359
+ confirmText: crudLabel('restore'),
360
+ cancelText: crudLabel('close'),
359
361
  })
360
362
  if (!ok) return
361
363
  trashBusyId.value = row.id
@@ -367,10 +369,10 @@ async function restoreDept(row: FlatRow) {
367
369
  async function forceDeleteDept(row: FlatRow) {
368
370
  if (trashBusyId.value === row.id) return
369
371
  const ok = await vmConfirm({
370
- title: '彻底删除',
371
- message: `确定彻底删除「${row.label}」?此操作不可恢复。`,
372
- confirmText: '彻底删除',
373
- cancelText: '取消',
372
+ title: crudLabel('deptForceDeleteTitle'),
373
+ message: crudFormat(crudLabel('deptForceDeleteMsg'), row.label),
374
+ confirmText: crudLabel('forceDelete'),
375
+ cancelText: crudLabel('close'),
374
376
  })
375
377
  if (!ok) return
376
378
  trashBusyId.value = row.id
@@ -1,9 +1,17 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
+ import { crudLabel } from '../crud/style'
3
4
  import { getScarceSvgUrl } from '../lib/scarce-assets'
4
5
 
5
6
  defineOptions({ name: 'vm-empty' })
6
7
 
8
+ /** scarce SVG 文件名 → crudLabel key(文件名保持中文) */
9
+ const KIND_LABEL_KEY: Record<string, string> = {
10
+ 暂无数据: 'empty',
11
+ 暂无内容: 'emptyContent',
12
+ 页面不存在: 'pageNotFound',
13
+ }
14
+
7
15
  const props = withDefaults(
8
16
  defineProps<{
9
17
  /** 对应 scarce 文件名(不含 .svg),如「暂无数据」 */
@@ -18,12 +26,18 @@ const props = withDefaults(
18
26
  )
19
27
 
20
28
  const src = computed(() => getScarceSvgUrl(props.kind))
29
+
30
+ const displayText = computed(() => {
31
+ if (props.description) return props.description
32
+ const key = KIND_LABEL_KEY[props.kind]
33
+ return key ? crudLabel(key) : props.kind
34
+ })
21
35
  </script>
22
36
 
23
37
  <template>
24
38
  <div class="vm-empty" :class="props.class">
25
- <img v-if="src" :src="src" :alt="kind" class="vm-empty__img" />
26
- <p class="vm-empty__text">{{ description || kind }}</p>
39
+ <img v-if="src" :src="src" :alt="displayText" class="vm-empty__img" />
40
+ <p class="vm-empty__text">{{ displayText }}</p>
27
41
  <slot />
28
42
  </div>
29
43
  </template>
@@ -6,7 +6,7 @@
6
6
  :model-value="modelValue ?? ''"
7
7
  :tree="epsTree"
8
8
  multiple
9
- :placeholder="placeholder"
9
+ :placeholder="placeholder ?? crudLabel('pickPerm')"
10
10
  :clearable="true"
11
11
  :editable="false"
12
12
  @update:model-value="(v) => emit('update:modelValue', v)"
@@ -19,6 +19,7 @@
19
19
  </template>
20
20
 
21
21
  <script setup lang="ts">
22
+ import { crudLabel } from '../crud/style'
22
23
  import { ref, computed, watch, onMounted } from 'vue'
23
24
  import { api } from '../api/client'
24
25
  import type { EpsSide } from '../lib/eps'
@@ -39,7 +40,7 @@ const props = withDefaults(
39
40
  side?: EpsSide
40
41
  }>(),
41
42
  {
42
- placeholder: '请选择权限',
43
+ placeholder: undefined,
43
44
  side: 'admin',
44
45
  },
45
46
  )
@@ -115,7 +115,7 @@
115
115
  class="ri-close-circle-fill vm-crud-upsert__textarea-clear"
116
116
  role="button"
117
117
  tabindex="-1"
118
- aria-label="清除"
118
+ :aria-label="crudLabel('clear')"
119
119
  @mousedown.prevent
120
120
  @click="setField(item.prop, '')"
121
121
  />
@@ -144,7 +144,7 @@
144
144
  :disabled="submitting"
145
145
  @click="onCloseClick"
146
146
  >
147
- {{ closeText || '取消' }}
147
+ {{ closeText || crudLabel('close') }}
148
148
  </button>
149
149
  <button
150
150
  v-if="confirm !== false && !readonly"
@@ -153,7 +153,7 @@
153
153
  :disabled="submitting"
154
154
  @click="onConfirmClick"
155
155
  >
156
- {{ submitting ? '提交中…' : confirmText || '确定' }}
156
+ {{ submitting ? crudLabel('submitting') : confirmText || crudLabel('confirm') }}
157
157
  </button>
158
158
  <slot name="footer-append" v-bind="footerSlotProps" />
159
159
  </template>
@@ -163,6 +163,7 @@
163
163
  </template>
164
164
 
165
165
  <script setup lang="ts">
166
+ import { crudLabel } from '../crud/style'
166
167
  import { ref, computed, watch, useSlots, toValue } from 'vue'
167
168
  import { preventDialogOutsideClose } from '../lib/dialog-float'
168
169
  import { resolveCrudComponent } from '../crud/registry'
@@ -10,7 +10,7 @@
10
10
  v-if="editable"
11
11
  class="vm-group-cascader__input"
12
12
  :value="displayText"
13
- :placeholder="placeholder"
13
+ :placeholder="placeholder ?? crudLabel('pleaseSelect')"
14
14
  :disabled="disabled"
15
15
  @focus="openPanel"
16
16
  @input="onInput"
@@ -30,7 +30,7 @@
30
30
  </span>
31
31
  </span>
32
32
  <span v-else-if="displayText" class="vm-group-cascader__value">{{ displayText }}</span>
33
- <span v-else class="vm-group-cascader__ph">{{ placeholder }}</span>
33
+ <span v-else class="vm-group-cascader__ph">{{ placeholder ?? crudLabel('pleaseSelect') }}</span>
34
34
  </button>
35
35
  <i
36
36
  v-if="clearable && hasValue"
@@ -82,7 +82,7 @@
82
82
  class="ri-check-line vm-group-cascader__tick"
83
83
  />
84
84
  </button>
85
- <p v-if="!col.length" class="vm-group-cascader__empty">暂无数据</p>
85
+ <p v-if="!col.length" class="vm-group-cascader__empty">{{ crudLabel('empty') }}</p>
86
86
  </div>
87
87
  </div>
88
88
  </Teleport>
@@ -90,6 +90,7 @@
90
90
  </template>
91
91
 
92
92
  <script setup lang="ts">
93
+ import { crudLabel } from '../crud/style'
93
94
  import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
94
95
  import { POPUP_TRIGGER_GAP } from '../lib/dialog-float'
95
96
  import type { GroupCascaderNode } from '../../../typings/admin/comm/cascader'
@@ -114,7 +115,7 @@ const props = withDefaults(
114
115
  multiple: false,
115
116
  clearable: true,
116
117
  editable: true,
117
- placeholder: '请选择',
118
+ placeholder: undefined,
118
119
  separator: ',',
119
120
  },
120
121
  )
@@ -6,7 +6,7 @@
6
6
  class="vm-icon-picker__preview"
7
7
  :class="{ 'is-open': open }"
8
8
  :disabled="disabled"
9
- :title="normalized || '选择图标'"
9
+ :title="normalized || crudLabel('pickIcon')"
10
10
  @click="toggle"
11
11
  >
12
12
  <i v-if="normalized" :class="normalized" />
@@ -16,7 +16,7 @@
16
16
  <input
17
17
  class="vm-icon-picker__input"
18
18
  :value="displayName"
19
- :placeholder="placeholder"
19
+ :placeholder="placeholder ?? crudLabel('iconPlaceholder')"
20
20
  :disabled="disabled"
21
21
  @focus="openPanel"
22
22
  @input="onNameInput"
@@ -53,7 +53,7 @@
53
53
  ref="searchRef"
54
54
  class="vm-icon-picker__search-input"
55
55
  :value="search"
56
- placeholder="模糊搜索,如 home / shield"
56
+ :placeholder="crudLabel('iconSearchPh')"
57
57
  @input="onSearchInput"
58
58
  @keydown.esc.stop="closePanel"
59
59
  />
@@ -63,7 +63,7 @@
63
63
  @click.stop="clearSearch"
64
64
  />
65
65
  </div>
66
- <div class="vm-icon-picker__styles" role="group" aria-label="图标类型">
66
+ <div class="vm-icon-picker__styles" role="group" :aria-label="crudLabel('iconType')">
67
67
  <button
68
68
  v-for="opt in STYLE_OPTS"
69
69
  :key="opt.value"
@@ -95,8 +95,8 @@
95
95
  type="button"
96
96
  class="vm-icon-picker__page-btn"
97
97
  :disabled="!canPrev"
98
- title="上一页"
99
- aria-label="上一页"
98
+ :title="crudLabel('prevPage')"
99
+ :aria-label="crudLabel('prevPage')"
100
100
  @click.prevent.stop="prevBatch"
101
101
  >
102
102
  <i class="ri-arrow-left-s-line" />
@@ -106,13 +106,13 @@
106
106
  type="button"
107
107
  class="vm-icon-picker__page-btn"
108
108
  :disabled="!canNext"
109
- title="下一页"
110
- aria-label="下一页"
109
+ :title="crudLabel('nextPage')"
110
+ :aria-label="crudLabel('nextPage')"
111
111
  @click.prevent.stop="nextBatch"
112
112
  >
113
113
  <i class="ri-arrow-right-s-line" />
114
114
  </button>
115
- <p v-if="!filtered.length" class="vm-icon-picker__empty">无匹配图标</p>
115
+ <p v-if="!filtered.length" class="vm-icon-picker__empty">{{ crudLabel('noMatchIcon') }}</p>
116
116
  </div>
117
117
  </div>
118
118
  </FocusScope>
@@ -121,6 +121,7 @@
121
121
  </template>
122
122
 
123
123
  <script setup lang="ts">
124
+ import { crudLabel } from '../crud/style'
124
125
  import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
125
126
  import { FocusScope } from 'reka-ui'
126
127
  import { POPUP_TRIGGER_GAP } from '../lib/dialog-float'
@@ -130,11 +131,19 @@ defineOptions({ name: 'vm-icon-picker' })
130
131
 
131
132
  type IconStyle = 'fill' | 'line' | 'other'
132
133
 
133
- const STYLE_OPTS: { value: IconStyle; label: string; title: string }[] = [
134
- { value: 'fill', label: '实心', title: 'Fill 实心图标' },
135
- { value: 'line', label: '线形', title: 'Line 线形图标' },
136
- { value: 'other', label: '其它', title: '无 fill/line 后缀(多为编辑类)' },
137
- ]
134
+ const STYLE_OPTS = computed(() =>
135
+ (
136
+ [
137
+ { value: 'fill' as const, labelKey: 'iconFill', titleKey: 'iconFillTitle' },
138
+ { value: 'line' as const, labelKey: 'iconLine', titleKey: 'iconLineTitle' },
139
+ { value: 'other' as const, labelKey: 'iconOther', titleKey: 'iconOtherTitle' },
140
+ ] as const
141
+ ).map((o) => ({
142
+ value: o.value,
143
+ label: crudLabel(o.labelKey),
144
+ title: crudLabel(o.titleKey),
145
+ })),
146
+ )
138
147
 
139
148
  const props = withDefaults(
140
149
  defineProps<{
@@ -144,7 +153,7 @@ const props = withDefaults(
144
153
  clearable?: boolean
145
154
  }>(),
146
155
  {
147
- placeholder: '如 ri-home-fill',
156
+ placeholder: undefined,
148
157
  clearable: true,
149
158
  },
150
159
  )
@@ -4,7 +4,7 @@
4
4
  <input
5
5
  :value="modelValue"
6
6
  type="search"
7
- :placeholder="placeholder"
7
+ :placeholder="placeholder ?? crudLabel('search')"
8
8
  @input="onInput"
9
9
  @keydown.enter="emit('search')"
10
10
  />
@@ -12,6 +12,7 @@
12
12
  </template>
13
13
 
14
14
  <script setup lang="ts">
15
+ import { crudLabel } from '../crud/style'
15
16
  defineOptions({ name: 'vm-inline-search' })
16
17
 
17
18
  withDefaults(
@@ -21,7 +22,7 @@ withDefaults(
21
22
  }>(),
22
23
  {
23
24
  modelValue: '',
24
- placeholder: '搜索',
25
+ placeholder: undefined,
25
26
  },
26
27
  )
27
28
 
@@ -19,12 +19,13 @@
19
19
  :style="footerStyle"
20
20
  role="status"
21
21
  >
22
- {{ status === 'end' ? endText : loadingText }}
22
+ {{ status === 'end' ? (endText ?? crudLabel('noMore')) : (loadingText ?? crudLabel('loading')) }}
23
23
  </div>
24
24
  </template>
25
25
 
26
26
  <script setup lang="ts">
27
27
  import { computed } from 'vue'
28
+ import { crudLabel } from '../crud/style'
28
29
 
29
30
  defineOptions({ name: 'vm-loading' })
30
31
 
@@ -49,8 +50,8 @@ const props = withDefaults(
49
50
  mode: 'overlay',
50
51
  status: 'loading',
51
52
  fixed: false,
52
- loadingText: '加载中...',
53
- endText: '没有更多了',
53
+ loadingText: undefined,
54
+ endText: undefined,
54
55
  },
55
56
  )
56
57
 
@@ -15,7 +15,7 @@
15
15
  :aria-selected="tab === 'edit'"
16
16
  @click="tab = 'edit'"
17
17
  >
18
- 编辑
18
+ {{ crudLabel('markdownEdit') }}
19
19
  </button>
20
20
  <button
21
21
  type="button"
@@ -25,7 +25,7 @@
25
25
  :aria-selected="tab === 'preview'"
26
26
  @click="tab = 'preview'"
27
27
  >
28
- 预览
28
+ {{ crudLabel('markdownPreview') }}
29
29
  </button>
30
30
  </div>
31
31
 
@@ -36,7 +36,7 @@
36
36
  :style="editorStyle"
37
37
  :value="text"
38
38
  :disabled="disabled || uploading"
39
- :placeholder="placeholder"
39
+ :placeholder="placeholder ?? crudLabel('markdownHint')"
40
40
  @input="onInput"
41
41
  @paste="onPaste"
42
42
  />
@@ -49,11 +49,12 @@
49
49
  v-html="html"
50
50
  />
51
51
 
52
- <p v-if="uploading" class="vm-markdown__hint">图片上传中…</p>
52
+ <p v-if="uploading" class="vm-markdown__hint">{{ crudLabel('markdownUploading') }}</p>
53
53
  </div>
54
54
  </template>
55
55
 
56
56
  <script setup lang="ts">
57
+ import { crudLabel } from '../crud/style'
57
58
  import { computed, nextTick, ref, watch } from 'vue'
58
59
  import DOMPurify from 'dompurify'
59
60
  import { marked } from 'marked'
@@ -78,7 +79,7 @@ const props = withDefaults(
78
79
  edit: false,
79
80
  preview: true,
80
81
  disabled: false,
81
- placeholder: '支持 Markdown;可粘贴图片(需配置 uploadImage)',
82
+ placeholder: undefined,
82
83
  },
83
84
  )
84
85
 
@@ -9,7 +9,7 @@
9
9
  @click="togglePanel"
10
10
  >
11
11
  <span v-if="displayText" class="vm-role-picker__value">{{ displayText }}</span>
12
- <span v-else class="vm-role-picker__ph">{{ placeholder }}</span>
12
+ <span v-else class="vm-role-picker__ph">{{ placeholder ?? crudLabel('pickRole') }}</span>
13
13
  <i
14
14
  v-if="clearable && picked.size"
15
15
  class="ri-close-circle-fill vm-role-picker__clear"
@@ -25,8 +25,8 @@
25
25
  class="vm-role-picker__panel vm-dialog-float"
26
26
  :style="panelStyle"
27
27
  >
28
- <div v-if="loading" class="vm-role-picker__state">加载中…</div>
29
- <div v-else-if="!options.length" class="vm-role-picker__state">暂无角色</div>
28
+ <div v-if="loading" class="vm-role-picker__state">{{ crudLabel('loading') }}</div>
29
+ <div v-else-if="!options.length" class="vm-role-picker__state">{{ crudLabel('noRoles') }}</div>
30
30
  <div v-else class="vm-role-picker__list vm-scroll">
31
31
  <label
32
32
  v-for="opt in options"
@@ -48,6 +48,7 @@
48
48
  </template>
49
49
 
50
50
  <script setup lang="ts">
51
+ import { crudFormat, crudLabel } from '../crud/style'
51
52
  import { useVome } from '../hooks/useVome'
52
53
  import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
53
54
  import { POPUP_TRIGGER_GAP } from '../lib/dialog-float'
@@ -66,7 +67,7 @@ const props = withDefaults(
66
67
  modelValue: () => [],
67
68
  disabled: false,
68
69
  clearable: true,
69
- placeholder: '请选择角色',
70
+ placeholder: undefined,
70
71
  source: 'base',
71
72
  },
72
73
  )
@@ -92,7 +93,7 @@ const displayText = computed(() => {
92
93
  const names = ids
93
94
  .map((id) => options.value.find((o) => o.id === id)?.name || String(id))
94
95
  .filter(Boolean)
95
- if (!names.length) return `已选 ${ids.length} 项`
96
+ if (!names.length) return crudFormat(crudLabel('selectedCount'), ids.length)
96
97
  if (names.length === 1) return names[0]!
97
98
  return `${names[0]} +${names.length - 1}`
98
99
  })
@@ -2,7 +2,7 @@
2
2
  <vm-group-cascader
3
3
  :model-value="modelValue ?? ''"
4
4
  :tree="tree"
5
- :placeholder="placeholder"
5
+ :placeholder="placeholder ?? crudLabel('pickFilePath')"
6
6
  :clearable="clearable"
7
7
  :editable="editable"
8
8
  :disabled="disabled"
@@ -11,6 +11,7 @@
11
11
  </template>
12
12
 
13
13
  <script setup lang="ts">
14
+ import { crudLabel } from '../crud/style'
14
15
  import { ref, onMounted, onUnmounted } from 'vue'
15
16
  import {
16
17
  listViewsTree,
@@ -28,7 +29,7 @@ withDefaults(
28
29
  disabled?: boolean
29
30
  }>(),
30
31
  {
31
- placeholder: '请选择文件路径',
32
+ placeholder: undefined,
32
33
  clearable: true,
33
34
  editable: true,
34
35
  },