vome-core 0.3.9 → 0.3.11

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 +32 -31
  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
@@ -6,7 +6,7 @@
6
6
  class="vm-preview-viewer__trigger"
7
7
  :style="thumbStyle"
8
8
  :disabled="!preview"
9
- :title="preview ? (isVideoKind ? '播放视频' : '查看大图') : undefined"
9
+ :title="preview ? (isVideoKind ? crudLabel('playVideo') : crudLabel('viewImage')) : undefined"
10
10
  @click="openViewer"
11
11
  >
12
12
  <img
@@ -75,9 +75,9 @@
75
75
  :disabled="opening"
76
76
  @click="openInNewWindow"
77
77
  >
78
- {{ opening ? '打开中…' : '新窗口打开' }}
78
+ {{ opening ? crudLabel('opening') : crudLabel('openNewWindow') }}
79
79
  </button>
80
- <button type="button" class="vm-file-preview-viewer__close" title="关闭" @click="closeViewer">
80
+ <button type="button" class="vm-file-preview-viewer__close" :title="crudLabel('dismiss')" @click="closeViewer">
81
81
  <i class="ri-close-line" />
82
82
  </button>
83
83
  </div>
@@ -87,7 +87,7 @@
87
87
  class="vm-file-preview-viewer__body vm-scroll"
88
88
  :class="{ 'is-media': kind === 'image' || kind === 'video' }"
89
89
  >
90
- <p v-if="loading" class="vm-file-preview-viewer__hint">加载中…</p>
90
+ <p v-if="loading" class="vm-file-preview-viewer__hint">{{ crudLabel('loading') }}</p>
91
91
  <p v-else-if="error" class="vm-file-preview-viewer__hint is-error">{{ error }}</p>
92
92
 
93
93
  <template v-else-if="kind === 'image'">
@@ -102,19 +102,19 @@
102
102
  />
103
103
  </div>
104
104
  <div class="vm-file-preview-viewer__toolbar" @click.stop>
105
- <button type="button" title="缩小" @click="zoomOut">
105
+ <button type="button" :title="crudLabel('zoomOut')" @click="zoomOut">
106
106
  <i class="ri-subtract-line" />
107
107
  </button>
108
- <button type="button" title="放大" @click="zoomIn">
108
+ <button type="button" :title="crudLabel('zoomIn')" @click="zoomIn">
109
109
  <i class="ri-add-line" />
110
110
  </button>
111
- <button type="button" title="原始尺寸" @click="resetView">
111
+ <button type="button" :title="crudLabel('actualSize')" @click="resetView">
112
112
  <i class="ri-aspect-ratio-line" />
113
113
  </button>
114
- <button type="button" title="逆时针旋转" @click="rotateLeft">
114
+ <button type="button" :title="crudLabel('rotateCCW')" @click="rotateLeft">
115
115
  <i class="ri-anticlockwise-line" />
116
116
  </button>
117
- <button type="button" title="顺时针旋转" @click="rotateRight">
117
+ <button type="button" :title="crudLabel('rotateCW')" @click="rotateRight">
118
118
  <i class="ri-clockwise-line" />
119
119
  </button>
120
120
  </div>
@@ -145,7 +145,7 @@
145
145
  v-else-if="kind === 'pdf'"
146
146
  class="vm-file-preview-viewer__iframe"
147
147
  :src="src"
148
- title="PDF 预览"
148
+ :title="crudLabel('pdfPreview')"
149
149
  />
150
150
 
151
151
  <div
@@ -160,29 +160,29 @@
160
160
 
161
161
  <div v-else-if="kind === 'office'" class="vm-file-preview-viewer__fallback">
162
162
  <i class="ri-file-3-line" aria-hidden="true" />
163
- <p>Word / PPT 等 Office 文档暂不支持浏览器内预览。</p>
164
- <p>请点击下方按钮下载后使用本地 Office / WPS 查看。</p>
163
+ <p>{{ crudLabel('officeNoPreview') }}</p>
164
+ <p>{{ crudLabel('officeDownloadHint') }}</p>
165
165
  <button
166
166
  type="button"
167
167
  class="vm-file-preview-viewer__dl-btn"
168
168
  :disabled="downloading"
169
169
  @click="downloadFile"
170
170
  >
171
- {{ downloading ? '下载中…' : '下载文件' }}
171
+ {{ downloading ? crudLabel('downloadingFile') : crudLabel('downloadFile') }}
172
172
  </button>
173
173
  </div>
174
174
 
175
175
  <div v-else class="vm-file-preview-viewer__fallback">
176
176
  <i class="ri-file-3-line" aria-hidden="true" />
177
- <p>该文件类型暂不支持在线预览。</p>
178
- <p>请点击下方按钮下载查看(如 .vome 插件包)。</p>
177
+ <p>{{ crudLabel('typeNoPreview') }}</p>
178
+ <p>{{ crudLabel('downloadHint') }}</p>
179
179
  <button
180
180
  type="button"
181
181
  class="vm-file-preview-viewer__dl-btn"
182
182
  :disabled="downloading"
183
183
  @click="downloadFile"
184
184
  >
185
- {{ downloading ? '下载中…' : '下载文件' }}
185
+ {{ downloading ? crudLabel('downloadingFile') : crudLabel('downloadFile') }}
186
186
  </button>
187
187
  </div>
188
188
  </div>
@@ -191,6 +191,7 @@
191
191
  </template>
192
192
 
193
193
  <script setup lang="ts">
194
+ import { crudLabel } from '../style'
194
195
  import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
195
196
  import DOMPurify from 'dompurify'
196
197
  import { useVideoFrame } from '../../lib/video-frame'
@@ -208,14 +209,14 @@ defineOptions({ name: 'vm-preview-viewer', inheritAttrs: false })
208
209
  const VIDEO_EXT = /\.(mp4|webm|mov|m4v|ogv|ogg)(\?|#|$)/i
209
210
 
210
211
  const KIND_LABEL: Record<MediaPreviewKind, string> = {
211
- image: '图片',
212
- video: '视频',
213
- audio: '音频',
212
+ image: 'typeImage',
213
+ video: 'typeVideo',
214
+ audio: 'typeAudio',
214
215
  pdf: 'PDF',
215
216
  excel: 'Excel',
216
- text: '文本',
217
+ text: 'typeText',
217
218
  office: 'Office',
218
- unsupported: '文件',
219
+ unsupported: 'typeFile',
219
220
  }
220
221
 
221
222
  const props = withDefaults(
@@ -250,7 +251,7 @@ const props = withDefaults(
250
251
  }>(),
251
252
  {
252
253
  embedded: false,
253
- label: '查看',
254
+ label: undefined,
254
255
  trigger: 'auto',
255
256
  type: 'auto',
256
257
  size: 40,
@@ -323,9 +324,9 @@ const kind = computed((): MediaPreviewKind => {
323
324
  })
324
325
 
325
326
  const displayLabel = computed(() => {
326
- if (props.label !== '查看') return props.label
327
- if (kind.value === 'audio') return '播放'
328
- return props.label
327
+ if (props.label != null && props.label !== '') return props.label
328
+ if (kind.value === 'audio') return crudLabel('play')
329
+ return crudLabel('view')
329
330
  })
330
331
 
331
332
  const triggerIcon = computed(() =>
@@ -333,13 +334,16 @@ const triggerIcon = computed(() =>
333
334
  )
334
335
 
335
336
  const triggerTitle = computed(() =>
336
- kind.value === 'audio' ? '播放音频' : '查看文件',
337
+ kind.value === 'audio' ? crudLabel('playAudio') : crudLabel('viewFile'),
337
338
  )
338
339
 
339
- const kindLabel = computed(() => KIND_LABEL[kind.value])
340
+ const kindLabel = computed(() => {
341
+ const key = KIND_LABEL[kind.value]
342
+ return key.startsWith('type') ? crudLabel(key) : key
343
+ })
340
344
 
341
345
  const displayName = computed(() =>
342
- fileDisplayName(props.fileName || src.value, '未命名文件'),
346
+ fileDisplayName(props.fileName || src.value, crudLabel('unnamedFile')),
343
347
  )
344
348
 
345
349
  /** 无法内嵌预览的类型在正文区提供「下载文件」;顶栏统一「新窗口打开」 */
@@ -463,7 +467,7 @@ async function loadPreview() {
463
467
  })
464
468
  }
465
469
  } catch (e) {
466
- error.value = e instanceof Error ? e.message : '预览失败'
470
+ error.value = e instanceof Error ? e.message : crudLabel('previewFail')
467
471
  } finally {
468
472
  loading.value = false
469
473
  }
@@ -1 +1 @@
1
- const _0x17174f=_0x60fa;function _0x60fa(_0x5925c9,_0x59d040){_0x5925c9=_0x5925c9-0x1d3;const _0x458e2d=_0x458e();let _0x60fa64=_0x458e2d[_0x5925c9];if(_0x60fa['vfACNV']===undefined){var _0x29ea89=function(_0x1b33a8){const _0x47e19e='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x5526ed='',_0x2b6c91='';for(let _0x43be8e=0x0,_0x178e27,_0x4bf41b,_0x47fed1=0x0;_0x4bf41b=_0x1b33a8['charAt'](_0x47fed1++);~_0x4bf41b&&(_0x178e27=_0x43be8e%0x4?_0x178e27*0x40+_0x4bf41b:_0x4bf41b,_0x43be8e++%0x4)?_0x5526ed+=String['fromCharCode'](0xff&_0x178e27>>(-0x2*_0x43be8e&0x6)):0x0){_0x4bf41b=_0x47e19e['indexOf'](_0x4bf41b);}for(let _0x1ff823=0x0,_0x5732b8=_0x5526ed['length'];_0x1ff823<_0x5732b8;_0x1ff823++){_0x2b6c91+='%'+('00'+_0x5526ed['charCodeAt'](_0x1ff823)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2b6c91);};_0x60fa['GuSeaE']=_0x29ea89,_0x60fa['HalQpj']={},_0x60fa['vfACNV']=!![];}const _0xfe3735=_0x458e2d[0x0],_0x3f98f4=_0x5925c9+_0xfe3735,_0x4921dd=_0x60fa['HalQpj'][_0x3f98f4];return!_0x4921dd?(_0x60fa64=_0x60fa['GuSeaE'](_0x60fa64),_0x60fa['HalQpj'][_0x3f98f4]=_0x60fa64):_0x60fa64=_0x4921dd,_0x60fa64;}function _0x458e(){const _0x5b93b8=['8j+qU+kaJEkDHo+4JW','y3jLyxrL','odq5odGWr3jAzwD2','Aw5Zzxj0q29UDgvUDa','mtbSDK9KAe0','mZzqqMXqyLq','mtC1mti5nwfAtNfcvW','mJuYmZbbu21VzfO','ndmZndqYoxPwzw5itW','mu1srwzeua','yMXVy2S','BMfTzq','DMLKzw8','odG0mtq0v09Hv0jM','nJm1AKrKB09z','Bwf4lxDPzhrOoJeWmcu7yM9YzgvYlxjHzgL1CZO4ChG','nZy0mdK0BLzTsxb1','mtC1mJG5ohHbwgfiqG'];_0x458e=function(){return _0x5b93b8;};return _0x458e();}(function(_0x5bfb20,_0xdc0d5e){const _0x5d9d3e=_0x60fa,_0x24f74e=_0x5bfb20();while(!![]){try{const _0x16d482=-parseInt(_0x5d9d3e(0x1d9))/0x1*(-parseInt(_0x5d9d3e(0x1e0))/0x2)+-parseInt(_0x5d9d3e(0x1d6))/0x3+-parseInt(_0x5d9d3e(0x1e4))/0x4+parseInt(_0x5d9d3e(0x1de))/0x5*(parseInt(_0x5d9d3e(0x1d7))/0x6)+parseInt(_0x5d9d3e(0x1e1))/0x7+parseInt(_0x5d9d3e(0x1dd))/0x8*(-parseInt(_0x5d9d3e(0x1d5))/0x9)+parseInt(_0x5d9d3e(0x1d4))/0xa*(parseInt(_0x5d9d3e(0x1d8))/0xb);if(_0x16d482===_0xdc0d5e)break;else _0x24f74e['push'](_0x24f74e['shift']());}catch(_0x27a510){_0x24f74e['push'](_0x24f74e['shift']());}}}(_0x458e,0x4eab4));import{Node,mergeAttributes}from'@tiptap/core';export const VmRichtextVideo=Node[_0x17174f(0x1e3)]({'name':'video','group':_0x17174f(0x1da),'atom':!![],'selectable':!![],'draggable':!![],'addAttributes'(){return{'src':{'default':null},'controls':{'default':!![]}};},'parseHTML'(){return[{'tag':'video[src]'}];},'renderHTML'({HTMLAttributes:_0x5526ed}){const _0x2eb143=_0x17174f;return[_0x2eb143(0x1dc),mergeAttributes(_0x5526ed,{'controls':!![],'style':_0x2eb143(0x1df)})];},'addCommands'(){const _0x7c33d7=_0x17174f;return{'setVideo':_0x2b6c91=>({commands:_0x43be8e})=>_0x43be8e[_0x7c33d7(0x1d3)]({'type':this[_0x7c33d7(0x1db)],'attrs':_0x2b6c91})};}});export const VM_RICHTEXT_EMOJIS=['\uD83D\uDE00','\uD83D\uDE03','\uD83D\uDE04','\uD83D\uDE01','\uD83D\uDE06','\uD83D\uDE05','\uD83E\uDD23','\uD83D\uDE02','\uD83D\uDE42','\uD83D\uDE43','\uD83D\uDE09','\uD83D\uDE0A','\uD83D\uDE07','\uD83E\uDD70','\uD83D\uDE0D','\uD83E\uDD29','\uD83D\uDE18','\uD83D\uDE17','\uD83D\uDE1A','\uD83D\uDE19','\uD83D\uDE0B','\uD83D\uDE1B','\uD83D\uDE1C','\uD83E\uDD2A','\uD83D\uDE1D','\uD83E\uDD11','\uD83E\uDD17','\uD83E\uDD2D','\uD83E\uDD2B','\uD83E\uDD14','\uD83E\uDD10','\uD83E\uDD28','\uD83D\uDE10','\uD83D\uDE11','\uD83D\uDE36','\uD83D\uDE0F','\uD83D\uDE12','\uD83D\uDE44','\uD83D\uDE2C','\uD83E\uDD25','\uD83D\uDE0C','\uD83D\uDE14','\uD83D\uDE2A','\uD83E\uDD24','\uD83D\uDE34','\uD83D\uDE37','\uD83E\uDD12','\uD83E\uDD15','\uD83E\uDD22','\uD83E\uDD2E','\uD83E\uDD75','\uD83E\uDD76','\uD83E\uDD74','\uD83D\uDE35','\uD83E\uDD2F','\uD83E\uDD20','\uD83E\uDD73','\uD83D\uDE0E','\uD83E\uDD13','\uD83E\uDDD0','\uD83D\uDE15','\uD83D\uDE1F','\uD83D\uDE41','☹️','\uD83D\uDE2E','\uD83D\uDE2F','\uD83D\uDE32','\uD83D\uDE33','\uD83E\uDD7A','\uD83D\uDE26','\uD83D\uDE27','\uD83D\uDE28','\uD83D\uDE30','\uD83D\uDE25','\uD83D\uDE22','\uD83D\uDE2D','\uD83D\uDE31','\uD83D\uDE16','\uD83D\uDE23','\uD83D\uDE1E','\uD83D\uDE13','\uD83D\uDE29','\uD83D\uDE2B','\uD83E\uDD71','\uD83D\uDE24','\uD83D\uDE21','\uD83D\uDE20','\uD83E\uDD2C','\uD83D\uDE08','\uD83D\uDC7F','\uD83D\uDC4B','\uD83E\uDD1A','\uD83D\uDD90','✋','\uD83D\uDD96','\uD83D\uDC4C','\uD83E\uDD0C','\uD83E\uDD0F','✌️','\uD83E\uDD1E','\uD83E\uDD1F','\uD83E\uDD18','\uD83E\uDD19','\uD83D\uDC48','\uD83D\uDC49','\uD83D\uDC46','\uD83D\uDD95','\uD83D\uDC47','☝️','\uD83D\uDC4D','\uD83D\uDC4E','✊','\uD83D\uDC4A','\uD83E\uDD1B','\uD83E\uDD1C','\uD83D\uDC4F','\uD83D\uDE4C','\uD83D\uDC50','\uD83E\uDD32','\uD83E\uDD1D','\uD83D\uDE4F','\uD83D\uDCAA','\uD83E\uDDBE','✍️','\uD83D\uDC85','\uD83E\uDD33','❤️','\uD83E\uDDE1','\uD83D\uDC9B','\uD83D\uDC9A','\uD83D\uDC99','\uD83D\uDC9C','\uD83D\uDDA4','\uD83E\uDD0D','\uD83E\uDD0E','\uD83D\uDC94','❣️','\uD83D\uDC95','\uD83D\uDC9E','\uD83D\uDC93','\uD83D\uDC97','\uD83D\uDC96','\uD83D\uDC98','\uD83D\uDC9D','\uD83D\uDC9F','☮️','✝️','☪️','\uD83D\uDD49','☸️','✡️','\uD83D\uDD2F','\uD83D\uDD4E','☯️','☦️','\uD83D\uDED0','⭐','\uD83C\uDF1F','✨','⚡','\uD83D\uDD25','\uD83D\uDCA5','☀️','\uD83C\uDF19','⭐️','\uD83C\uDF08','✅','❌','⭕','❗','❓','‼️','⁉️','\uD83D\uDCAF','\uD83D\uDD14','\uD83D\uDD15','\uD83D\uDCCC','\uD83D\uDCCD','\uD83D\uDCCE','\uD83D\uDD17','\uD83D\uDD12','\uD83D\uDD13','\uD83D\uDD11','\uD83D\uDDDD','\uD83D\uDCA1','\uD83D\uDD26','\uD83D\uDCDD','✏️','✒️','\uD83D\uDD8A','\uD83D\uDD8B','\uD83D\uDD8C','\uD83D\uDD8D','\uD83D\uDCBC','\uD83D\uDCC1','\uD83D\uDCC2','\uD83D\uDCC5','\uD83D\uDCC6','\uD83D\uDDD3','\uD83D\uDCC7','\uD83D\uDCC8','\uD83D\uDCC9','\uD83D\uDCCA','\uD83D\uDCCB','\uD83D\uDCE4','\uD83D\uDCE5','\uD83D\uDCE6','\uD83D\uDCEB','\uD83D\uDCEC','\uD83D\uDCED','\uD83D\uDCEE','\uD83D\uDCEF','\uD83D\uDCDC','\uD83D\uDCC3','\uD83D\uDCC4','\uD83D\uDCD1','☕','\uD83C\uDF75','\uD83E\uDDC3','\uD83E\uDD64','\uD83E\uDDCB','\uD83C\uDF7A','\uD83C\uDF7B','\uD83E\uDD42','\uD83C\uDF77','\uD83C\uDF78','\uD83C\uDF55','\uD83C\uDF54','\uD83C\uDF5F','\uD83C\uDF2D','\uD83C\uDF7F','\uD83C\uDF69','\uD83C\uDF6A','\uD83C\uDF82','\uD83C\uDF70','\uD83E\uDDC1','\uD83C\uDF4E','\uD83C\uDF4A','\uD83C\uDF4B','\uD83C\uDF4C','\uD83C\uDF49','\uD83C\uDF47','\uD83C\uDF53','\uD83E\uDED0','\uD83C\uDF52','\uD83C\uDF51','⚽','\uD83C\uDFC0','\uD83C\uDFC8','⚾','\uD83C\uDFBE','\uD83C\uDFD0','\uD83C\uDFC9','\uD83C\uDFB1','\uD83C\uDFD3','\uD83C\uDFF8','\uD83C\uDFAE','\uD83D\uDD79','\uD83C\uDFB2','\uD83E\uDDE9','\uD83C\uDFAF','\uD83C\uDFB3','\uD83C\uDFB0','\uD83C\uDFB8','\uD83C\uDFB9','\uD83E\uDD41','\uD83C\uDFA4','\uD83C\uDFA7','\uD83C\uDFAC','\uD83C\uDFA5','\uD83D\uDCF7','\uD83D\uDCF8','\uD83D\uDCF9','\uD83D\uDCFA','\uD83D\uDCFB','\uD83C\uDF99','\uD83D\uDE80','✈️','\uD83D\uDEE9','\uD83D\uDEEB','\uD83D\uDEEC','\uD83D\uDEF0','\uD83D\uDCBA','\uD83D\uDEF8','\uD83D\uDE81','\uD83D\uDEF6','\uD83D\uDE97','\uD83D\uDE95','\uD83D\uDE99','\uD83D\uDE8C','\uD83D\uDE8E','\uD83C\uDFCE','\uD83D\uDE93','\uD83D\uDE91','\uD83D\uDE92','\uD83D\uDE90','\uD83C\uDFE0','\uD83C\uDFE1','\uD83C\uDFE2','\uD83C\uDFE3','\uD83C\uDFE4','\uD83C\uDFE5','\uD83C\uDFE6','\uD83C\uDFE8','\uD83C\uDFE9','\uD83C\uDFEA','\uD83D\uDC36','\uD83D\uDC31','\uD83D\uDC2D','\uD83D\uDC39','\uD83D\uDC30','\uD83E\uDD8A','\uD83D\uDC3B','\uD83D\uDC3C',_0x17174f(0x1e2),'\uD83D\uDC28','\uD83D\uDC2F','\uD83E\uDD81','\uD83D\uDC2E','\uD83D\uDC37','\uD83D\uDC38','\uD83D\uDC35','\uD83D\uDE48','\uD83D\uDE49','\uD83D\uDE4A','\uD83D\uDC12','\uD83D\uDC14','\uD83D\uDC27','\uD83D\uDC26','\uD83D\uDC24','\uD83D\uDC23','\uD83D\uDC25','\uD83E\uDD86','\uD83E\uDD85','\uD83E\uDD89','\uD83E\uDD87','\uD83C\uDF89','\uD83C\uDF8A','\uD83C\uDF88','\uD83C\uDF81','\uD83C\uDF80','\uD83C\uDF97','\uD83C\uDFAB','\uD83C\uDF9F','\uD83C\uDFC6','\uD83C\uDFC5'];
1
+ function _0x1aa3(_0x2e248a,_0x10e119){_0x2e248a=_0x2e248a-0x104;const _0x4bbe12=_0x4bbe();let _0x1aa38e=_0x4bbe12[_0x2e248a];if(_0x1aa3['zIQVwC']===undefined){var _0x59a641=function(_0x1a99b3){const _0x3769da='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x9b7760='',_0x592ea2='';for(let _0x2bcc99=0x0,_0x281fb0,_0x39db77,_0x57af89=0x0;_0x39db77=_0x1a99b3['charAt'](_0x57af89++);~_0x39db77&&(_0x281fb0=_0x2bcc99%0x4?_0x281fb0*0x40+_0x39db77:_0x39db77,_0x2bcc99++%0x4)?_0x9b7760+=String['fromCharCode'](0xff&_0x281fb0>>(-0x2*_0x2bcc99&0x6)):0x0){_0x39db77=_0x3769da['indexOf'](_0x39db77);}for(let _0x199da8=0x0,_0x5abc51=_0x9b7760['length'];_0x199da8<_0x5abc51;_0x199da8++){_0x592ea2+='%'+('00'+_0x9b7760['charCodeAt'](_0x199da8)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x592ea2);};_0x1aa3['uBjRol']=_0x59a641,_0x1aa3['WaYnUS']={},_0x1aa3['zIQVwC']=!![];}const _0x165e16=_0x4bbe12[0x0],_0x2893dd=_0x2e248a+_0x165e16,_0x566b61=_0x1aa3['WaYnUS'][_0x2893dd];return!_0x566b61?(_0x1aa38e=_0x1aa3['uBjRol'](_0x1aa38e),_0x1aa3['WaYnUS'][_0x2893dd]=_0x1aa38e):_0x1aa38e=_0x566b61,_0x1aa38e;}const _0x58ef28=_0x1aa3;(function(_0x318a1e,_0x3761d8){const _0x3cf43d=_0x1aa3,_0x4b2e0a=_0x318a1e();while(!![]){try{const _0x359861=parseInt(_0x3cf43d(0x10b))/0x1+parseInt(_0x3cf43d(0x10a))/0x2*(parseInt(_0x3cf43d(0x110))/0x3)+-parseInt(_0x3cf43d(0x10f))/0x4*(parseInt(_0x3cf43d(0x111))/0x5)+parseInt(_0x3cf43d(0x114))/0x6+parseInt(_0x3cf43d(0x106))/0x7*(-parseInt(_0x3cf43d(0x10d))/0x8)+-parseInt(_0x3cf43d(0x10e))/0x9*(-parseInt(_0x3cf43d(0x10c))/0xa)+-parseInt(_0x3cf43d(0x108))/0xb*(parseInt(_0x3cf43d(0x112))/0xc);if(_0x359861===_0x3761d8)break;else _0x4b2e0a['push'](_0x4b2e0a['shift']());}catch(_0x292f8f){_0x4b2e0a['push'](_0x4b2e0a['shift']());}}}(_0x4bbe,0x74d8b));import{Node,mergeAttributes}from'@tiptap/core';export const VmRichtextVideo=Node['create']({'name':'video','group':_0x58ef28(0x105),'atom':!![],'selectable':!![],'draggable':!![],'addAttributes'(){return{'src':{'default':null},'controls':{'default':!![]}};},'parseHTML'(){return[{'tag':'video[src]'}];},'renderHTML'({HTMLAttributes:_0x9b7760}){const _0x3099ae=_0x58ef28;return['video',mergeAttributes(_0x9b7760,{'controls':!![],'style':_0x3099ae(0x109)})];},'addCommands'(){const _0x47a060=_0x58ef28;return{'setVideo':_0x592ea2=>({commands:_0x2bcc99})=>_0x2bcc99[_0x47a060(0x107)]({'type':this[_0x47a060(0x104)],'attrs':_0x592ea2})};}});function _0x4bbe(){const _0x21e207=['mJa0mtuWrfvQuLD4','nZmWthjkv0Hx','mtm4ngrjyvzMuG','nJiYmZvxu2LzB2u','nJm3nMXOu2zssW','mJqWodK2mxvqyvHcDW','mtm5mhjbyM9yDq','mtjtB1bOt2u','8j+qU+kaJEkDHo+4JW','nte4ntmWoevhEhnqua','BMfTzq','yMXVy2S','mZGYndH1q1rSvNu','Aw5Zzxj0q29UDgvUDa','ntyWmdu3m3ngv1vszG','Bwf4lxDPzhrOoJeWmcu7yM9YzgvYlxjHzgL1CZO4ChG','mKfkr0DoAq'];_0x4bbe=function(){return _0x21e207;};return _0x4bbe();}export const VM_RICHTEXT_EMOJIS=['\uD83D\uDE00','\uD83D\uDE03','\uD83D\uDE04','\uD83D\uDE01','\uD83D\uDE06','\uD83D\uDE05','\uD83E\uDD23','\uD83D\uDE02','\uD83D\uDE42','\uD83D\uDE43','\uD83D\uDE09','\uD83D\uDE0A','\uD83D\uDE07','\uD83E\uDD70','\uD83D\uDE0D','\uD83E\uDD29','\uD83D\uDE18','\uD83D\uDE17','\uD83D\uDE1A','\uD83D\uDE19','\uD83D\uDE0B','\uD83D\uDE1B','\uD83D\uDE1C','\uD83E\uDD2A','\uD83D\uDE1D','\uD83E\uDD11','\uD83E\uDD17','\uD83E\uDD2D','\uD83E\uDD2B','\uD83E\uDD14','\uD83E\uDD10','\uD83E\uDD28','\uD83D\uDE10','\uD83D\uDE11','\uD83D\uDE36','\uD83D\uDE0F','\uD83D\uDE12','\uD83D\uDE44','\uD83D\uDE2C','\uD83E\uDD25','\uD83D\uDE0C','\uD83D\uDE14','\uD83D\uDE2A','\uD83E\uDD24','\uD83D\uDE34','\uD83D\uDE37','\uD83E\uDD12','\uD83E\uDD15','\uD83E\uDD22','\uD83E\uDD2E','\uD83E\uDD75','\uD83E\uDD76','\uD83E\uDD74','\uD83D\uDE35','\uD83E\uDD2F','\uD83E\uDD20','\uD83E\uDD73','\uD83D\uDE0E','\uD83E\uDD13','\uD83E\uDDD0','\uD83D\uDE15','\uD83D\uDE1F','\uD83D\uDE41','☹️','\uD83D\uDE2E','\uD83D\uDE2F','\uD83D\uDE32','\uD83D\uDE33','\uD83E\uDD7A','\uD83D\uDE26','\uD83D\uDE27','\uD83D\uDE28','\uD83D\uDE30','\uD83D\uDE25','\uD83D\uDE22','\uD83D\uDE2D','\uD83D\uDE31','\uD83D\uDE16','\uD83D\uDE23','\uD83D\uDE1E','\uD83D\uDE13','\uD83D\uDE29','\uD83D\uDE2B','\uD83E\uDD71','\uD83D\uDE24','\uD83D\uDE21','\uD83D\uDE20','\uD83E\uDD2C','\uD83D\uDE08','\uD83D\uDC7F','\uD83D\uDC4B','\uD83E\uDD1A','\uD83D\uDD90','✋','\uD83D\uDD96','\uD83D\uDC4C','\uD83E\uDD0C','\uD83E\uDD0F','✌️','\uD83E\uDD1E','\uD83E\uDD1F','\uD83E\uDD18','\uD83E\uDD19','\uD83D\uDC48','\uD83D\uDC49','\uD83D\uDC46','\uD83D\uDD95','\uD83D\uDC47','☝️','\uD83D\uDC4D','\uD83D\uDC4E','✊','\uD83D\uDC4A','\uD83E\uDD1B','\uD83E\uDD1C','\uD83D\uDC4F','\uD83D\uDE4C','\uD83D\uDC50','\uD83E\uDD32','\uD83E\uDD1D','\uD83D\uDE4F','\uD83D\uDCAA','\uD83E\uDDBE','✍️','\uD83D\uDC85','\uD83E\uDD33','❤️','\uD83E\uDDE1','\uD83D\uDC9B','\uD83D\uDC9A','\uD83D\uDC99','\uD83D\uDC9C','\uD83D\uDDA4','\uD83E\uDD0D','\uD83E\uDD0E','\uD83D\uDC94','❣️','\uD83D\uDC95','\uD83D\uDC9E','\uD83D\uDC93','\uD83D\uDC97','\uD83D\uDC96','\uD83D\uDC98','\uD83D\uDC9D','\uD83D\uDC9F','☮️','✝️','☪️','\uD83D\uDD49','☸️','✡️','\uD83D\uDD2F','\uD83D\uDD4E','☯️','☦️','\uD83D\uDED0','⭐','\uD83C\uDF1F','✨','⚡','\uD83D\uDD25','\uD83D\uDCA5','☀️','\uD83C\uDF19','⭐️','\uD83C\uDF08','✅','❌','⭕','❗','❓','‼️','⁉️','\uD83D\uDCAF','\uD83D\uDD14','\uD83D\uDD15','\uD83D\uDCCC','\uD83D\uDCCD','\uD83D\uDCCE','\uD83D\uDD17','\uD83D\uDD12','\uD83D\uDD13','\uD83D\uDD11','\uD83D\uDDDD','\uD83D\uDCA1','\uD83D\uDD26','\uD83D\uDCDD','✏️','✒️','\uD83D\uDD8A','\uD83D\uDD8B','\uD83D\uDD8C','\uD83D\uDD8D','\uD83D\uDCBC','\uD83D\uDCC1','\uD83D\uDCC2','\uD83D\uDCC5','\uD83D\uDCC6','\uD83D\uDDD3','\uD83D\uDCC7','\uD83D\uDCC8','\uD83D\uDCC9','\uD83D\uDCCA','\uD83D\uDCCB','\uD83D\uDCE4','\uD83D\uDCE5','\uD83D\uDCE6','\uD83D\uDCEB','\uD83D\uDCEC','\uD83D\uDCED','\uD83D\uDCEE','\uD83D\uDCEF','\uD83D\uDCDC','\uD83D\uDCC3','\uD83D\uDCC4','\uD83D\uDCD1','☕','\uD83C\uDF75','\uD83E\uDDC3','\uD83E\uDD64','\uD83E\uDDCB','\uD83C\uDF7A','\uD83C\uDF7B','\uD83E\uDD42','\uD83C\uDF77','\uD83C\uDF78','\uD83C\uDF55','\uD83C\uDF54','\uD83C\uDF5F','\uD83C\uDF2D','\uD83C\uDF7F','\uD83C\uDF69','\uD83C\uDF6A','\uD83C\uDF82','\uD83C\uDF70','\uD83E\uDDC1','\uD83C\uDF4E','\uD83C\uDF4A','\uD83C\uDF4B','\uD83C\uDF4C','\uD83C\uDF49','\uD83C\uDF47','\uD83C\uDF53','\uD83E\uDED0','\uD83C\uDF52','\uD83C\uDF51','⚽','\uD83C\uDFC0','\uD83C\uDFC8','⚾','\uD83C\uDFBE','\uD83C\uDFD0','\uD83C\uDFC9','\uD83C\uDFB1','\uD83C\uDFD3','\uD83C\uDFF8','\uD83C\uDFAE','\uD83D\uDD79','\uD83C\uDFB2','\uD83E\uDDE9','\uD83C\uDFAF','\uD83C\uDFB3','\uD83C\uDFB0','\uD83C\uDFB8','\uD83C\uDFB9','\uD83E\uDD41','\uD83C\uDFA4','\uD83C\uDFA7','\uD83C\uDFAC','\uD83C\uDFA5','\uD83D\uDCF7','\uD83D\uDCF8','\uD83D\uDCF9','\uD83D\uDCFA','\uD83D\uDCFB','\uD83C\uDF99','\uD83D\uDE80','✈️','\uD83D\uDEE9','\uD83D\uDEEB','\uD83D\uDEEC','\uD83D\uDEF0','\uD83D\uDCBA','\uD83D\uDEF8','\uD83D\uDE81','\uD83D\uDEF6','\uD83D\uDE97','\uD83D\uDE95','\uD83D\uDE99','\uD83D\uDE8C','\uD83D\uDE8E','\uD83C\uDFCE','\uD83D\uDE93','\uD83D\uDE91','\uD83D\uDE92','\uD83D\uDE90','\uD83C\uDFE0','\uD83C\uDFE1','\uD83C\uDFE2','\uD83C\uDFE3','\uD83C\uDFE4','\uD83C\uDFE5','\uD83C\uDFE6','\uD83C\uDFE8','\uD83C\uDFE9','\uD83C\uDFEA','\uD83D\uDC36','\uD83D\uDC31','\uD83D\uDC2D','\uD83D\uDC39','\uD83D\uDC30','\uD83E\uDD8A','\uD83D\uDC3B','\uD83D\uDC3C',_0x58ef28(0x113),'\uD83D\uDC28','\uD83D\uDC2F','\uD83E\uDD81','\uD83D\uDC2E','\uD83D\uDC37','\uD83D\uDC38','\uD83D\uDC35','\uD83D\uDE48','\uD83D\uDE49','\uD83D\uDE4A','\uD83D\uDC12','\uD83D\uDC14','\uD83D\uDC27','\uD83D\uDC26','\uD83D\uDC24','\uD83D\uDC23','\uD83D\uDC25','\uD83E\uDD86','\uD83E\uDD85','\uD83E\uDD89','\uD83E\uDD87','\uD83C\uDF89','\uD83C\uDF8A','\uD83C\uDF88','\uD83C\uDF81','\uD83C\uDF80','\uD83C\uDF97','\uD83C\uDFAB','\uD83C\uDF9F','\uD83C\uDFC6','\uD83C\uDFC5'];
@@ -21,38 +21,38 @@
21
21
  v-if="editor"
22
22
  class="vm-richtext__toolbar"
23
23
  >
24
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('bold') }" title="加粗" @click="editor.chain().focus().toggleBold().run()"><i class="ri-bold" /></button>
25
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('italic') }" title="斜体" @click="editor.chain().focus().toggleItalic().run()"><i class="ri-italic" /></button>
26
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('underline') }" title="下划线" @click="editor.chain().focus().toggleUnderline().run()"><i class="ri-underline" /></button>
27
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('strike') }" title="删除线" @click="editor.chain().focus().toggleStrike().run()"><i class="ri-strikethrough" /></button>
28
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('highlight') }" title="高亮" @click="editor.chain().focus().toggleHighlight().run()"><i class="ri-mark-pen-line" /></button>
24
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('bold') }" :title="crudLabel('rtBold')" @click="editor.chain().focus().toggleBold().run()"><i class="ri-bold" /></button>
25
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('italic') }" :title="crudLabel('rtItalic')" @click="editor.chain().focus().toggleItalic().run()"><i class="ri-italic" /></button>
26
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('underline') }" :title="crudLabel('rtUnderline')" @click="editor.chain().focus().toggleUnderline().run()"><i class="ri-underline" /></button>
27
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('strike') }" :title="crudLabel('rtStrike')" @click="editor.chain().focus().toggleStrike().run()"><i class="ri-strikethrough" /></button>
28
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('highlight') }" :title="crudLabel('rtHighlight')" @click="editor.chain().focus().toggleHighlight().run()"><i class="ri-mark-pen-line" /></button>
29
29
  <span class="vm-richtext__sep" />
30
- <button type="button" class="vm-richtext__btn" title="文字颜色" @click="pickTextColor"><i class="ri-font-color" /></button>
30
+ <button type="button" class="vm-richtext__btn" :title="crudLabel('rtTextColor')" @click="pickTextColor"><i class="ri-font-color" /></button>
31
31
  <span class="vm-richtext__sep" />
32
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }" title="标题 2" @click="editor.chain().focus().toggleHeading({ level: 2 }).run()">H2</button>
33
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }" title="标题 3" @click="editor.chain().focus().toggleHeading({ level: 3 }).run()">H3</button>
34
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('paragraph') }" title="正文" @click="editor.chain().focus().setParagraph().run()">P</button>
32
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }" :title="crudLabel('rtH2')" @click="editor.chain().focus().toggleHeading({ level: 2 }).run()">H2</button>
33
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }" :title="crudLabel('rtH3')" @click="editor.chain().focus().toggleHeading({ level: 3 }).run()">H3</button>
34
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('paragraph') }" :title="crudLabel('rtParagraph')" @click="editor.chain().focus().setParagraph().run()">P</button>
35
35
  <span class="vm-richtext__sep" />
36
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive({ textAlign: 'left' }) }" title="左对齐" @click="editor.chain().focus().setTextAlign('left').run()"><i class="ri-align-left" /></button>
37
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive({ textAlign: 'center' }) }" title="居中" @click="editor.chain().focus().setTextAlign('center').run()"><i class="ri-align-center" /></button>
38
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }" title="右对齐" @click="editor.chain().focus().setTextAlign('right').run()"><i class="ri-align-right" /></button>
36
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive({ textAlign: 'left' }) }" :title="crudLabel('rtAlignLeft')" @click="editor.chain().focus().setTextAlign('left').run()"><i class="ri-align-left" /></button>
37
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive({ textAlign: 'center' }) }" :title="crudLabel('rtAlignCenter')" @click="editor.chain().focus().setTextAlign('center').run()"><i class="ri-align-center" /></button>
38
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive({ textAlign: 'right' }) }" :title="crudLabel('rtAlignRight')" @click="editor.chain().focus().setTextAlign('right').run()"><i class="ri-align-right" /></button>
39
39
  <span class="vm-richtext__sep" />
40
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('bulletList') }" title="无序列表" @click="editor.chain().focus().toggleBulletList().run()"><i class="ri-list-unordered" /></button>
41
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('orderedList') }" title="有序列表" @click="editor.chain().focus().toggleOrderedList().run()"><i class="ri-list-ordered" /></button>
42
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('taskList') }" title="任务列表" @click="editor.chain().focus().toggleTaskList().run()"><i class="ri-checkbox-line" /></button>
43
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('blockquote') }" title="引用" @click="editor.chain().focus().toggleBlockquote().run()"><i class="ri-double-quotes-l" /></button>
44
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('codeBlock') }" title="代码块" @click="editor.chain().focus().toggleCodeBlock().run()"><i class="ri-code-box-line" /></button>
45
- <button type="button" class="vm-richtext__btn" title="分割线" @click="editor.chain().focus().setHorizontalRule().run()"><i class="ri-separator" /></button>
40
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('bulletList') }" :title="crudLabel('rtBulletList')" @click="editor.chain().focus().toggleBulletList().run()"><i class="ri-list-unordered" /></button>
41
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('orderedList') }" :title="crudLabel('rtOrderedList')" @click="editor.chain().focus().toggleOrderedList().run()"><i class="ri-list-ordered" /></button>
42
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('taskList') }" :title="crudLabel('rtTaskList')" @click="editor.chain().focus().toggleTaskList().run()"><i class="ri-checkbox-line" /></button>
43
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('blockquote') }" :title="crudLabel('rtBlockquote')" @click="editor.chain().focus().toggleBlockquote().run()"><i class="ri-double-quotes-l" /></button>
44
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('codeBlock') }" :title="crudLabel('rtCodeBlock')" @click="editor.chain().focus().toggleCodeBlock().run()"><i class="ri-code-box-line" /></button>
45
+ <button type="button" class="vm-richtext__btn" :title="crudLabel('rtHr')" @click="editor.chain().focus().setHorizontalRule().run()"><i class="ri-separator" /></button>
46
46
  <span class="vm-richtext__sep" />
47
- <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('link') }" title="链接" @click="toggleLink"><i class="ri-link" /></button>
48
- <button v-if="uploadEnabled" type="button" class="vm-richtext__btn" title="图片" @click="pickImage"><i class="ri-image-line" /></button>
49
- <button v-if="uploadEnabled" type="button" class="vm-richtext__btn" title="上传视频" @click="pickVideo"><i class="ri-video-line" /></button>
47
+ <button type="button" class="vm-richtext__btn" :class="{ 'is-active': editor.isActive('link') }" :title="crudLabel('rtLink')" @click="toggleLink"><i class="ri-link" /></button>
48
+ <button v-if="uploadEnabled" type="button" class="vm-richtext__btn" :title="crudLabel('rtImage')" @click="pickImage"><i class="ri-image-line" /></button>
49
+ <button v-if="uploadEnabled" type="button" class="vm-richtext__btn" :title="crudLabel('rtVideo')" @click="pickVideo"><i class="ri-video-line" /></button>
50
50
  <button
51
51
  ref="emojiBtnRef"
52
52
  type="button"
53
53
  class="vm-richtext__btn"
54
54
  :class="{ 'is-active': emojiOpen }"
55
- title="表情"
55
+ :title="crudLabel('rtEmoji')"
56
56
  @click="toggleEmoji"
57
57
  >
58
58
  <i class="ri-emotion-line" />
@@ -78,9 +78,9 @@
78
78
  </div>
79
79
  </Teleport>
80
80
  <span class="vm-richtext__sep" />
81
- <button type="button" class="vm-richtext__btn" title="插入表格" @click="insertTable"><i class="ri-table-line" /></button>
82
- <button type="button" class="vm-richtext__btn" title="撤销" :disabled="!editor.can().undo()" @click="editor.chain().focus().undo().run()"><i class="ri-arrow-go-back-line" /></button>
83
- <button type="button" class="vm-richtext__btn" title="重做" :disabled="!editor.can().redo()" @click="editor.chain().focus().redo().run()"><i class="ri-arrow-go-forward-line" /></button>
81
+ <button type="button" class="vm-richtext__btn" :title="crudLabel('rtTable')" @click="insertTable"><i class="ri-table-line" /></button>
82
+ <button type="button" class="vm-richtext__btn" :title="crudLabel('rtUndo')" :disabled="!editor.can().undo()" @click="editor.chain().focus().undo().run()"><i class="ri-arrow-go-back-line" /></button>
83
+ <button type="button" class="vm-richtext__btn" :title="crudLabel('rtRedo')" :disabled="!editor.can().redo()" @click="editor.chain().focus().redo().run()"><i class="ri-arrow-go-forward-line" /></button>
84
84
  </div>
85
85
 
86
86
  <EditorContent class="vm-richtext__content" :editor="editor" />
@@ -89,6 +89,7 @@
89
89
  </template>
90
90
 
91
91
  <script setup lang="ts">
92
+ import { crudLabel } from '../style'
92
93
  import { computed, nextTick, onBeforeUnmount, onMounted, shallowRef, watch } from 'vue'
93
94
  import DOMPurify from 'dompurify'
94
95
  import { EditorContent, useEditor } from '@tiptap/vue-3'
@@ -132,7 +133,7 @@ const props = withDefaults(
132
133
  }>(),
133
134
  {
134
135
  readonly: false,
135
- placeholder: '请输入内容…',
136
+ placeholder: undefined,
136
137
  height: 320,
137
138
  uploadPrefixPath: 'app/public/richtext',
138
139
  autoUpload: true,
@@ -177,7 +178,7 @@ const safeHtml = computed(() => sanitize(String(props.modelValue || '')))
177
178
 
178
179
  async function resolveUpload(file: File): Promise<string> {
179
180
  if (props.uploadImage) return props.uploadImage(file)
180
- if (props.autoUpload === false) throw new Error('未配置 uploadImage')
181
+ if (props.autoUpload === false) throw new Error(crudLabel('noUploadImage'))
181
182
  const res = await toUpload(file, { prefixPath: props.uploadPrefixPath })
182
183
  return res.url
183
184
  }
@@ -286,7 +287,7 @@ function pickTextColor() {
286
287
  const ed = editor.value
287
288
  if (!ed) return
288
289
  const current = (ed.getAttributes('textStyle').color as string | undefined) || '#111827'
289
- const color = window.prompt('文字颜色(#RRGGBB)', current)
290
+ const color = window.prompt(crudLabel('textColorPrompt'), current)
290
291
  if (color == null) return
291
292
  const v = color.trim()
292
293
  if (!v) {
@@ -304,7 +305,7 @@ function toggleLink() {
304
305
  ed.chain().focus().unsetLink().run()
305
306
  return
306
307
  }
307
- const url = window.prompt('链接地址', prev || 'https://')
308
+ const url = window.prompt(crudLabel('linkUrlPrompt'), prev || 'https://')
308
309
  if (url == null) return
309
310
  const href = url.trim()
310
311
  if (!href) {
@@ -376,7 +377,9 @@ const editor = useEditor({
376
377
  TableRow,
377
378
  TableHeader,
378
379
  TableCell,
379
- Placeholder.configure({ placeholder: props.placeholder }),
380
+ Placeholder.configure({
381
+ placeholder: props.placeholder ?? crudLabel('richtextPlaceholder'),
382
+ }),
380
383
  ],
381
384
  editorProps: {
382
385
  attributes: { class: 'vm-richtext__prose' },
@@ -17,7 +17,7 @@
17
17
  class="vm-select__label"
18
18
  :class="{ 'is-placeholder': !displayLabel }"
19
19
  >
20
- {{ displayLabel || placeholder || '请选择' }}
20
+ {{ displayLabel || placeholder || crudLabel('pleaseSelect') }}
21
21
  </span>
22
22
  <i
23
23
  v-if="clearable && hasValue"
@@ -48,7 +48,7 @@
48
48
  ref="searchRef"
49
49
  class="vm-select__search-input"
50
50
  :value="search"
51
- placeholder="搜索…"
51
+ :placeholder="crudLabel('searchEllipsis')"
52
52
  @input="onSearchInput"
53
53
  @keydown.esc.stop="closePanel"
54
54
  />
@@ -74,7 +74,7 @@
74
74
  mode="footer"
75
75
  status="loading"
76
76
  />
77
- <p v-else-if="!filtered.length" class="vm-select__empty">无匹配选项</p>
77
+ <p v-else-if="!filtered.length" class="vm-select__empty">{{ crudLabel('noMatch') }}</p>
78
78
  <vm-loading
79
79
  v-else-if="lazy && !hasMore"
80
80
  mode="footer"
@@ -88,6 +88,7 @@
88
88
  </template>
89
89
 
90
90
  <script setup lang="ts">
91
+ import { crudLabel } from '../style'
91
92
  import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
92
93
  import { FocusScope } from 'reka-ui'
93
94
  import { useCrud } from '../useCrud'
@@ -15,6 +15,7 @@
15
15
  </template>
16
16
 
17
17
  <script setup lang="ts">
18
+ import { crudLabel } from '../style'
18
19
  import { computed } from 'vue'
19
20
 
20
21
  defineOptions({ name: 'vm-status-tag' })
@@ -66,7 +67,7 @@ function resolveQueue(v: unknown): { variant: Variant; label: string } {
66
67
 
67
68
  function resolveTask(v: unknown): { variant: Variant; label: string } {
68
69
  const on = Number(v) === 1
69
- const label = props.label ?? (on ? '运行中' : '已停止')
70
+ const label = props.label ?? (on ? crudLabel('running') : crudLabel('stopped'))
70
71
  return { variant: on ? 'success' : 'neutral', label }
71
72
  }
72
73
 
@@ -13,6 +13,7 @@
13
13
  </template>
14
14
 
15
15
  <script setup lang="ts">
16
+ import { crudLabel } from '../style'
16
17
  import { ref, computed, watch } from 'vue'
17
18
  import { toast } from 'vue-sonner'
18
19
  import { vmConfirm } from '../confirm'
@@ -59,7 +60,7 @@ watch(
59
60
  },
60
61
  )
61
62
 
62
- /** options 按 value 排序:小=关、大=开(status 0/1、yes_no false/true) */
63
+ /** options 按 value 排序:小=关、大=开(status/yes_no_number 0|1、yes_no_boolean false|true) */
63
64
  const pair = computed(() => {
64
65
  const opts = props.options
65
66
  if (opts && opts.length >= 2) {
@@ -90,7 +91,7 @@ const on = computed(
90
91
  async function toggle() {
91
92
  if (loading.value || props.disabled) return
92
93
  const next = on.value ? pair.value.inactive : pair.value.active
93
- if (props.check && !(await vmConfirm('确定切换状态?'))) return
94
+ if (props.check && !(await vmConfirm(crudLabel('switchConfirm')))) return
94
95
 
95
96
  const prev = status.value
96
97
  status.value = next
@@ -104,11 +105,11 @@ async function toggle() {
104
105
  const params = { id: props.scope.id, [prop]: next }
105
106
  if (props.api) await props.api(params)
106
107
  else if (crud.service.update) await crud.service.update(params)
107
- else throw new Error('无 update')
108
+ else throw new Error(crudLabel('noUpdateApi'))
108
109
  } catch (e) {
109
110
  status.value = prev
110
111
  emit('update:modelValue', prev as number | string | boolean)
111
- toast.error(e instanceof Error ? e.message : '更新失败')
112
+ toast.error(e instanceof Error ? e.message : crudLabel('updateFailed'))
112
113
  } finally {
113
114
  loading.value = false
114
115
  }
@@ -5,11 +5,12 @@
5
5
  :disabled="disabled"
6
6
  @click="emit('click', $event)"
7
7
  >
8
- <slot>{{ label }}</slot>
8
+ <slot>{{ label ?? crudLabel('view') }}</slot>
9
9
  </button>
10
10
  </template>
11
11
 
12
12
  <script setup lang="ts">
13
+ import { crudLabel } from '../style'
13
14
  defineOptions({ name: 'vm-text-link' })
14
15
 
15
16
  withDefaults(
@@ -18,7 +19,7 @@ withDefaults(
18
19
  disabled?: boolean
19
20
  }>(),
20
21
  {
21
- label: '查看',
22
+ label: undefined,
22
23
  disabled: false,
23
24
  },
24
25
  )
@@ -9,7 +9,7 @@
9
9
  @click="toggle"
10
10
  >
11
11
  <span v-if="displayLabel" class="vm-tree-select__value">{{ displayLabel }}</span>
12
- <span v-else class="vm-tree-select__ph">{{ placeholder || '请选择' }}</span>
12
+ <span v-else class="vm-tree-select__ph">{{ placeholder || crudLabel('pleaseSelect') }}</span>
13
13
  <i
14
14
  v-if="clearable && hasValue"
15
15
  class="ri-close-circle-fill vm-tree-select__clear"
@@ -46,13 +46,14 @@
46
46
  {{ row.label }}
47
47
  </button>
48
48
  </div>
49
- <p v-if="!visibleRows.length" class="vm-tree-select__empty">暂无数据</p>
49
+ <p v-if="!visibleRows.length" class="vm-tree-select__empty">{{ crudLabel('empty') }}</p>
50
50
  </div>
51
51
  </Teleport>
52
52
  </div>
53
53
  </template>
54
54
 
55
55
  <script setup lang="ts">
56
+ import { crudLabel } from '../style'
56
57
  import { ref, computed, watch, nextTick, onMounted, onUnmounted } from 'vue'
57
58
  import { POPUP_TRIGGER_GAP } from '../../lib/dialog-float'
58
59
  import type { TreeSelectOption } from '../../../../typings/admin/comm/crud'
@@ -35,7 +35,7 @@
35
35
  <i class="vm-upload-item__file-icon" :class="fileIcon" aria-hidden="true" />
36
36
  <span class="vm-upload-item__name">{{ item.name || item.url }}</span>
37
37
  <span v-if="item.error || imgBroken" class="vm-upload-item__error">
38
- {{ item.error || '加载失败' }}
38
+ {{ item.error || crudLabel('loadFailed') }}
39
39
  </span>
40
40
  </div>
41
41
  </template>
@@ -59,12 +59,12 @@
59
59
  <div v-if="previewSrc" class="vm-upload-item__actions">
60
60
  <button
61
61
  type="button"
62
- title="预览"
62
+ :title="crudLabel('preview')"
63
63
  @click.stop="previewOpen = true"
64
64
  >
65
65
  <i class="ri-zoom-in-line" />
66
66
  </button>
67
- <button v-if="!disabled" type="button" title="删除" @click.stop="emit('remove')">
67
+ <button v-if="!disabled" type="button" :title="crudLabel('delete')" @click.stop="emit('remove')">
68
68
  <i class="ri-delete-bin-line" />
69
69
  </button>
70
70
  </div>
@@ -81,6 +81,8 @@
81
81
  </template>
82
82
 
83
83
  <script setup lang="ts">
84
+ import { uploadRuleLabel } from '../../lib/upload'
85
+ import { crudLabel } from '../style'
84
86
  import { ref, computed, watch } from 'vue'
85
87
  import { useVideoFrame } from '../../lib/video-frame'
86
88
  import { extname, getUploadRule } from '../../lib/upload'
@@ -128,7 +130,7 @@ const tag = computed(() => {
128
130
  const rule = getUploadRule(props.item.type)
129
131
  return {
130
132
  color: rule.color,
131
- label: extname(props.item.name || props.item.url) || rule.name,
133
+ label: extname(props.item.name || props.item.url) || uploadRuleLabel(rule),
132
134
  }
133
135
  })
134
136
 
@@ -74,7 +74,7 @@
74
74
  <slot>
75
75
  <div v-if="drag" class="vm-upload__drag">
76
76
  <i class="ri-upload-cloud-2-line" />
77
- <span>点击或拖拽上传,限制 {{ limitSize }}MB</span>
77
+ <span>{{ crudFormat(crudLabel('uploadHint'), limitSize) }}</span>
78
78
  </div>
79
79
  <div v-else class="vm-upload__demo">
80
80
  <i class="ri-image-add-line" />
@@ -87,6 +87,7 @@
87
87
  </template>
88
88
 
89
89
  <script setup lang="ts">
90
+ import { crudFormat, crudLabel } from '../style'
90
91
  import { ref, computed, watch, reactive } from 'vue'
91
92
  import { toast } from 'vue-sonner'
92
93
  import { useUpload } from '../../hooks/useUpload'
@@ -153,7 +154,7 @@ const acceptValue = computed(() => props.accept ?? '')
153
154
 
154
155
  const text = computed(() => {
155
156
  if (props.text) return props.text
156
- return props.type === 'file' ? '选择文件' : '选择图片'
157
+ return props.type === 'file' ? crudLabel('selectFile') : crudLabel('selectImage')
157
158
  })
158
159
 
159
160
  const itemStyle = computed(() => {
@@ -260,7 +261,7 @@ async function onDrop(e: DragEvent) {
260
261
  async function ingestFiles(files: File[]) {
261
262
  for (const file of files) {
262
263
  if (!isAdd.value) {
263
- toast.warning(`最多上传 ${props.limit} 个文件`)
264
+ toast.warning(crudFormat(crudLabel('maxUpload'), props.limit))
264
265
  break
265
266
  }
266
267
  await appendFile(file)
@@ -269,7 +270,7 @@ async function ingestFiles(files: File[]) {
269
270
 
270
271
  async function appendFile(raw: File) {
271
272
  if (raw.size / 1024 / 1024 >= props.limitSize) {
272
- toast.error(`文件不能超过 ${props.limitSize}MB`)
273
+ toast.error(crudFormat(crudLabel('fileTooLarge'), props.limitSize))
273
274
  return
274
275
  }
275
276
  if (props.beforeUpload) {
@@ -330,7 +331,7 @@ async function appendFile(raw: File) {
330
331
  emitValue()
331
332
  } catch (e) {
332
333
  item.progress = -1
333
- item.error = e instanceof Error ? e.message : '上传失败'
334
+ item.error = e instanceof Error ? e.message : crudLabel('uploadFailed')
334
335
  emit('error', item)
335
336
  }
336
337
  }