feat: plugin-layout的页签信息支持国际化

This commit is contained in:
harrywan 2024-09-04 15:27:44 +08:00
parent 6d64a0997a
commit 0a78b72fdf
13 changed files with 127 additions and 65 deletions

View File

@ -31,5 +31,8 @@
"source": "./fixtures/output/**/*.*",
"target": "./fixtures/input/<base>"
}
],
"cSpell.words": [
"unref"
]
}

View File

@ -30,6 +30,13 @@ export default (api) => {
const absRuntimeFilePath = join(namespace, 'runtime.js');
api.register({
key: 'addExtraLocales',
fn: () => [
join(api.paths.absTmpPath, namespace, 'locales'),
],
});
api.onGenerateFiles(async () => {
// .fes配置
const userConfig = {

View File

@ -1,4 +1,4 @@
const getMetaByName = (config, name) => {
function getMetaByName(config, name) {
let res = {};
if (Array.isArray(config)) {
for (let i = 0; i < config.length; i++) {
@ -17,9 +17,9 @@ const getMetaByName = (config, name) => {
}
}
return res;
};
}
const fillMenuByRoute = (menuConfig, routeConfig, dep = 0) => {
function fillMenuByRoute(menuConfig, routeConfig, dep = 0) {
dep += 1;
if (dep > 3) {
console.warn('[plugin-layout]: 菜单层级最好不要超出三层!');
@ -44,6 +44,6 @@ const fillMenuByRoute = (menuConfig, routeConfig, dep = 0) => {
});
}
return arr;
};
}
export default fillMenuByRoute;

View File

@ -1,6 +1,6 @@
import { plugin } from '@@/core/coreExports';
export const transTitle = (name) => {
export function transTitle(name) {
if (!/^\$\S+$/.test(name)) {
return name;
}
@ -10,10 +10,10 @@ export const transTitle = (name) => {
return t(name.slice(1));
}
return name;
};
}
export const transform = (menus) =>
menus.map((menu) => {
export function transform(menus) {
return menus.map((menu) => {
const copy = {
...menu,
label: transTitle(menu.label),
@ -23,3 +23,4 @@ export const transform = (menus) =>
}
return copy;
});
}

View File

@ -2,7 +2,7 @@ const isStr = function (str) {
return typeof str === 'string';
};
export const isValid = (elm) => {
export function isValid(elm) {
if (elm.nodeType === 1) {
if (elm.nodeName.toLowerCase() === 'script') {
return false;
@ -22,9 +22,9 @@ export const isValid = (elm) => {
}
}
return true;
};
}
export const validateContent = (svgContent) => {
export function validateContent(svgContent) {
const div = document.createElement('div');
div.innerHTML = svgContent;
@ -46,4 +46,4 @@ export const validateContent = (svgContent) => {
}
}
return '';
};
}

View File

@ -1,8 +1,9 @@
export const flatNodes = (nodes = []) =>
nodes.reduce((res, node) => {
export function flatNodes(nodes = []) {
return nodes.reduce((res, node) => {
res.push(node);
if (node.children) {
res = res.concat(flatNodes(node.children));
}
return res;
}, []);
}

View File

@ -0,0 +1,6 @@
export default {
pluginLayout: {
closeOtherPage: 'Close Other Page',
reloadPage: 'Reload Page',
},
};

View File

@ -0,0 +1,6 @@
export default {
pluginLayout: {
closeOtherPage: '关闭其他页签',
reloadPage: '刷新当前页签',
},
};

View File

@ -7,7 +7,7 @@
type="card"
class="layout-content-tabs"
@close="handleCloseTab"
@update:modelValue="switchPage"
@update:model-value="switchPage"
>
<FTabPane v-for="page in pageList" :key="page.path" :value="page.path" :closable="pageList.length > 1">
<template #tab>
@ -30,7 +30,7 @@
import { computed, ref, unref } from 'vue';
import { FDropdown, FTabPane, FTabs } from '@fesjs/fes-design';
import { MoreOutlined, ReloadOutlined } from '@fesjs/fes-design/icon';
import { useRoute, useRouter } from '@@/core/coreExports';
import { plugin, useRoute, useRouter } from '@@/core/coreExports';
import { transTitle } from '../helpers/pluginLocale';
import { deleteTitle, getTitle } from '../useTitle';
import { useLayout } from '../useLayout';
@ -71,26 +71,45 @@ export default {
};
const pageList = ref([createPage(router.currentRoute.value)]);
const actions = [
{
value: 'closeOtherPage',
label: '关闭其他页签',
},
{
value: 'reloadPage',
label: '刷新当前页签',
},
];
const actions = computed(() => {
const sharedLocale = plugin.getShared('locale');
if (sharedLocale) {
const { t } = sharedLocale.locale;
return [
{
value: 'closeOtherPage',
label: t('pluginLayout.closeOtherPage'),
},
{
value: 'reloadPage',
label: t('pluginLayout.reloadPage'),
},
];
}
return [
{
value: 'closeOtherPage',
label: '关闭其他页签',
},
{
value: 'reloadPage',
label: '刷新当前页签',
},
];
});
const findPage = path => pageList.value.find(item => unref(item.path) === unref(path));
router.beforeEach((to) => {
const page = findPage(to.path);
if (!page)
if (!page) {
pageList.value = [...pageList.value, createPage(to)];
}
else
else {
page.route = to;
}
return true;
});
@ -113,11 +132,13 @@ export default {
const index = list.indexOf(selectedPage);
if (route.path === selectedPage.path) {
if (list.length > 1) {
if (list.length - 1 === index)
if (list.length - 1 === index) {
await switchPage(list[index - 1].path);
}
else
else {
await switchPage(list[index + 1].path);
}
}
}
list.splice(index, 1);
@ -129,8 +150,9 @@ export default {
const reloadPage = (path) => {
const selectedPage = findPage(path || unref(route.path));
if (selectedPage)
if (selectedPage) {
selectedPage.key = getKey();
}
};
const closeOtherPage = (path) => {
const selectedPage = findPage(path || unref(route.path));
@ -139,8 +161,9 @@ export default {
};
const getPageKey = (_route) => {
const selectedPage = findPage(_route.path);
if (selectedPage)
if (selectedPage) {
return selectedPage.key;
}
return '';
};

View File

@ -1,5 +1,5 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { name } from '../package.json';
const namespace = 'plugin-locale';
@ -32,10 +32,17 @@ export default (api) => {
return join(api.paths.absSrcPath, api.config.singular ? 'locale' : 'locales');
}
api.register({
key: 'addExtraLocales',
fn: () => [
getLocaleFileBasePath(),
],
});
// 监听 locale 文件改变,重新生成文件
api.addTmpGenerateWatcherPaths(getLocaleFileBasePath);
api.onGenerateFiles(() => {
api.onGenerateFiles(async () => {
// .fes配置
const userConfig = {
locale: 'zh-CN', // default locale
@ -45,9 +52,13 @@ export default (api) => {
...api.config.locale,
};
const localeConfigFileBasePath = getLocaleFileBasePath();
const additionalLocales = await api.applyPlugins({
key: 'addExtraLocales',
type: api.ApplyPluginsType.add,
initialValue: [],
});
const { files, locales } = getLocales(localeConfigFileBasePath);
const { files, locales } = getLocales(additionalLocales);
const { baseNavigator, ...otherConfig } = userConfig;
@ -55,7 +66,7 @@ export default (api) => {
path: join(namespace, 'locales.js'),
content: Mustache.render(readFileSync(join(__dirname, 'runtime/locales.js.tpl'), 'utf-8'), {
REPLACE_IMPORTS: files,
REPLACE_LOCALES: locales.map((item) => ({
REPLACE_LOCALES: locales.map(item => ({
locale: item.locale,
importNames: item.importNames.join(', '),
})),

View File

@ -1,6 +1,6 @@
import { plugin } from '@@/core/coreExports';
// eslint-disable-next-line import/extensions
import { useI18n, locale, install } from './core';
import { install, locale, useI18n } from './core';
import SelectLang from './views/SelectLang.vue';
// 共享出去

View File

@ -1,11 +1,11 @@
<template>
<FTooltip v-model="isOpened" popperClass="lang-popper" mode="popover">
<FTooltip v-model="isOpened" popper-class="lang-popper" mode="popover">
<div class="lang-icon">
<LanguageOutlined />
</div>
<template #content>
<FScrollbar height="274" class="lang-container">
<div v-for="item in configs" :key="item.lang" :class="['lang-option', item.lang === locale && 'is-selected']" @click="handleSelect(item)">
<div v-for="item in configs" :key="item.lang" class="lang-option" :class="[item.lang === locale && 'is-selected']" @click="handleSelect(item)">
<span>{{ item.icon }}</span>
<span>{{ item.label }}</span>
</div>
@ -15,12 +15,12 @@
</template>
<script>
import { FTooltip, FScrollbar } from '@fesjs/fes-design';
import { FScrollbar, FTooltip } from '@fesjs/fes-design';
import { LanguageOutlined } from '@fesjs/fes-design/icon';
import { useI18n } from 'vue-i18n';
import { computed, ref } from 'vue';
import langUConfigMap from '../langUConfigMap';
// eslint-disable-next-line import/extensions
import { locale as _locale } from '../core';
export default {
@ -55,11 +55,13 @@ export default {
},
};
</script>
<style>
.fes-tooltip.fes-tooltip-popover.lang-popper {
padding: 0;
}
</style>
<style lang="less" scoped>
.lang-icon {
display: flex;

View File

@ -1,4 +1,4 @@
import { join, basename } from 'path';
import { basename, join } from 'node:path';
import { glob, winPath } from '@fesjs/utils';
const ignore = /\.(d\.ts|\.test\.(js|ts))$/;
@ -15,30 +15,32 @@ const getRouteName = function (path) {
.replace(/\[...([a-zA-Z]*)\]/, 'FUZZYMATCH-$1');
};
export function getLocales(cwd) {
export function getLocales(cwdArray) {
const map = {};
const files = [];
glob.sync('**/*.js', {
cwd,
})
.filter((file) => !ignore.test(file))
.forEach((fileName) => {
const locale = basename(fileName, '.js');
const importName = getRouteName(fileName).replace('.js', '');
const result = {
importName,
// import语法的路径必须处理win
path: winPath(join(cwd, fileName)),
};
files.push(result);
if (!map[locale]) {
map[locale] = [];
}
map[locale].push(importName);
});
cwdArray.forEach((cwd) => {
glob.sync('**/*.js', {
cwd,
})
.filter(file => !ignore.test(file))
.forEach((fileName) => {
const locale = basename(fileName, '.js');
const importName = getRouteName(fileName).replace('.js', '');
const result = {
importName,
// import语法的路径必须处理win
path: winPath(join(cwd, fileName)),
};
files.push(result);
if (!map[locale]) {
map[locale] = [];
}
map[locale].push(importName);
});
});
return {
locales: Object.keys(map).map((key) => ({ locale: key, importNames: map[key] })),
locales: Object.keys(map).map(key => ({ locale: key, importNames: map[key] })),
files,
};
}