perf: 框架核心代码国际化全量覆盖(仅需处理业务代码国际化即可) (#1082)

This commit is contained in:
xiaoming
2024-04-17 15:41:37 +08:00
committed by GitHub
parent d1d2bcaa11
commit a402d33b89
28 changed files with 254 additions and 99 deletions

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import Footer from "./footer/index.vue";
import { useGlobal, isNumber } from "@pureadmin/utils";
import KeepAliveFrame from "./keepAliveFrame/index.vue";
@@ -10,6 +11,7 @@ const props = defineProps({
fixedHeader: Boolean
});
const { t } = useI18n();
const { $storage, $config } = useGlobal<GlobalPropertiesApi>();
const isKeepAlive = computed(() => {
@@ -121,7 +123,7 @@ const transitionMain = defineComponent({
}"
>
<el-backtop
title="回到顶部"
:title="t('buttons.pureBackTop')"
target=".app-main .el-scrollbar__wrap"
>
<backTop />

View File

@@ -113,7 +113,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
:title="t('buttons.pureSystemSet')"
:title="t('buttons.pureOpenSystemSet')"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />

View File

@@ -1,9 +1,11 @@
<script setup lang="ts">
import { ref } from "vue";
import { useI18n } from "vue-i18n";
import { noticesData } from "./data";
import NoticeList from "./noticeList.vue";
import Bell from "@iconify-icons/ep/bell";
const { t } = useI18n();
const noticesNum = ref(0);
const notices = ref(noticesData);
const activeKey = ref(noticesData[0].key);
@@ -30,7 +32,7 @@ notices.value.map(v => (noticesNum.value += v.list.length));
>
<el-empty
v-if="notices.length === 0"
description="暂无消息"
:description="t('status.pureNoMessage')"
:image-size="60"
/>
<span v-else>

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { PropType } from "vue";
import { ListItem } from "./data";
import { useI18n } from "vue-i18n";
import NoticeItem from "./noticeItem.vue";
const props = defineProps({
@@ -9,6 +10,8 @@ const props = defineProps({
default: () => []
}
});
const { t } = useI18n();
</script>
<template>
@@ -19,5 +22,5 @@ const props = defineProps({
:noticeItem="item"
/>
</div>
<el-empty v-else description="暂无消息" />
<el-empty v-else :description="t('status.pureNoMessage')" />
</template>

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
@@ -25,6 +26,7 @@ const iconClass = computed(() => {
];
});
const { t } = useI18n();
const { onReset } = useDataThemeChange();
onClickOutside(target, (event: any) => {
@@ -51,10 +53,12 @@ onBeforeUnmount(() => {
<div
class="project-configuration border-b-[1px] border-solid border-[var(--pure-border-color)]"
>
<h4 class="dark:text-white">项目配置</h4>
<h4 class="dark:text-white">
{{ t("panel.pureSystemSet") }}
</h4>
<span
v-tippy="{
content: '关闭配置',
content: t('panel.pureCloseSystemSet'),
placement: 'bottom-start',
zIndex: 41000
}"
@@ -78,7 +82,7 @@ onBeforeUnmount(() => {
>
<el-button
v-tippy="{
content: '清空缓存并返回登录页',
content: t('panel.pureClearCacheAndToLogin'),
placement: 'left-start',
zIndex: 41000
}"
@@ -87,7 +91,7 @@ onBeforeUnmount(() => {
bg
@click="onReset"
>
清空缓存
{{ t("panel.pureClearCache") }}
</el-button>
</div>
</div>

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import { useNav } from "@/layout/hooks/useNav";
import mdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
import enterOutlined from "@/assets/svg/enter_outlined.svg?component";
@@ -9,6 +10,7 @@ const props = withDefaults(defineProps<{ total: number }>(), {
total: 0
});
const { t } = useI18n();
const { device } = useNav();
</script>
@@ -16,22 +18,22 @@ const { device } = useNav();
<div class="search-footer text-[#333] dark:text-white">
<span class="search-footer-item">
<enterOutlined class="icon" />
确认
{{ t("buttons.pureConfirm") }}
</span>
<span class="search-footer-item">
<IconifyIconOffline :icon="ArrowUpLine" class="icon" />
<IconifyIconOffline :icon="ArrowDownLine" class="icon" />
切换
{{ t("buttons.pureSwitch") }}
</span>
<span class="search-footer-item">
<mdiKeyboardEsc class="icon" />
关闭
{{ t("buttons.pureClose") }}
</span>
<p
v-if="device !== 'mobile' && props.total > 0"
class="search-footer-total"
>
{{ props.total }}
{{ `${t("search.pureTotal")} ${props.total}` }}
</p>
</div>
</template>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import Sortable from "sortablejs";
import { useI18n } from "vue-i18n";
import SearchHistoryItem from "./SearchHistoryItem.vue";
import type { optionsItem, dragItem, Props } from "../types";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
@@ -19,6 +20,7 @@ const innerHeight = ref();
/** 判断是否停止鼠标移入事件处理 */
const stopMouseEvent = ref(false);
const { t } = useI18n();
const emit = defineEmits<Emits>();
const instance = getCurrentInstance()!;
const props = withDefaults(defineProps<Props>(), {});
@@ -141,7 +143,9 @@ defineExpose({ handleScroll });
<template>
<div ref="historyRef" class="history">
<template v-if="historyList.length">
<div :style="titleStyle">搜索历史</div>
<div :style="titleStyle">
{{ t("search.pureHistory") }}
</div>
<div
v-for="(item, index) in historyList"
:key="item.path"
@@ -160,7 +164,9 @@ defineExpose({ handleScroll });
</template>
<template v-if="collectList.length">
<div :style="titleStyle">
收藏{{ collectList.length > 1 ? "(可拖拽排序)" : "" }}
{{
`${t("search.pureCollect")}${collectList.length > 1 ? t("search.pureDragSort") : ""}`
}}
</div>
<div class="collect-container">
<div

View File

@@ -6,8 +6,8 @@ import { useRouter } from "vue-router";
import SearchResult from "./SearchResult.vue";
import SearchFooter from "./SearchFooter.vue";
import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n";
import SearchHistory from "./SearchHistory.vue";
import { transformI18n, $t } from "@/plugins/i18n";
import type { optionsItem, dragItem } from "../types";
import { ref, computed, shallowRef, watch } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core";
@@ -29,7 +29,7 @@ const emit = defineEmits<Emits>();
const props = withDefaults(defineProps<Props>(), {});
const router = useRouter();
const { locale } = useI18n();
const { t, locale } = useI18n();
const HISTORY_TYPE = "history";
const COLLECT_TYPE = "collect";
@@ -293,7 +293,7 @@ onKeyStroke("ArrowDown", handleDown);
v-model="keyword"
size="large"
clearable
placeholder="搜索菜单(中文模式下支持拼音搜索)"
:placeholder="t('search.purePlaceholder')"
@input="handleSearch"
>
<template #prefix>
@@ -305,7 +305,7 @@ onKeyStroke("ArrowDown", handleDown);
</el-input>
<div class="search-content">
<el-scrollbar ref="scrollbarRef" max-height="calc(90vh - 140px)">
<el-empty v-if="showEmpty" description="暂无搜索结果" />
<el-empty v-if="showEmpty" :description="t('search.pureEmpty')" />
<SearchHistory
v-if="showSearchHistory"
ref="historyRef"

View File

@@ -9,6 +9,7 @@ import {
onUnmounted,
onBeforeMount
} from "vue";
import { useI18n } from "vue-i18n";
import panel from "../panel/index.vue";
import { emitter } from "@/utils/mitt";
import { useNav } from "@/layout/hooks/useNav";
@@ -26,6 +27,7 @@ import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import systemIcon from "@/assets/svg/system.svg?component";
const { t } = useI18n();
const { device } = useNav();
const { isDark } = useDark();
const { $storage } = useGlobal<GlobalPropertiesApi>();
@@ -145,18 +147,20 @@ function setFalse(Doms): any {
}
/** 页宽 */
const stretchTypeOptions: Array<OptionsType> = [
{
label: "固定",
tip: "紧凑页面,轻松找到所需信息",
value: "fixed"
},
{
label: "自定义",
tip: "最小1280、最大1600",
value: "custom"
}
];
const stretchTypeOptions = computed<Array<OptionsType>>(() => {
return [
{
label: t("panel.pureStretchFixed"),
tip: t("panel.pureStretchFixedTip"),
value: "fixed"
},
{
label: t("panel.pureStretchCustom"),
tip: t("panel.pureStretchCustomTip"),
value: "custom"
}
];
});
const setStretch = value => {
settings.stretch = value;
@@ -194,41 +198,43 @@ const pClass = computed(() => {
const themeOptions = computed<Array<OptionsType>>(() => {
return [
{
label: "浅色",
label: t("panel.pureOverallStyleLight"),
icon: dayIcon,
theme: "light",
tip: "清新启航,点亮舒适的工作界面",
tip: t("panel.pureOverallStyleLightTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
},
{
label: "深色",
label: t("panel.pureOverallStyleDark"),
icon: darkIcon,
theme: "dark",
tip: "月光序曲,沉醉于夜的静谧雅致",
tip: t("panel.pureOverallStyleDarkTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
},
{
label: "自动",
label: t("panel.pureOverallStyleSystem"),
icon: systemIcon,
theme: "system",
tip: "同步时光,界面随晨昏自然呼应",
tip: t("panel.pureOverallStyleSystemTip"),
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
}
];
});
const markOptions: Array<OptionsType> = [
{
label: "灵动",
tip: "灵动标签,添趣生辉",
value: "smart"
},
{
label: "卡片",
tip: "卡片标签,高效浏览",
value: "card"
}
];
const markOptions = computed<Array<OptionsType>>(() => {
return [
{
label: t("panel.pureTagsStyleSmart"),
tip: t("panel.pureTagsStyleSmartTip"),
value: "smart"
},
{
label: t("panel.pureTagsStyleCard"),
tip: t("panel.pureTagsStyleCardTip"),
value: "card"
}
];
});
/** 设置导航模式 */
function setLayoutModel(layout: string) {
@@ -291,7 +297,7 @@ function watchSystemThemeChange() {
}
onBeforeMount(() => {
/* 初始化项目配置 */
/* 初始化系统配置 */
nextTick(() => {
watchSystemThemeChange();
settings.greyVal &&
@@ -309,8 +315,9 @@ onUnmounted(() => removeMatchMedia);
<template>
<panel>
<div class="p-5">
<p :class="pClass">整体风格</p>
<p :class="pClass">{{ t("panel.pureOverallStyle") }}</p>
<Segmented
resize
class="select-none"
:modelValue="overallStyle === 'system' ? 2 : dataTheme ? 1 : 0"
:options="themeOptions"
@@ -326,7 +333,7 @@ onUnmounted(() => removeMatchMedia);
"
/>
<p :class="['mt-5', pClass]">主题色</p>
<p :class="['mt-5', pClass]">{{ t("panel.pureThemeColor") }}</p>
<ul class="theme-color">
<li
v-for="(item, index) in themeColors"
@@ -345,12 +352,12 @@ onUnmounted(() => removeMatchMedia);
</li>
</ul>
<p :class="['mt-5', pClass]">导航模式</p>
<p :class="['mt-5', pClass]">{{ t("panel.pureLayoutModel") }}</p>
<ul class="pure-theme">
<li
ref="verticalRef"
v-tippy="{
content: '左侧菜单,亲切熟悉',
content: t('panel.pureVerticalTip'),
zIndex: 41000
}"
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
@@ -363,7 +370,7 @@ onUnmounted(() => removeMatchMedia);
v-if="device !== 'mobile'"
ref="horizontalRef"
v-tippy="{
content: '顶部菜单,简洁概览',
content: t('panel.pureHorizontalTip'),
zIndex: 41000
}"
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
@@ -376,7 +383,7 @@ onUnmounted(() => removeMatchMedia);
v-if="device !== 'mobile'"
ref="mixRef"
v-tippy="{
content: '混合菜单,灵活多变',
content: t('panel.pureMixTip'),
zIndex: 41000
}"
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
@@ -388,8 +395,9 @@ onUnmounted(() => removeMatchMedia);
</ul>
<span v-if="useAppStoreHook().getViewportWidth > 1280">
<p :class="['mt-5', pClass]">页宽</p>
<p :class="['mt-5', pClass]">{{ t("panel.pureStretch") }}</p>
<Segmented
resize
class="mb-2 select-none"
:modelValue="isNumber(settings.stretch) ? 1 : 0"
:options="stretchTypeOptions"
@@ -430,53 +438,56 @@ onUnmounted(() => removeMatchMedia);
</button>
</span>
<p :class="['mt-4', pClass]">页签风格</p>
<p :class="['mt-4', pClass]">{{ t("panel.pureTagsStyle") }}</p>
<Segmented
resize
class="select-none"
:modelValue="markValue === 'smart' ? 0 : 1"
:options="markOptions"
@change="onChange"
/>
<p class="mt-5 font-medium text-sm dark:text-white">界面显示</p>
<p class="mt-5 font-medium text-sm dark:text-white">
{{ t("panel.pureInterfaceDisplay") }}
</p>
<ul class="setting">
<li>
<span class="dark:text-white">灰色模式</span>
<span class="dark:text-white">{{ t("panel.pureGreyModel") }}</span>
<el-switch
v-model="settings.greyVal"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="greyChange"
/>
</li>
<li>
<span class="dark:text-white">色弱模式</span>
<span class="dark:text-white">{{ t("panel.pureWeakModel") }}</span>
<el-switch
v-model="settings.weakVal"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="weekChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏标签页</span>
<span class="dark:text-white">{{ t("panel.pureHiddenTags") }}</span>
<el-switch
v-model="settings.tabsVal"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="tagsChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏页脚</span>
<span class="dark:text-white">{{ t("panel.pureHiddenFooter") }}</span>
<el-switch
v-model="settings.hideFooter"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="hideFooterChange"
/>
</li>
@@ -487,18 +498,20 @@ onUnmounted(() => removeMatchMedia);
inline-prompt
:active-value="true"
:inactive-value="false"
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="logoChange"
/>
</li>
<li>
<span class="dark:text-white">页签持久化</span>
<span class="dark:text-white">
{{ t("panel.pureMultiTagsCache") }}
</span>
<el-switch
v-model="settings.multiTagsCache"
inline-prompt
active-text=""
inactive-text=""
:active-text="t('buttons.pureOpenText')"
:inactive-text="t('buttons.pureCloseText')"
@change="multiTagsCacheChange"
/>
</li>

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { useGlobal } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav";
@@ -13,6 +14,7 @@ const props = withDefaults(defineProps<Props>(), {
isActive: false
});
const { t } = useI18n();
const { tooltipEffect } = useNav();
const iconClass = computed(() => {
@@ -34,7 +36,9 @@ const toggleClick = () => {
<template>
<div
v-tippy="{
content: props.isActive ? '点击折叠' : '点击展开',
content: props.isActive
? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand'),
theme: tooltipEffect,
hideOnClick: 'toggle',
placement: 'right'

View File

@@ -129,7 +129,7 @@ nextTick(() => {
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
:title="t('buttons.pureSystemSet')"
:title="t('buttons.pureOpenSystemSet')"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />

View File

@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { useGlobal } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav";
@@ -13,6 +14,7 @@ const props = withDefaults(defineProps<Props>(), {
isActive: false
});
const { t } = useI18n();
const { tooltipEffect } = useNav();
const iconClass = computed(() => {
@@ -44,7 +46,9 @@ const toggleClick = () => {
<div class="left-collapse">
<IconifyIconOffline
v-tippy="{
content: props.isActive ? '点击折叠' : '点击展开',
content: props.isActive
? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand'),
theme: tooltipEffect,
hideOnClick: 'toggle',
placement: 'right'

View File

@@ -162,7 +162,7 @@ watch(
</el-dropdown>
<span
class="set-icon navbar-bg-hover"
:title="t('buttons.pureSystemSet')"
:title="t('buttons.pureOpenSystemSet')"
@click="onPanel"
>
<IconifyIconOffline :icon="Setting" />

View File

@@ -1,4 +1,5 @@
<script setup lang="ts">
import { useI18n } from "vue-i18n";
import MenuFold from "@iconify-icons/ri/menu-fold-fill";
import MenuUnfold from "@iconify-icons/ri/menu-unfold-fill";
@@ -10,6 +11,8 @@ const props = withDefaults(defineProps<Props>(), {
isActive: false
});
const { t } = useI18n();
const emit = defineEmits<{
(e: "toggleClick"): void;
}>();
@@ -22,7 +25,11 @@ const toggleClick = () => {
<template>
<div
class="px-3 mr-1 navbar-bg-hover"
:title="props.isActive ? '点击折叠' : '点击展开'"
:title="
props.isActive
? t('buttons.pureClickCollapse')
: t('buttons.pureClickExpand')
"
@click="toggleClick"
>
<IconifyIconOffline

View File

@@ -17,7 +17,8 @@ import { usePermissionStoreHook } from "@/store/modules/permission";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
const errorInfo = "当前路由配置不正确,请检查配置";
const errorInfo =
"The current routing configuration is incorrect, please check the configuration";
export function useNav() {
const pureApp = useAppStoreHook();

View File

@@ -3,6 +3,7 @@ import "animate.css";
// 引入 src/components/ReIcon/src/offlineIcon.ts 文件中所有使用addIcon添加过的本地图标
import "@/components/ReIcon/src/offlineIcon";
import { setType } from "./types";
import { useI18n } from "vue-i18n";
import { useLayout } from "./hooks/useLayout";
import { useAppStoreHook } from "@/store/modules/app";
import { useSettingStoreHook } from "@/store/modules/settings";
@@ -31,6 +32,7 @@ import Vertical from "./components/sidebar/vertical.vue";
import Horizontal from "./components/sidebar/horizontal.vue";
import backTop from "@/assets/svg/back_top.svg?component";
const { t } = useI18n();
const appWrapperRef = ref();
const { isDark } = useDark();
const { layout } = useLayout();
@@ -185,7 +187,7 @@ const layoutHeader = defineComponent({
</div>
<el-scrollbar v-else>
<el-backtop
title="回到顶部"
:title="t('buttons.pureBackTop')"
target=".main-container .el-scrollbar__wrap"
>
<backTop />