@skyfox2000/webui 1.5.7 → 1.5.10

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 (59) hide show
  1. package/lib/assets/modules/{baseLayout-DfTxHOFc.js → baseLayout-BuQjrozB.js} +9 -9
  2. package/lib/assets/modules/{file-upload-DTOdV5vM.js → file-upload-DcusqXDb.js} +5 -5
  3. package/lib/assets/modules/{index-M1qERbea.js → index-BlQB5KSU.js} +2 -2
  4. package/lib/assets/modules/{index-BwMaOrJT.js → index-CW_ZCHWs.js} +1 -1
  5. package/lib/assets/modules/index-CekzHbWp.js +114 -0
  6. package/lib/assets/modules/{menuTabs-DWaBSRNr.js → menuTabs-BYSjomB7.js} +44 -36
  7. package/lib/assets/modules/{toolIcon-BYnHhAy-.js → toolIcon-D4vAp0qA.js} +1 -1
  8. package/lib/assets/modules/{upload-template-BKm9mFq8.js → upload-template-NU0Bpg4N.js} +2056 -2176
  9. package/lib/assets/modules/uploadList-ENSsTfpD.js +472 -0
  10. package/lib/const/options.d.ts +6 -20
  11. package/lib/const/stores.d.ts +11 -0
  12. package/lib/es/AceEditor/index.js +70 -70
  13. package/lib/es/BasicLayout/index.js +6 -6
  14. package/lib/es/Error403/index.js +1 -1
  15. package/lib/es/Error404/index.js +1 -1
  16. package/lib/es/ExcelForm/index.js +16 -16
  17. package/lib/es/MenuLayout/index.js +6 -6
  18. package/lib/es/TemplateFile/index.js +5 -5
  19. package/lib/es/UploadForm/index.js +5 -5
  20. package/lib/index.d.ts +2 -1
  21. package/lib/locales/default.d.ts +137 -115
  22. package/lib/utils/tools.d.ts +1 -0
  23. package/lib/webui.css +1 -1
  24. package/lib/webui.es.js +1692 -1547
  25. package/package.json +1 -1
  26. package/src/components/content/dialog/index.vue +1 -0
  27. package/src/components/content/form/formItem.vue +3 -2
  28. package/src/components/content/search/index.vue +1 -0
  29. package/src/components/content/search/searchItem.vue +3 -2
  30. package/src/components/form/aceEditor/index.vue +5 -3
  31. package/src/components/form/cascader/index.vue +5 -3
  32. package/src/components/form/checkbox/index.vue +1 -1
  33. package/src/components/form/datePicker/index.vue +2 -1
  34. package/src/components/form/input/index.vue +1 -1
  35. package/src/components/form/input/inputNumber.vue +3 -1
  36. package/src/components/form/input/inputPassword.vue +3 -1
  37. package/src/components/form/propEditor/index.vue +8 -3
  38. package/src/components/form/radio/index.vue +2 -1
  39. package/src/components/form/radio/radioStatus.vue +2 -1
  40. package/src/components/form/switch/index.vue +3 -1
  41. package/src/components/form/textarea/index.vue +1 -1
  42. package/src/components/form/timePicker/index.vue +4 -1
  43. package/src/components/form/treeSelect/index.vue +3 -1
  44. package/src/components/form/upload/imageList.vue +8 -6
  45. package/src/components/form/upload/uploadList.vue +6 -5
  46. package/src/components/layout/datetime/index.vue +15 -4
  47. package/src/components/layout/header/language.vue +2 -0
  48. package/src/const/options.ts +76 -52
  49. package/src/const/stores.ts +58 -0
  50. package/src/index.ts +2 -0
  51. package/src/locales/default.ts +145 -123
  52. package/src/locales/en-US.json +320 -0
  53. package/src/locales/index.ts +109 -39
  54. package/src/stores/hostInfo.ts +2 -19
  55. package/src/stores/userInfo.ts +2 -30
  56. package/src/utils/options.ts +2 -2
  57. package/src/utils/tools.ts +88 -83
  58. package/lib/assets/modules/index-BDoBUrYA.js +0 -114
  59. package/lib/assets/modules/uploadList-CHvr6Hp1.js +0 -472
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyfox2000/webui",
3
- "version": "1.5.7",
3
+ "version": "1.5.10",
4
4
  "description": "后台前端通用组件定义, 支持国际化",
5
5
  "type": "module",
6
6
  "keywords": [],
@@ -4,6 +4,7 @@ import { Button } from '../../common';
4
4
  import { Modal, Space } from 'ant-design-vue';
5
5
  import { onFormSave, onFormSaveAs, onFormClose, EditorControl, ProviderKeys } from '@/index';
6
6
  import { AnyData } from '@skyfox2000/fapi';
7
+ import { $t } from '@/locales/index';
7
8
 
8
9
  const props = defineProps<{
9
10
  /**
@@ -5,6 +5,7 @@ import { Helper } from '../../common';
5
5
  import { computed, inject, ref, useAttrs } from 'vue';
6
6
  import { AnyData } from '@skyfox2000/fapi';
7
7
  import { getRule } from '@/utils/form-validate';
8
+ import { $t } from '@/locales/index';
8
9
 
9
10
  const props = defineProps<{
10
11
  /**
@@ -62,9 +63,9 @@ const required = computed(() => {
62
63
  // 如果rule包含.,则表示是对象属性
63
64
  const rule = getRule(props.rule.split('.'), editorCtrl?.formRules?.value);
64
65
  if (!rule) {
65
- message.error(`"${props.label}" 的验证规则 \`${props.rule}\` 不存在`);
66
+ message.error($t('webui.components.content.formItem.validationRuleNotFound', { label: props.label, rule: props.rule }));
66
67
  errInfo.value.errClass = 'text-[#ff4d4f]';
67
- errInfo.value.msg = `规则 \`${props.rule}\` 不存在,请检查代码!`;
68
+ errInfo.value.msg = $t('webui.components.content.formItem.validationRuleNotFound', { label: props.label, rule: props.rule });
68
69
  return true;
69
70
  }
70
71
  if (!rule.required) {
@@ -5,6 +5,7 @@ import SearchItem from './searchItem.vue';
5
5
  import { Button } from '../../common';
6
6
  import { GridControl } from '@/index';
7
7
  import { AnyData } from '@skyfox2000/fapi';
8
+ import { $t } from '@/locales/index';
8
9
 
9
10
  /**
10
11
  * 搜索表单BUG
@@ -5,6 +5,7 @@ import { FormItem } from 'ant-design-vue';
5
5
  import { AnyData } from '@skyfox2000/fapi';
6
6
  import { getRule } from '@/utils/form-validate';
7
7
  import message from 'vue-m-message';
8
+ import { $t } from '@/locales/index';
8
9
 
9
10
  const props = defineProps<{
10
11
  /**
@@ -42,9 +43,9 @@ const required = computed(() => {
42
43
  // 如果rule包含.,则表示是对象属性
43
44
  const rule = getRule(props.rule.split('.'), editorCtrl?.formRules?.value);
44
45
  if (!rule) {
45
- message.error(`"${props.label}" 的验证规则 \`${props.rule}\` 不存在`);
46
+ message.error($t('webui.components.content.searchItem.validationRuleNotFound', { label: props.label, rule: props.rule }));
46
47
  errInfo.value.errClass = 'text-[#ff4d4f]';
47
- errInfo.value.msg = `规则 \`${props.rule}\` 不存在,请检查代码!`;
48
+ errInfo.value.msg = $t('webui.components.content.searchItem.validationRuleNotFound', { label: props.label, rule: props.rule });
48
49
  return true;
49
50
  }
50
51
  if (!rule.required) {
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { onMounted, ref, watch, defineAsyncComponent } from 'vue';
3
3
  import { ToolIcon, Dialog } from '../../index';
4
+ import { $t } from '@/locales/index';
4
5
  import { loadTheme, loadWorker, ensureAceLoaded } from './aceConfig';
5
6
 
6
7
  const props = defineProps<{
@@ -160,16 +161,17 @@ onMounted(async () => {
160
161
  <div class="flex-1 overflow-hidden">
161
162
  <VAceEditor v-if="editorLoaded" :class="[height]" v-model:value="content" :lang="language" :theme="theme"
162
163
  :options="aceOptions" @blur="handleBlur" @keyup.enter.native.stop @keydown.enter.native.stop />
163
- <div v-else :class="[height, 'flex justify-center items-center']">加载编辑器中...</div>
164
+ <div v-else :class="[height, 'flex justify-center items-center']">{{
165
+ $t('webui.components.form.aceEditor.loading') }}</div>
164
166
  </div>
165
167
  </div>
166
- <Dialog title="代码编辑器" v-model:open="isFullScreen" :width="680" :footer="null">
168
+ <Dialog :title="$t('webui.components.form.aceEditor.title')" v-model:open="isFullScreen" :width="680" :footer="null">
167
169
  <VAceEditor v-if="isFullScreen && editorLoaded" class="h-[500px] w-[99.3%] border-1 border-solid border-gray-200"
168
170
  v-model:value="content" :lang="language" :theme="theme" :options="aceOptions" @blur="handleBlur"
169
171
  @keyup.enter.native.stop @keydown.enter.native.stop />
170
172
  <div v-else-if="isFullScreen"
171
173
  class="h-[500px] w-[99.3%] flex justify-center items-center border-1 border-solid border-gray-200">
172
- 加载编辑器中...
174
+ {{ $t('webui.components.form.aceEditor.loading') }}
173
175
  </div>
174
176
  </Dialog>
175
177
  </template>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, onMounted, onUnmounted, useAttrs, watch, shallowRef, onActivated } from 'vue';
3
+ import { $t } from '@/locales/index';
3
4
  import {
4
5
  circleLoading,
5
6
  useInputFactory,
@@ -45,7 +46,7 @@ watch(
45
46
  () => url.value.loading,
46
47
  (newVal) => {
47
48
  if (newVal) placeholder.value = '';
48
- else if (!placeholder.value) placeholder.value = '请选择' + labelText.value;
49
+ else if (!placeholder.value) placeholder.value = $t('webui.components.form.cascader.pleaseSelect', [labelText.value]);
49
50
  },
50
51
  { immediate: true },
51
52
  );
@@ -110,10 +111,11 @@ onUnmounted(() => {
110
111
  <div class="relative w-[248px] max-w-[248px]">
111
112
  <div v-if="!selectOptions.length" class="absolute z-10 mt-[5px] mr-[10px] text-[#999] flex items-center">
112
113
  <circleLoading class="text-[#555] mx-[5px] !ml-[10px] !w-4 !h-4" />
113
- <span>数据加载中...</span>
114
+ <span>{{ $t('webui.components.form.cascader.loading') }}</span>
114
115
  </div>
115
116
  <Cascader :options="selectOptions" :class="[errInfo?.errClass]" :allow-clear="true"
116
- :placeholder="selectOptions.length > 0 ? '请选择' + labelText : ''" @change="onChanged" v-bind="attrs" />
117
+ :placeholder="selectOptions.length > 0 ? $t('webui.components.form.cascader.pleaseSelect', [labelText]) : ''"
118
+ @change="onChanged" v-bind="attrs" />
117
119
  </div>
118
120
  </template>
119
121
 
@@ -99,7 +99,7 @@ onUnmounted(() => {
99
99
  <!-- TODO 获取结果通知 -->
100
100
  <!-- <div v-if="!checkboxOptions.length" class="absolute z-10 mt-[5px] mr-[10px] text-[#999] flex items-center">
101
101
  <circleLoading class="text-[#555] mx-[5px] !ml-[10px] !w-4 !h-4" />
102
- <span>数据加载中...</span>
102
+ <span>{{ $t('webui.components.form.checkbox.loading') }}</span>
103
103
  </div> -->
104
104
  <CheckboxGroup @change="onChanged" class="w-full mb-[-3px]" v-bind="attrs">
105
105
  <template v-if="checkboxOptions.length > 0">
@@ -3,6 +3,7 @@ import { ref } from 'vue';
3
3
  import { DatePicker } from 'ant-design-vue';
4
4
  import locale from 'ant-design-vue/es/date-picker/locale/zh_CN';
5
5
  import { formValidate, useInputFactory } from '@/index';
6
+ import { $t } from '@/locales/index';
6
7
  const props = defineProps<{
7
8
  valueFormat?: string;
8
9
  }>();
@@ -21,7 +22,7 @@ const dateFormat = ref<string>(props.valueFormat ?? 'YYYY-MM-DD');
21
22
  <DatePicker
22
23
  class="w-full"
23
24
  :class="[errInfo?.errClass === 'error' ? 'error !border-red-300 shadow-[0_0_3px_0px_#ff4d4f]' : '']"
24
- :placeholder="'请选择' + labelText"
25
+ :placeholder="$t('webui.components.form.datePicker.pleaseSelect', [labelText])"
25
26
  :locale="locale"
26
27
  :valueFormat="dateFormat"
27
28
  @blur="onBlur"
@@ -70,7 +70,7 @@ const onClear = () => {
70
70
  v-model:value="innerValue"
71
71
  autocomplete="new-password"
72
72
  :allow-clear="true"
73
- :placeholder="t('webui.form.inputPlaceholder', [labelText])"
73
+ :placeholder="t('webui.common.placeholder', [labelText])"
74
74
  @blur="onBlur"
75
75
  @change="onClear"
76
76
  v-bind="$attrs"
@@ -1,8 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { formValidate, useInputFactory } from '@/index';
3
3
  import { InputNumber } from 'ant-design-vue';
4
+ import { useI18n } from 'vue-i18n';
4
5
 
5
6
  const { editorCtrl, labelText, errInfo } = useInputFactory();
7
+ const { t } = useI18n();
6
8
  const onBlur = () => {
7
9
  if (errInfo?.value.errClass && editorCtrl) {
8
10
  /// 重新开始验证
@@ -15,7 +17,7 @@ const onBlur = () => {
15
17
  :class="[errInfo?.errClass === 'error' ? 'error !border-red-300 shadow-[0_0_3px_0px_#ff4d4f]' : '']"
16
18
  @blur="onBlur"
17
19
  :allow-clear="false"
18
- :placeholder="'请输入' + labelText"
20
+ :placeholder="t('webui.common.placeholder', [labelText])"
19
21
  class="w-[50%]"
20
22
  v-bind="$attrs"
21
23
  >
@@ -1,8 +1,10 @@
1
1
  <script setup lang="ts">
2
2
  import { formValidate, useInputFactory } from '@/index';
3
3
  import { InputPassword } from 'ant-design-vue';
4
+ import { useI18n } from 'vue-i18n';
4
5
 
5
6
  const { editorCtrl, labelText, errInfo } = useInputFactory();
7
+ const { t } = useI18n();
6
8
  const onBlur = () => {
7
9
  if (errInfo?.value.errClass && editorCtrl) {
8
10
  /// 重新开始验证
@@ -15,7 +17,7 @@ const onBlur = () => {
15
17
  :class="errInfo?.errClass === 'error' ? ['error', '!border-red-300', 'shadow-[0_0_3px_0px_#ff4d4f]'] : ''"
16
18
  :allow-clear="true"
17
19
  autocomplete="new-password"
18
- :placeholder="'请输入' + labelText"
20
+ :placeholder="t('webui.common.placeholder', [labelText])"
19
21
  @blur="onBlur"
20
22
  v-bind="$attrs"
21
23
  />
@@ -2,6 +2,7 @@
2
2
  import { ref, watch } from 'vue';
3
3
  import { Input, Button } from 'ant-design-vue';
4
4
  import type { PropConfigItem } from '@/typings/form.d';
5
+ import { useI18n } from 'vue-i18n';
5
6
 
6
7
  const props = defineProps<{
7
8
  /**
@@ -85,6 +86,8 @@ watch(
85
86
  { immediate: true },
86
87
  );
87
88
 
89
+ const { t } = useI18n();
90
+
88
91
  const updateConfig = () => {
89
92
  let newConfig: Record<string, string>;
90
93
 
@@ -140,7 +143,8 @@ const handleInputChange = () => {
140
143
  <div v-for="item in configList" :key="item.id" class="flex items-center gap-2">
141
144
  <div :class="[fieldWidth ? `w-[${fieldWidth}%]` : 'w-[33%]']">
142
145
  <Input v-if="!selectList || selectList.length === 0" v-model:value="item.field"
143
- :title="item.text || item.field" class="w-full" :placeholder="item.text || labelHolder || '配置名'"
146
+ :title="item.text || item.field" class="w-full"
147
+ :placeholder="item.text || labelHolder || t('webui.components.form.propEditor.configName')"
144
148
  @input="handleInputChange" :disabled="!addMore" />
145
149
  <div v-else>
146
150
  <Input v-model:value="item.text" :title="item.text" :disabled="true" class="w-[100%]" />
@@ -149,14 +153,15 @@ const handleInputChange = () => {
149
153
  </div>
150
154
  <div class="w-[3%]">=</div>
151
155
  <div :class="[fieldWidth ? `w-[${97 - fieldWidth}%]` : 'w-[64%]']">
152
- <Input v-model:value="item.value" :placeholder="valueHolder || '请输入' + item.text || '请输入配置值'"
156
+ <Input v-model:value="item.value"
157
+ :placeholder="valueHolder || t('webui.common.placeholder', [item.text || t('webui.components.form.propEditor.configValue')])"
153
158
  @input="handleInputChange" :title="item.value" />
154
159
  </div>
155
160
  </div>
156
161
  <Button v-if="addMore" @click="addNewLine"
157
162
  class="mt-1 w-[80px] !text-[12px] text-[#666] bg-[#e6f7ff] border-[#b3e0ff] hover:bg-[#b3e0ff] hover:border-[#8abeff]"
158
163
  size="small">
159
- 新增配置行
164
+ {{ t('webui.components.form.propEditor.addConfigRow') }}
160
165
  </Button>
161
166
  </div>
162
167
  </template>
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, onMounted, onUnmounted, watch, useAttrs, shallowRef, onActivated } from 'vue';
3
+ import { $t } from '@/locales/index';
3
4
  import { Radio, RadioChangeEvent, RadioGroup } from 'ant-design-vue';
4
5
  import {
5
6
  OptionCommProps,
@@ -22,7 +23,7 @@ const props = defineProps({
22
23
  },
23
24
  nodata: {
24
25
  type: String,
25
- default: '无数据',
26
+ default: $t('webui.components.form.radio.noData'),
26
27
  },
27
28
  /**
28
29
  * 换行数量
@@ -2,6 +2,7 @@
2
2
  import { ref, PropType } from 'vue';
3
3
  import Radio from './index.vue';
4
4
  import { OPTIONS } from '@/index';
5
+ import { $t } from '@/locales/index';
5
6
 
6
7
  const props = defineProps({
7
8
  /**
@@ -31,7 +32,7 @@ const props = defineProps({
31
32
  const options = ref(JSON.parse(JSON.stringify(OPTIONS.getOptions(props.dataKey))));
32
33
  if (props.all === true) {
33
34
  options.value.unshift({
34
- label: '全部',
35
+ label: $t('webui.components.form.radio.all'),
35
36
  value: props.allValue || [0, 1],
36
37
  });
37
38
  }
@@ -5,6 +5,7 @@ import { Switch } from 'ant-design-vue';
5
5
  import message from 'vue-m-message';
6
6
  import { useOptionFactory } from '@/utils/page';
7
7
  import { computed } from 'vue';
8
+ import { useI18n } from 'vue-i18n';
8
9
 
9
10
  const props = defineProps({
10
11
  ...OptionCommProps,
@@ -47,6 +48,7 @@ const emit = defineEmits<{
47
48
  }>();
48
49
 
49
50
  const { editorCtrl, errInfo } = useInputFactory();
51
+ const { t } = useI18n();
50
52
 
51
53
  const customSize = computed(() => {
52
54
  switch (props.size) {
@@ -66,7 +68,7 @@ const onChange = (checked: boolean | string | number) => {
66
68
  onMounted(() => {
67
69
  if (!props.data || props.data.length != 2) {
68
70
  console.error('Switch组件: ', props.data);
69
- message.error('Switch组件必须有且只有两个选项');
71
+ message.error(t('webui.components.form.switch.error'));
70
72
  return;
71
73
  }
72
74
  if (optionCtrl) loadOption(optionCtrl.autoload, optionCtrl, props);
@@ -16,7 +16,7 @@ const onBlur = () => {
16
16
  <Textarea
17
17
  :class="errInfo?.errClass === 'error' ? ['error', '!border-red-300', 'shadow-[0_0_3px_0px_#ff4d4f]'] : ''"
18
18
  :allow-clear="true"
19
- :placeholder="t('webui.form.inputPlaceholder', [labelText])"
19
+ :placeholder="t('webui.common.placeholder', [labelText])"
20
20
  @blur="onBlur"
21
21
  @keyup.enter.native.stop
22
22
  @keydown.enter.native.stop
@@ -3,11 +3,13 @@ import { ref } from 'vue';
3
3
  import { TimePicker } from 'ant-design-vue';
4
4
  import locale from 'ant-design-vue/es/date-picker/locale/zh_CN';
5
5
  import { formValidate, useInputFactory } from '@/index';
6
+ import { useI18n } from 'vue-i18n';
6
7
  const props = defineProps<{
7
8
  valueFormat?: string;
8
9
  }>();
9
10
 
10
11
  const { editorCtrl, labelText, errInfo } = useInputFactory();
12
+ const { t } = useI18n();
11
13
  const onBlur = () => {
12
14
  if (errInfo?.value.errClass && editorCtrl) {
13
15
  /// 重新开始验证
@@ -20,5 +22,6 @@ const timeFormat = ref<string>(props.valueFormat ?? 'HH:mm');
20
22
  <template>
21
23
  <TimePicker class="w-full"
22
24
  :class="[errInfo?.errClass === 'error' ? 'error !border-red-300 shadow-[0_0_3px_0px_#ff4d4f]' : '']"
23
- :placeholder="'请选择' + labelText" :locale="locale" :valueFormat="timeFormat" @blur="onBlur" />
25
+ :placeholder="t('webui.common.pleaseSelect') + labelText" :locale="locale" :valueFormat="timeFormat"
26
+ @blur="onBlur" />
24
27
  </template>
@@ -1,5 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import { ref, watch, onMounted, PropType, onActivated } from 'vue';
3
+ import { $t } from '@/locales/index';
3
4
  import { TreeSelect } from 'ant-design-vue';
4
5
  import type { SelectValue, TreeControl, TreeNode } from '@/index';
5
6
  import { queryTree, useInputFactory } from '@/index';
@@ -116,7 +117,8 @@ const onClear = () => {
116
117
 
117
118
  <template>
118
119
  <TreeSelect :class="[errInfo?.errClass]" tree-line :multiple="multiple" :tree-default-expanded-keys="['-']"
119
- v-model:value="currentValue" :tree-data="selectTreeData" :placeholder="'请选择' + labelText" class="w-full"
120
+ v-model:value="currentValue" :tree-data="selectTreeData"
121
+ :placeholder="$t('webui.components.form.treeSelect.pleaseSelect', [labelText])" class="w-full"
120
122
  @change="handleChange" @clear="onClear" v-bind="$attrs" />
121
123
  </template>
122
124
  <style scoped>
@@ -2,6 +2,7 @@
2
2
  import { ToolIcon } from '../../common';
3
3
  import { computed, ref, watch } from 'vue';
4
4
  import message from 'vue-m-message';
5
+ import { useI18n } from 'vue-i18n';
5
6
  import type { UploadProps } from 'ant-design-vue';
6
7
  import { Upload, Image } from 'ant-design-vue';
7
8
  import { UploadFile, path } from '@/index';
@@ -85,6 +86,7 @@ const props = withDefaults(defineProps<ImageListProps>(), {
85
86
 
86
87
  const inputFactory = useInputFactory();
87
88
  const { errInfo } = inputFactory;
89
+ const { t } = useI18n();
88
90
 
89
91
  // Upload 组件的文件列表更新
90
92
  const displayList = ref<UploadFile[]>(props.fileList);
@@ -101,14 +103,14 @@ const validateFile = (file: File): boolean => {
101
103
  if (props.fileExt && props.fileExt.length > 0) {
102
104
  const extension = file.name.split('.').pop()?.toLowerCase() || '';
103
105
  if (!props.fileExt.includes(extension)) {
104
- message.error('文件类型不支持');
106
+ message.error(t('webui.components.form.upload.unsupportedFileType'));
105
107
  return false;
106
108
  }
107
109
  }
108
110
 
109
111
  // 文件大小验证
110
112
  if (file.size / 1024 / 1024 > props.maxFileSize) {
111
- message.error(`文件大小超过 ${props.maxFileSize}MB 限制`);
113
+ message.error(t('webui.components.form.upload.fileSizeExceeded', [props.maxFileSize]));
112
114
  return false;
113
115
  }
114
116
 
@@ -161,7 +163,7 @@ const handleFileSelect = async (newFiles: any[]) => {
161
163
 
162
164
  // 检查是否达到最大数量限制
163
165
  if (isMaxCountReached()) {
164
- message.error(`最多上传 ${props.maxCount} 个文件`);
166
+ message.error(t('webui.components.form.upload.maxFileCount', [props.maxCount]));
165
167
  hasError = true;
166
168
  break;
167
169
  }
@@ -260,7 +262,7 @@ const removeFile = (index: number) => {
260
262
  },
261
263
  }).then((res) => {
262
264
  if (res && res.status === ResStatus.SUCCESS) {
263
- message.success('删除文件成功!');
265
+ message.success(t('webui.common.success'));
264
266
  displayList.value.splice(index, 1);
265
267
  }
266
268
  });
@@ -342,12 +344,12 @@ const hoveredIndex = ref(-1);
342
344
  <div v-if="previewUrl" class="flex items-center text-white cursor-pointer hover:text-blue-400"
343
345
  @click="previewFile(index)">
344
346
  <ToolIcon icon="icon-eye" clickable />
345
- <span class="text-sm ml-1">预览</span>
347
+ <span class="text-sm ml-1">{{ t('webui.components.form.upload.preview') }}</span>
346
348
  </div>
347
349
  <div v-if="showDelete !== false" class="flex items-center text-white cursor-pointer hover:text-red-400"
348
350
  @click="removeFile(index)">
349
351
  <ToolIcon icon="icon-new" :angle="45" clickable />
350
- <span class="text-sm ml-1">删除</span>
352
+ <span class="text-sm ml-1">{{ t('webui.components.form.upload.delete') }}</span>
351
353
  </div>
352
354
  </div>
353
355
  </div>
@@ -369,9 +369,9 @@ const confirmIndex = (index: number) => {
369
369
 
370
370
  const getPlaceholder = (): string => {
371
371
  const typeMsg =
372
- props.fileExt && props.fileExt.length && props.fileExtTip ? `文件必须为 ${props.fileExt.join('/')}` : '';
373
- const sizeMsg = props.maxFileSize !== 0 && props.maxFileSizeTip ? `单文件最大 ${props.maxFileSize}MB` : '';
374
- const countMsg = props.maxCount !== 0 && props.maxCountTip ? `最多 ${props.maxCount} 个文件` : '';
372
+ props.fileExt && props.fileExt.length && props.fileExtTip ? $t('webui.components.form.upload.fileTypeRequired', [props.fileExt.join('/')]) : '';
373
+ const sizeMsg = props.maxFileSize !== 0 && props.maxFileSizeTip ? $t('webui.components.form.upload.singleFileMax', [props.maxFileSize]) : '';
374
+ const countMsg = props.maxCount !== 0 && props.maxCountTip ? $t('webui.components.form.upload.maxFiles', [props.maxCount]) : '';
375
375
 
376
376
  return [sizeMsg, typeMsg, countMsg].filter(Boolean).join(',');
377
377
  };
@@ -473,7 +473,7 @@ const getStatus = (status?: UploadStatus) => {
473
473
  clickable @click="previewFile(index)" />
474
474
  <span v-if="showActionText" class="mr-2 text-sm text-nowrap"
475
475
  v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:preview' }"
476
- @click="previewFile(index)">预览</span>
476
+ @click="previewFile(index)">{{ $t('webui.common.preview') }}</span>
477
477
  </Tooltip>
478
478
  </div>
479
479
  <div v-if="showDelete !== false"
@@ -487,7 +487,8 @@ const getStatus = (status?: UploadStatus) => {
487
487
  <ToolIcon icon="icon-new" :angle="45"
488
488
  v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }" clickable />
489
489
  <span v-if="showActionText" class="text-sm text-nowrap"
490
- v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }">删除</span>
490
+ v-auth="{ role: ['Super', 'Admin'], permit: ':uploadlist:delete' }">{{
491
+ $t('webui.common.delete') }}</span>
491
492
  </div>
492
493
  </Tooltip>
493
494
  </Popconfirm>
@@ -1,13 +1,24 @@
1
1
  <script lang="ts" setup>
2
2
  import { ref, onMounted } from 'vue'
3
+ import { getLang } from '@/locales/index'
4
+
3
5
  const DateTime = ref("")
6
+
4
7
  onMounted(() => {
5
8
  setInterval(() => {
6
9
  const curTime = new Date()
7
- const options: any = { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' };
8
- const formattedTime = curTime.toLocaleString(undefined, options).replace(/\//g, '-').replace(',', '');
9
- DateTime.value = curTime.getFullYear() + "-" + formattedTime
10
- }, 1000);
10
+ const lang = getLang()
11
+ const options: Intl.DateTimeFormatOptions = {
12
+ year: 'numeric',
13
+ month: '2-digit',
14
+ day: '2-digit',
15
+ hour: '2-digit',
16
+ minute: '2-digit',
17
+ second: '2-digit',
18
+ hour12: false
19
+ }
20
+ DateTime.value = curTime.toLocaleString(lang, options)
21
+ }, 1000)
11
22
  })
12
23
  </script>
13
24
 
@@ -17,6 +17,8 @@ const handleLangChange = async (lang: string) => {
17
17
  try {
18
18
  await setLang(lang);
19
19
  currentLang.value = lang;
20
+ // 刷新整个页面以确保语言完全生效
21
+ window.location.reload();
20
22
  } catch (error) {
21
23
  console.error('Failed to change language:', error);
22
24
  }