@liujitcn/kratos-admin-core 0.0.19 → 0.0.21

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 (110) hide show
  1. package/README.md +2 -2
  2. package/dist/package/declarations/src/api/base/language.d.ts +8 -0
  3. package/dist/package/declarations/src/components/Dialog/ProDialog.vue.d.ts +1 -0
  4. package/dist/package/declarations/src/components/LocaleSwitch/index.vue.d.ts +3 -0
  5. package/dist/package/declarations/src/index.d.ts +2 -0
  6. package/dist/package/declarations/src/locales/index.d.ts +63 -0
  7. package/dist/package/declarations/src/modules/index.d.ts +7 -2
  8. package/dist/package/declarations/src/request.d.ts +1 -0
  9. package/dist/package/declarations/src/routers/modules/staticRouter.d.ts +1 -0
  10. package/dist/package/declarations/src/rpc/base/v1/config.d.ts +2 -0
  11. package/dist/package/declarations/src/rpc/base/v1/language.d.ts +26 -0
  12. package/dist/package/declarations/src/rpc/system/admin/v1/base_dict.d.ts +79 -0
  13. package/dist/package/declarations/src/rpc/system/admin/v1/base_translation.d.ts +102 -0
  14. package/dist/package/declarations/src/stores/interface/index.d.ts +2 -0
  15. package/dist/package/declarations/src/stores/runtime.d.ts +1 -0
  16. package/dist/package/declarations/src/utils/dict.d.ts +2 -2
  17. package/dist/package/declarations/src/utils/index.d.ts +1 -1
  18. package/dist/package/declarations/src/utils/oauthProvider.d.ts +2 -0
  19. package/dist/package/declarations/src/utils/passwordStrength.d.ts +0 -7
  20. package/dist/package/src/App.vue +10 -1
  21. package/dist/package/src/api/base/language.ts +20 -0
  22. package/dist/package/src/api/base/oauth.ts +0 -1
  23. package/dist/package/src/api/system/auth.ts +0 -1
  24. package/dist/package/src/bootstrap.ts +57 -2
  25. package/dist/package/src/components/CronExpression/index.vue +80 -82
  26. package/dist/package/src/components/Dialog/FormDialog.vue +9 -4
  27. package/dist/package/src/components/Dialog/ProDialog.vue +10 -4
  28. package/dist/package/src/components/Dict/index.vue +5 -2
  29. package/dist/package/src/components/ECharts/config/index.ts +6 -1
  30. package/dist/package/src/components/ErrorMessage/index.vue +3 -1
  31. package/dist/package/src/components/ImportExcel/index.vue +29 -18
  32. package/dist/package/src/components/LocaleSwitch/index.vue +28 -0
  33. package/dist/package/src/components/PasswordStrength/index.vue +8 -5
  34. package/dist/package/src/components/ProForm/components/DynamicList.vue +8 -3
  35. package/dist/package/src/components/ProForm/components/KvList.vue +14 -6
  36. package/dist/package/src/components/ProTable/components/ColSetting.vue +7 -5
  37. package/dist/package/src/components/ProTable/components/TableColumn.vue +2 -1
  38. package/dist/package/src/components/ProTable/index.vue +36 -11
  39. package/dist/package/src/components/SearchForm/components/SearchFormItem.vue +9 -4
  40. package/dist/package/src/components/SearchForm/index.vue +6 -3
  41. package/dist/package/src/components/SelectFilter/index.vue +4 -1
  42. package/dist/package/src/components/SelectIcon/index.vue +8 -4
  43. package/dist/package/src/components/TreeFilter/index.vue +14 -6
  44. package/dist/package/src/components/Upload/File.vue +18 -13
  45. package/dist/package/src/components/Upload/Files.vue +22 -15
  46. package/dist/package/src/components/Upload/Img.vue +14 -11
  47. package/dist/package/src/components/Upload/Imgs.vue +15 -12
  48. package/dist/package/src/components/WangEditor/index.vue +12 -4
  49. package/dist/package/src/directives/modules/copy.ts +2 -1
  50. package/dist/package/src/hooks/useDownload.ts +3 -2
  51. package/dist/package/src/hooks/useHandleData.ts +5 -4
  52. package/dist/package/src/hooks/useTheme.ts +2 -1
  53. package/dist/package/src/hooks/useTime.ts +11 -2
  54. package/dist/package/src/index.ts +2 -0
  55. package/dist/package/src/layouts/components/Header/ToolBarRight.vue +2 -0
  56. package/dist/package/src/layouts/components/Header/components/AssemblySize.vue +8 -6
  57. package/dist/package/src/layouts/components/Header/components/Avatar.vue +11 -9
  58. package/dist/package/src/layouts/components/Header/components/Breadcrumb.vue +10 -2
  59. package/dist/package/src/layouts/components/Header/components/Fullscreen.vue +4 -2
  60. package/dist/package/src/layouts/components/Header/components/InfoDialog.vue +6 -3
  61. package/dist/package/src/layouts/components/Header/components/PasswordDialog.vue +6 -3
  62. package/dist/package/src/layouts/components/Header/components/SearchMenu.vue +5 -3
  63. package/dist/package/src/layouts/components/Header/components/ThemeSetting.vue +3 -1
  64. package/dist/package/src/layouts/components/Tabs/components/MoreButton.vue +9 -7
  65. package/dist/package/src/layouts/components/ThemeDrawer/index.vue +27 -24
  66. package/dist/package/src/locales/README.md +15 -0
  67. package/dist/package/src/locales/en-US.json +258 -0
  68. package/dist/package/src/locales/es-ES.json +258 -0
  69. package/dist/package/src/locales/fr-FR.json +258 -0
  70. package/dist/package/src/locales/index.ts +223 -0
  71. package/dist/package/src/locales/ja-JP.json +258 -0
  72. package/dist/package/src/locales/ko-KR.json +258 -0
  73. package/dist/package/src/locales/zh-CN.json +258 -0
  74. package/dist/package/src/locales/zh-TW.json +258 -0
  75. package/dist/package/src/modules/index.ts +7 -2
  76. package/dist/package/src/modules/kratosAdmin.ts +19 -1
  77. package/dist/package/src/request.ts +8 -1
  78. package/dist/package/src/routers/index.ts +4 -1
  79. package/dist/package/src/routers/modules/dynamicRouter.ts +3 -2
  80. package/dist/package/src/routers/modules/staticRouter.ts +8 -4
  81. package/dist/package/src/rpc/base/v1/config.ts +2 -0
  82. package/dist/package/src/rpc/base/v1/language.ts +37 -0
  83. package/dist/package/src/rpc/system/admin/v1/base_dict.ts +81 -0
  84. package/dist/package/src/rpc/system/admin/v1/base_translation.ts +117 -0
  85. package/dist/package/src/stores/interface/index.ts +2 -0
  86. package/dist/package/src/stores/modules/config.ts +5 -1
  87. package/dist/package/src/stores/modules/user.ts +2 -1
  88. package/dist/package/src/stores/runtime.ts +1 -0
  89. package/dist/package/src/utils/color.ts +5 -4
  90. package/dist/package/src/utils/dict.ts +27 -4
  91. package/dist/package/src/utils/eleValidate.ts +3 -2
  92. package/dist/package/src/utils/errorHandler.ts +9 -8
  93. package/dist/package/src/utils/index.ts +7 -6
  94. package/dist/package/src/utils/oauthProvider.ts +15 -10
  95. package/dist/package/src/utils/passwordCrypto.ts +3 -2
  96. package/dist/package/src/utils/passwordStrength.ts +1 -16
  97. package/dist/package/src/utils/request.ts +13 -8
  98. package/dist/package/src/views/error/403.vue +3 -1
  99. package/dist/package/src/views/error/404.vue +3 -1
  100. package/dist/package/src/views/error/500.vue +3 -1
  101. package/dist/package/src/views/error/pending.vue +5 -2
  102. package/dist/package/src/views/login/components/LoginForm.vue +53 -27
  103. package/dist/package/src/views/login/index.scss +6 -0
  104. package/dist/package/src/views/login/index.vue +2 -0
  105. package/dist/package/types/generated/auto-imports.d.ts +129 -111
  106. package/dist/package/types/generated/components.d.ts +67 -67
  107. package/package.json +3 -1
  108. package/types/generated/auto-imports.d.ts +129 -111
  109. package/types/generated/components.d.ts +67 -67
  110. package/vite.config.ts +8 -0
@@ -3,7 +3,7 @@
3
3
  <el-input v-model="currentValue" :placeholder="placeholder" clearable>
4
4
  <template #suffix>
5
5
  <div class="cron-expression__actions">
6
- <el-tooltip content="编辑表达式" placement="top">
6
+ <el-tooltip :content="t('core.cron.edit')" placement="top">
7
7
  <el-icon class="cron-expression__icon" @click="handleOpenEditor()">
8
8
  <Operation />
9
9
  </el-icon>
@@ -12,10 +12,10 @@
12
12
  </template>
13
13
  </el-input>
14
14
 
15
- <el-dialog v-model="dialogVisible" title="编辑定时表达式" width="980px" top="2vh" destroy-on-close>
15
+ <el-dialog v-model="dialogVisible" :title="t('core.cron.title')" width="980px" top="2vh" destroy-on-close>
16
16
  <div class="cron-editor">
17
17
  <div class="cron-editor__preset">
18
- <div class="cron-editor__section-title">常用表达式</div>
18
+ <div class="cron-editor__section-title">{{ t("core.cron.presets") }}</div>
19
19
  <div class="cron-editor__preset-list">
20
20
  <el-tag
21
21
  v-for="option in presetOptions"
@@ -30,36 +30,36 @@
30
30
  </div>
31
31
 
32
32
  <el-tabs v-model="activeTab">
33
- <el-tab-pane label="" name="second">
33
+ <el-tab-pane :label="t('core.cron.segment.second')" name="second">
34
34
  <CronSegmentEditor
35
- unit=""
35
+ unit-key="second"
36
36
  :max="59"
37
37
  :state="editorState.second"
38
38
  :supports-unspecified="false"
39
39
  @change="value => handleUpdateSegment('second', value)"
40
40
  />
41
41
  </el-tab-pane>
42
- <el-tab-pane label="" name="minute">
42
+ <el-tab-pane :label="t('core.cron.segment.minute')" name="minute">
43
43
  <CronSegmentEditor
44
- unit=""
44
+ unit-key="minute"
45
45
  :max="59"
46
46
  :state="editorState.minute"
47
47
  :supports-unspecified="false"
48
48
  @change="value => handleUpdateSegment('minute', value)"
49
49
  />
50
50
  </el-tab-pane>
51
- <el-tab-pane label="" name="hour">
51
+ <el-tab-pane :label="t('core.cron.segment.hour')" name="hour">
52
52
  <CronSegmentEditor
53
- unit=""
53
+ unit-key="hour"
54
54
  :max="23"
55
55
  :state="editorState.hour"
56
56
  :supports-unspecified="false"
57
57
  @change="value => handleUpdateSegment('hour', value)"
58
58
  />
59
59
  </el-tab-pane>
60
- <el-tab-pane label="" name="day">
60
+ <el-tab-pane :label="t('core.cron.segment.day')" name="day">
61
61
  <CronSegmentEditor
62
- unit=""
62
+ unit-key="day"
63
63
  :min="1"
64
64
  :max="31"
65
65
  :state="editorState.day"
@@ -69,9 +69,9 @@
69
69
  @change="value => handleUpdateSegment('day', value)"
70
70
  />
71
71
  </el-tab-pane>
72
- <el-tab-pane label="" name="month">
72
+ <el-tab-pane :label="t('core.cron.segment.month')" name="month">
73
73
  <CronSegmentEditor
74
- unit=""
74
+ unit-key="month"
75
75
  :min="1"
76
76
  :max="12"
77
77
  :state="editorState.month"
@@ -79,9 +79,9 @@
79
79
  @change="value => handleUpdateSegment('month', value)"
80
80
  />
81
81
  </el-tab-pane>
82
- <el-tab-pane label="" name="year">
82
+ <el-tab-pane :label="t('core.cron.segment.year')" name="year">
83
83
  <CronSegmentEditor
84
- unit=""
84
+ unit-key="year"
85
85
  :min="currentYear"
86
86
  :max="currentYear + 20"
87
87
  :state="editorState.year"
@@ -95,7 +95,7 @@
95
95
  </el-tabs>
96
96
 
97
97
  <div class="cron-editor__preview">
98
- <div class="cron-editor__section-title">Cron 表达式</div>
98
+ <div class="cron-editor__section-title">{{ t("core.cron.expression") }}</div>
99
99
  <el-input :model-value="previewExpression" readonly />
100
100
  <div class="cron-editor__preview-desc">{{ expressionDescription }}</div>
101
101
  </div>
@@ -103,8 +103,8 @@
103
103
 
104
104
  <template #footer>
105
105
  <div class="dialog-footer">
106
- <el-button type="primary" @click="handleConfirmEditor">确 定</el-button>
107
- <el-button @click="dialogVisible = false">取 消</el-button>
106
+ <el-button type="primary" @click="handleConfirmEditor">{{ t("common.action.confirm") }}</el-button>
107
+ <el-button @click="dialogVisible = false">{{ t("common.action.cancel") }}</el-button>
108
108
  </div>
109
109
  </template>
110
110
  </el-dialog>
@@ -116,6 +116,9 @@ import { computed, defineComponent, h, reactive, ref, watch } from "vue";
116
116
  import type { PropType } from "vue";
117
117
  import { ElCheckbox, ElCheckboxGroup, ElInputNumber, ElRadio } from "element-plus";
118
118
  import { Operation } from "@element-plus/icons-vue";
119
+ import { useLocaleStore } from "../../locales";
120
+
121
+ const { t } = useLocaleStore();
119
122
 
120
123
  /** Cron 单个字段支持的编辑模式。 */
121
124
  type CronSegmentMode = "every" | "unspecified" | "range" | "step" | "specific" | "last" | "weekday";
@@ -144,8 +147,9 @@ type CronEditorState = Record<CronSegmentKey, CronSegmentState>;
144
147
 
145
148
  const props = withDefaults(defineProps<CronExpressionProps>(), {
146
149
  modelValue: "",
147
- placeholder: "请输入 cron 表达式"
150
+ placeholder: ""
148
151
  });
152
+ const placeholder = computed(() => props.placeholder || t("core.cron.placeholder"));
149
153
 
150
154
  const emit = defineEmits<{
151
155
  "update:modelValue": [value: string];
@@ -155,15 +159,15 @@ const currentYear = new Date().getFullYear();
155
159
  const dialogVisible = ref(false);
156
160
  const activeTab = ref<CronSegmentKey>("second");
157
161
 
158
- const presetOptions = [
159
- { label: "每分钟执行", value: "0 * * * * *" },
160
- { label: "每 5 分钟执行", value: "0 */5 * * * *" },
161
- { label: "每小时执行", value: "0 0 * * * *" },
162
- { label: "每天零点执行", value: "0 0 0 * * *" },
163
- { label: "每天早上 8 点执行", value: "0 0 8 * * *" },
164
- { label: "每月 1 号零点执行", value: "0 0 0 1 * *" },
165
- { label: "明年每天零点执行", value: `0 0 0 * * ${currentYear + 1}` }
166
- ];
162
+ const presetOptions = computed(() => [
163
+ { label: t("core.cron.preset.everyMinute"), value: "0 * * * * *" },
164
+ { label: t("core.cron.preset.everyFiveMinutes"), value: "0 */5 * * * *" },
165
+ { label: t("core.cron.preset.everyHour"), value: "0 0 * * * *" },
166
+ { label: t("core.cron.preset.dailyMidnight"), value: "0 0 0 * * *" },
167
+ { label: t("core.cron.preset.dailyEight"), value: "0 0 8 * * *" },
168
+ { label: t("core.cron.preset.monthlyFirst"), value: "0 0 0 1 * *" },
169
+ { label: t("core.cron.preset.nextYearDaily"), value: `0 0 0 * * ${currentYear + 1}` }
170
+ ]);
167
171
 
168
172
  /** 创建单个 Cron 字段的默认编辑状态。 */
169
173
  function createSegmentState(min = 0): CronSegmentState {
@@ -210,14 +214,14 @@ const previewExpression = computed(() => {
210
214
 
211
215
  const expressionDescription = computed(() => {
212
216
  const descriptionList = [
213
- formatSegmentDescription("", editorState.second),
214
- formatSegmentDescription("分钟", editorState.minute),
215
- formatSegmentDescription("小时", editorState.hour),
216
- formatSegmentDescription("日期", editorState.day),
217
- formatSegmentDescription("月份", editorState.month),
218
- formatSegmentDescription("年份", editorState.year)
217
+ formatSegmentDescription("second", editorState.second),
218
+ formatSegmentDescription("minute", editorState.minute),
219
+ formatSegmentDescription("hour", editorState.hour),
220
+ formatSegmentDescription("day", editorState.day),
221
+ formatSegmentDescription("month", editorState.month),
222
+ formatSegmentDescription("year", editorState.year)
219
223
  ].filter(Boolean);
220
- return descriptionList.length ? descriptionList.join("") : "未配置执行规则";
224
+ return descriptionList.length ? descriptionList.join(t("core.cron.descriptionSeparator")) : t("core.cron.notConfigured");
221
225
  });
222
226
 
223
227
  /** 打开 Cron 编辑弹窗,并按当前表达式回填编辑状态。 */
@@ -280,50 +284,43 @@ function buildSegmentValue(segment: CronSegmentState) {
280
284
  }
281
285
  }
282
286
 
283
- /** 将单个字段编辑状态转换为中文预览描述。 */
284
- function formatSegmentDescription(label: string, segment: CronSegmentState) {
287
+ /** 将单个字段编辑状态转换为当前语言的预览描述。 */
288
+ function formatSegmentDescription(segmentKey: CronSegmentKey, segment: CronSegmentState) {
289
+ const segmentName = t(`core.cron.segment.${segmentKey}`);
290
+ const unit = t(`core.cron.unit.${segmentKey}`);
285
291
  switch (segment.mode) {
286
292
  case "every":
287
- return `每${label === "分钟" ? "分钟" : label === "小时" ? "小时" : label}`;
293
+ return t("core.cron.description.every", { unit });
288
294
  case "unspecified":
289
- return `${label}不指定`;
295
+ return t("core.cron.description.unspecified", { segment: segmentName });
290
296
  case "range":
291
- return `${label}${segment.rangeStart}到${segment.rangeEnd}`;
297
+ return t("core.cron.description.range", { segment: segmentName, start: segment.rangeStart, end: segment.rangeEnd });
292
298
  case "step":
293
- return `从${segment.stepStart}${formatUnitSuffix(label)}开始,每${segment.stepValue}${formatCycleSuffix(label)}执行一次`;
299
+ return t("core.cron.description.step", {
300
+ start: segment.stepStart,
301
+ unit,
302
+ step: segment.stepValue,
303
+ cycleUnit: t(`core.cron.cycleUnit.${segmentKey}`)
304
+ });
294
305
  case "specific":
295
306
  return segment.specific.length
296
- ? `${label}指定为 ${segment.specific.map(item => formatSpecificLabel(label, item)).join("、")}`
297
- : `${label}未指定`;
307
+ ? t("core.cron.description.specific", {
308
+ segment: segmentName,
309
+ values: segment.specific.map(item => formatSpecificLabel(segmentKey, item)).join(t("core.cron.valueSeparator"))
310
+ })
311
+ : t("core.cron.description.unspecified", { segment: segmentName });
298
312
  case "last":
299
- return "本月最后一天";
313
+ return t("core.cron.lastDay");
300
314
  case "weekday":
301
- return `本月 ${segment.weekday} 号最近的工作日`;
315
+ return t("core.cron.description.weekday", { day: segment.weekday });
302
316
  default:
303
317
  return "";
304
318
  }
305
319
  }
306
320
 
307
- /** 获取 Cron 字段在范围描述中的单位后缀。 */
308
- function formatUnitSuffix(label: string) {
309
- if (label === "日期") return "日";
310
- if (label === "月份") return "月";
311
- if (label === "年份") return "年";
312
- return label;
313
- }
314
-
315
- /** 获取 Cron 字段在周期描述中的单位后缀。 */
316
- function formatCycleSuffix(label: string) {
317
- if (label === "日期") return "天";
318
- if (label === "月份") return "个月";
319
- if (label === "年份") return "年";
320
- return label;
321
- }
322
-
323
321
  /** 格式化指定值模式下的单个展示值。 */
324
- function formatSpecificLabel(label: string, value: number) {
325
- if (label === "年份") return `${value}年`;
326
- return String(value);
322
+ function formatSpecificLabel(segmentKey: CronSegmentKey, value: number) {
323
+ return t("core.cron.valueWithUnit", { value, unit: t(`core.cron.unit.${segmentKey}`) });
327
324
  }
328
325
 
329
326
  /**
@@ -404,8 +401,8 @@ watch(
404
401
  const CronSegmentEditor = defineComponent({
405
402
  name: "CronSegmentEditor",
406
403
  props: {
407
- unit: {
408
- type: String,
404
+ unitKey: {
405
+ type: String as PropType<CronSegmentKey>,
409
406
  required: true
410
407
  },
411
408
  min: {
@@ -468,9 +465,10 @@ const CronSegmentEditor = defineComponent({
468
465
  });
469
466
 
470
467
  const specificOptions = computed(() => {
468
+ const unit = t(`core.cron.unit.${segmentProps.unitKey}`);
471
469
  return numberOptions.value.map(item => ({
472
470
  value: item,
473
- label: segmentProps.unit === "年" ? `${item}年` : `${item}${segmentProps.unit}`
471
+ label: t("core.cron.valueWithUnit", { value: item, unit })
474
472
  }));
475
473
  });
476
474
 
@@ -501,21 +499,21 @@ const CronSegmentEditor = defineComponent({
501
499
  {segmentProps.supportsEvery && (
502
500
  <label class="segment-editor__row">
503
501
  {h(ElRadio, { modelValue: localState.mode, value: "every", onChange: () => handleModeChange("every") })}
504
- <span>每{segmentProps.unit}</span>
502
+ <span>{t("core.cron.everyUnit", { unit: t(`core.cron.unit.${segmentProps.unitKey}`) })}</span>
505
503
  </label>
506
504
  )}
507
505
 
508
506
  {segmentProps.supportsUnspecified && (
509
507
  <label class="segment-editor__row">
510
508
  {h(ElRadio, { modelValue: localState.mode, value: "unspecified", onChange: () => handleModeChange("unspecified") })}
511
- <span>不指定</span>
509
+ <span>{t("core.cron.unspecified")}</span>
512
510
  </label>
513
511
  )}
514
512
 
515
513
  <label class="segment-editor__row">
516
514
  {h(ElRadio, { modelValue: localState.mode, value: "range", onChange: () => handleModeChange("range") })}
517
- <span>周期</span>
518
- <span>从</span>
515
+ <span>{t("core.cron.range")}</span>
516
+ <span>{t("core.cron.from")}</span>
519
517
  {h(ElInputNumber, {
520
518
  modelValue: localState.rangeStart,
521
519
  min: segmentProps.min,
@@ -523,7 +521,7 @@ const CronSegmentEditor = defineComponent({
523
521
  controlsPosition: "right",
524
522
  "onUpdate:modelValue": value => handleNumberChange("rangeStart", Number(value))
525
523
  })}
526
- <span>至</span>
524
+ <span>{t("core.cron.to")}</span>
527
525
  {h(ElInputNumber, {
528
526
  modelValue: localState.rangeEnd,
529
527
  min: segmentProps.min,
@@ -531,14 +529,14 @@ const CronSegmentEditor = defineComponent({
531
529
  controlsPosition: "right",
532
530
  "onUpdate:modelValue": value => handleNumberChange("rangeEnd", Number(value))
533
531
  })}
534
- <span>{segmentProps.unit}</span>
532
+ <span>{t(`core.cron.unit.${segmentProps.unitKey}`)}</span>
535
533
  </label>
536
534
 
537
535
  {segmentProps.supportsStep && (
538
536
  <label class="segment-editor__row">
539
537
  {h(ElRadio, { modelValue: localState.mode, value: "step", onChange: () => handleModeChange("step") })}
540
- <span>循环</span>
541
- <span>从</span>
538
+ <span>{t("core.cron.repeat")}</span>
539
+ <span>{t("core.cron.from")}</span>
542
540
  {h(ElInputNumber, {
543
541
  modelValue: localState.stepStart,
544
542
  min: segmentProps.min,
@@ -546,7 +544,7 @@ const CronSegmentEditor = defineComponent({
546
544
  controlsPosition: "right",
547
545
  "onUpdate:modelValue": value => handleNumberChange("stepStart", Number(value))
548
546
  })}
549
- <span>{segmentProps.unit}开始,每</span>
547
+ <span>{t("core.cron.startEvery", { unit: t(`core.cron.unit.${segmentProps.unitKey}`) })}</span>
550
548
  {h(ElInputNumber, {
551
549
  modelValue: localState.stepValue,
552
550
  min: 1,
@@ -554,14 +552,14 @@ const CronSegmentEditor = defineComponent({
554
552
  controlsPosition: "right",
555
553
  "onUpdate:modelValue": value => handleNumberChange("stepValue", Number(value))
556
554
  })}
557
- <span>{segmentProps.unit}执行一次</span>
555
+ <span>{t("core.cron.runOnce", { unit: t(`core.cron.cycleUnit.${segmentProps.unitKey}`) })}</span>
558
556
  </label>
559
557
  )}
560
558
 
561
559
  {segmentProps.supportsSpecific && (
562
560
  <div class="segment-editor__row segment-editor__row--top">
563
561
  {h(ElRadio, { modelValue: localState.mode, value: "specific", onChange: () => handleModeChange("specific") })}
564
- <span>指定</span>
562
+ <span>{t("core.cron.specific")}</span>
565
563
  {h(
566
564
  ElCheckboxGroup,
567
565
  {
@@ -582,15 +580,15 @@ const CronSegmentEditor = defineComponent({
582
580
  {segmentProps.supportsLast && (
583
581
  <label class="segment-editor__row">
584
582
  {h(ElRadio, { modelValue: localState.mode, value: "last", onChange: () => handleModeChange("last") })}
585
- <span>本月最后一天</span>
583
+ <span>{t("core.cron.lastDay")}</span>
586
584
  </label>
587
585
  )}
588
586
 
589
587
  {segmentProps.supportsWeekday && (
590
588
  <label class="segment-editor__row">
591
589
  {h(ElRadio, { modelValue: localState.mode, value: "weekday", onChange: () => handleModeChange("weekday") })}
592
- <span>工作日</span>
593
- <span>本月</span>
590
+ <span>{t("core.cron.workday")}</span>
591
+ <span>{t("core.cron.thisMonth")}</span>
594
592
  {h(ElInputNumber, {
595
593
  modelValue: localState.weekday,
596
594
  min: segmentProps.min,
@@ -598,7 +596,7 @@ const CronSegmentEditor = defineComponent({
598
596
  controlsPosition: "right",
599
597
  "onUpdate:modelValue": value => handleNumberChange("weekday", Number(value))
600
598
  })}
601
- <span>号最近的工作日</span>
599
+ <span>{t("core.cron.nearestWorkday")}</span>
602
600
  </label>
603
601
  )}
604
602
  </div>
@@ -35,8 +35,10 @@
35
35
  <template #footer>
36
36
  <slot name="footer">
37
37
  <div class="dialog-footer">
38
- <el-button @click="handleCancel">{{ cancelText }}</el-button>
39
- <el-button type="primary" :loading="confirmLoading" @click="handleConfirm">{{ confirmText }}</el-button>
38
+ <el-button @click="handleCancel">{{ cancelText || t("common.action.cancel") }}</el-button>
39
+ <el-button type="primary" :loading="confirmLoading" @click="handleConfirm">
40
+ {{ confirmText || t("common.action.confirm") }}
41
+ </el-button>
40
42
  </div>
41
43
  </slot>
42
44
  </template>
@@ -49,6 +51,9 @@ import type { FormRules } from "element-plus";
49
51
  import ProDialog from "../../components/Dialog/ProDialog.vue";
50
52
  import ProForm from "../../components/ProForm/index.vue";
51
53
  import type { ProFormField, ProFormInstance } from "../../components/ProForm/interface";
54
+ import { useLocaleStore } from "../../locales";
55
+
56
+ const { t } = useLocaleStore();
52
57
 
53
58
  /** 表单弹窗组件属性。 */
54
59
  interface FormDialogProps {
@@ -79,8 +84,8 @@ const props = withDefaults(defineProps<FormDialogProps>(), {
79
84
  labelWidth: "110px",
80
85
  gutter: 20,
81
86
  colSpan: 24,
82
- confirmText: "确定",
83
- cancelText: "取消",
87
+ confirmText: "",
88
+ cancelText: "",
84
89
  confirmLoading: false,
85
90
  destroyOnClose: false,
86
91
  closeOnClickModal: true,
@@ -23,14 +23,20 @@
23
23
  </template>
24
24
  <template v-else-if="showFooter" #footer>
25
25
  <div class="dialog-footer">
26
- <el-button @click="handleCancel"> {{ cancelText }} </el-button>
27
- <el-button type="primary" :loading="confirmLoading" @click="handleConfirm"> {{ confirmText }} </el-button>
26
+ <el-button @click="handleCancel">{{ cancelText || t("common.action.cancel") }}</el-button>
27
+ <el-button type="primary" :loading="confirmLoading" @click="handleConfirm">
28
+ {{ confirmText || t("common.action.confirm") }}
29
+ </el-button>
28
30
  </div>
29
31
  </template>
30
32
  </el-dialog>
31
33
  </template>
32
34
 
33
35
  <script setup lang="ts" name="ProDialog">
36
+ import { useLocaleStore } from "../../locales";
37
+
38
+ const { t } = useLocaleStore();
39
+
34
40
  /** 通用弹窗组件属性。 */
35
41
  interface ProDialogProps {
36
42
  modelValue: boolean;
@@ -51,8 +57,8 @@ withDefaults(defineProps<ProDialogProps>(), {
51
57
  title: "",
52
58
  width: "500px",
53
59
  top: "8vh",
54
- confirmText: "确定",
55
- cancelText: "取消",
60
+ confirmText: "",
61
+ cancelText: "",
56
62
  confirmLoading: false,
57
63
  destroyOnClose: false,
58
64
  closeOnClickModal: true,
@@ -2,7 +2,7 @@
2
2
  <el-select
3
3
  v-if="type === 'select'"
4
4
  v-model="selectedSingleValue"
5
- :placeholder="placeholder"
5
+ :placeholder="placeholder || t('common.placeholder.select')"
6
6
  :disabled="disabled"
7
7
  clearable
8
8
  :style="style"
@@ -41,6 +41,9 @@ import type { CSSProperties } from "vue";
41
41
  import { ref, watch } from "vue";
42
42
  import { useDictStore } from "../../stores/modules/dict";
43
43
  import type { OptionBaseDictResponse_BaseDictItem } from "../../rpc/system/admin/v1/base_dict";
44
+ import { useLocaleStore } from "../../locales";
45
+
46
+ const { t } = useLocaleStore();
44
47
 
45
48
  /** 字典组件支持的控件类型。 */
46
49
  type DictType = "select" | "radio" | "checkbox";
@@ -63,7 +66,7 @@ interface DictProps {
63
66
  const props = withDefaults(defineProps<DictProps>(), {
64
67
  codeType: "number",
65
68
  type: "select",
66
- placeholder: "请选择",
69
+ placeholder: "",
67
70
  disabled: false,
68
71
  style: () =>
69
72
  ({
@@ -12,7 +12,12 @@ import {
12
12
  import { LabelLayout, UniversalTransition } from "echarts/features";
13
13
  import { CanvasRenderer } from "echarts/renderers";
14
14
  import type { BarSeriesOption, LineSeriesOption, PieSeriesOption } from "echarts/charts";
15
- import type { TitleComponentOption, TooltipComponentOption, GridComponentOption, DatasetComponentOption } from "echarts/components";
15
+ import type {
16
+ TitleComponentOption,
17
+ TooltipComponentOption,
18
+ GridComponentOption,
19
+ DatasetComponentOption
20
+ } from "echarts/components";
16
21
  import type { ComposeOption } from "echarts/core";
17
22
 
18
23
  /** 管理后台 ECharts 组件统一支持的图表配置类型。 */
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts" name="ErrorMessage">
2
2
  import { useRouter } from "vue-router";
3
+ import { useLocaleStore } from "../../locales";
3
4
 
4
5
  /** 错误状态展示属性。 */
5
6
  interface ErrorMessageProps {
@@ -14,6 +15,7 @@ interface ErrorMessageProps {
14
15
  defineProps<ErrorMessageProps>();
15
16
 
16
17
  const router = useRouter();
18
+ const { t } = useLocaleStore();
17
19
  </script>
18
20
 
19
21
  <template>
@@ -22,7 +24,7 @@ const router = useRouter();
22
24
  <div class="not-detail">
23
25
  <h2>{{ code }}</h2>
24
26
  <h4>{{ message }}</h4>
25
- <el-button type="primary" @click="router.back()"> 返回上一页 </el-button>
27
+ <el-button type="primary" @click="router.back()">{{ t("common.action.back") }}</el-button>
26
28
  </div>
27
29
  </div>
28
30
  </template>
@@ -1,10 +1,16 @@
1
1
  <template>
2
- <el-dialog v-model="dialogVisible" :title="`批量添加${parameter.title}`" :destroy-on-close="true" width="580px" draggable>
2
+ <el-dialog
3
+ v-model="dialogVisible"
4
+ :title="t('core.upload.batchAdd', { resource: parameter.title })"
5
+ :destroy-on-close="true"
6
+ width="580px"
7
+ draggable
8
+ >
3
9
  <el-form class="drawer-multiColumn-form" label-width="100px">
4
- <el-form-item label="模板下载 :">
5
- <el-button type="primary" :icon="Download" @click="downloadTemp"> 点击下载 </el-button>
10
+ <el-form-item :label="t('core.upload.templateDownload')">
11
+ <el-button type="primary" :icon="Download" @click="downloadTemp">{{ t("common.action.download") }}</el-button>
6
12
  </el-form-item>
7
- <el-form-item label="文件上传 :">
13
+ <el-form-item :label="t('core.upload.file')">
8
14
  <el-upload
9
15
  action="#"
10
16
  class="upload"
@@ -23,16 +29,18 @@
23
29
  <el-icon class="el-icon--upload">
24
30
  <upload-filled />
25
31
  </el-icon>
26
- <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
32
+ <div class="el-upload__text">
33
+ {{ t("core.upload.dragOrClick") }}<em>{{ t("core.upload.click") }}</em>
34
+ </div>
27
35
  </slot>
28
36
  <template #tip>
29
37
  <slot name="tip">
30
- <div class="el-upload__tip">请上传 .xls , .xlsx 标准格式文件,文件最大为 {{ parameter.fileSize }}M</div>
38
+ <div class="el-upload__tip">{{ t("core.upload.excelFormat", { size: parameter.fileSize ?? 5 }) }}</div>
31
39
  </slot>
32
40
  </template>
33
41
  </el-upload>
34
42
  </el-form-item>
35
- <el-form-item label="数据覆盖 :">
43
+ <el-form-item :label="t('core.upload.dataCover')">
36
44
  <el-switch v-model="isCover" />
37
45
  </el-form-item>
38
46
  </el-form>
@@ -44,6 +52,9 @@ import { ref } from "vue";
44
52
  import { useDownload } from "../../hooks/useDownload";
45
53
  import { Download } from "@element-plus/icons-vue";
46
54
  import { ElNotification, UploadRequestOptions, UploadRawFile } from "element-plus";
55
+ import { useLocaleStore } from "../../locales";
56
+
57
+ const { t } = useLocaleStore();
47
58
 
48
59
  /** Excel 导入弹窗参数。 */
49
60
  export interface ExcelParameterProps {
@@ -77,7 +88,7 @@ const acceptParams = (params: ExcelParameterProps) => {
77
88
  // Excel 导入模板下载
78
89
  const downloadTemp = () => {
79
90
  if (!parameter.value.tempApi) return;
80
- useDownload(parameter.value.tempApi, `${parameter.value.title}模板`);
91
+ useDownload(parameter.value.tempApi, t("core.upload.template", { resource: parameter.value.title }));
81
92
  };
82
93
 
83
94
  // 文件上传
@@ -99,15 +110,15 @@ const beforeExcelUpload = (file: UploadRawFile) => {
99
110
  const fileSize = file.size / 1024 / 1024 < parameter.value.fileSize!;
100
111
  if (!isExcel)
101
112
  ElNotification({
102
- title: "温馨提示",
103
- message: "上传文件只能是 xls / xlsx 格式!",
113
+ title: t("common.title.warning"),
114
+ message: t("core.upload.fileFormatInvalid"),
104
115
  type: "warning"
105
116
  });
106
117
  if (!fileSize)
107
118
  setTimeout(() => {
108
119
  ElNotification({
109
- title: "温馨提示",
110
- message: `上传文件大小不能超过 ${parameter.value.fileSize}MB!`,
120
+ title: t("common.title.warning"),
121
+ message: t("core.upload.fileSizeExceeded", { size: parameter.value.fileSize ?? 5 }),
111
122
  type: "warning"
112
123
  });
113
124
  }, 0);
@@ -117,8 +128,8 @@ const beforeExcelUpload = (file: UploadRawFile) => {
117
128
  // 文件数超出提示
118
129
  const handleExceed = () => {
119
130
  ElNotification({
120
- title: "温馨提示",
121
- message: "最多只能上传一个文件!",
131
+ title: t("common.title.warning"),
132
+ message: t("core.upload.singleFileOnly"),
122
133
  type: "warning"
123
134
  });
124
135
  };
@@ -126,8 +137,8 @@ const handleExceed = () => {
126
137
  // 上传错误提示
127
138
  const excelUploadError = () => {
128
139
  ElNotification({
129
- title: "温馨提示",
130
- message: `批量添加${parameter.value.title}失败,请您重新上传!`,
140
+ title: t("common.title.warning"),
141
+ message: t("core.upload.batchFailed", { resource: parameter.value.title }),
131
142
  type: "error"
132
143
  });
133
144
  };
@@ -135,8 +146,8 @@ const excelUploadError = () => {
135
146
  // 上传成功提示
136
147
  const excelUploadSuccess = () => {
137
148
  ElNotification({
138
- title: "温馨提示",
139
- message: `批量添加${parameter.value.title}成功!`,
149
+ title: t("common.title.notice"),
150
+ message: t("core.upload.batchSuccess", { resource: parameter.value.title }),
140
151
  type: "success"
141
152
  });
142
153
  };
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <el-dropdown trigger="click" placement="bottom-end" @command="handleLocaleChange">
3
+ <Languages class="toolBar-icon" :size="20" :title="t('core.header.language')" />
4
+ <template #dropdown>
5
+ <el-dropdown-menu>
6
+ <el-dropdown-item v-for="item in localeOptions" :key="item.value" :command="item.value" :disabled="item.value === locale">
7
+ {{ item.label }}
8
+ </el-dropdown-item>
9
+ </el-dropdown-menu>
10
+ </template>
11
+ </el-dropdown>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { computed } from "vue";
16
+ import { Languages } from "@lucide/vue";
17
+ import { type SupportedLocale, useLocaleStore } from "../../locales";
18
+
19
+ const { locale, setLocale, languageOptions, t } = useLocaleStore();
20
+ const localeOptions = computed(() => {
21
+ return languageOptions.value.map(item => ({ value: item.language_code, label: item.language_name }));
22
+ });
23
+
24
+ /** 切换管理端语言并刷新动态本地化数据。 */
25
+ function handleLocaleChange(value: SupportedLocale) {
26
+ void setLocale(value);
27
+ }
28
+ </script>