mirror of
https://gitee.com/WeBank/fes.js.git
synced 2024-12-01 19:27:47 +08:00
feat: plugin-layout的页签信息支持国际化
This commit is contained in:
parent
6d64a0997a
commit
0a78b72fdf
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -31,5 +31,8 @@
|
|||||||
"source": "./fixtures/output/**/*.*",
|
"source": "./fixtures/output/**/*.*",
|
||||||
"target": "./fixtures/input/<base>"
|
"target": "./fixtures/input/<base>"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"cSpell.words": [
|
||||||
|
"unref"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,13 @@ export default (api) => {
|
|||||||
|
|
||||||
const absRuntimeFilePath = join(namespace, 'runtime.js');
|
const absRuntimeFilePath = join(namespace, 'runtime.js');
|
||||||
|
|
||||||
|
api.register({
|
||||||
|
key: 'addExtraLocales',
|
||||||
|
fn: () => [
|
||||||
|
join(api.paths.absTmpPath, namespace, 'locales'),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
api.onGenerateFiles(async () => {
|
api.onGenerateFiles(async () => {
|
||||||
// .fes配置
|
// .fes配置
|
||||||
const userConfig = {
|
const userConfig = {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const getMetaByName = (config, name) => {
|
function getMetaByName(config, name) {
|
||||||
let res = {};
|
let res = {};
|
||||||
if (Array.isArray(config)) {
|
if (Array.isArray(config)) {
|
||||||
for (let i = 0; i < config.length; i++) {
|
for (let i = 0; i < config.length; i++) {
|
||||||
@ -17,9 +17,9 @@ const getMetaByName = (config, name) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
};
|
}
|
||||||
|
|
||||||
const fillMenuByRoute = (menuConfig, routeConfig, dep = 0) => {
|
function fillMenuByRoute(menuConfig, routeConfig, dep = 0) {
|
||||||
dep += 1;
|
dep += 1;
|
||||||
if (dep > 3) {
|
if (dep > 3) {
|
||||||
console.warn('[plugin-layout]: 菜单层级最好不要超出三层!');
|
console.warn('[plugin-layout]: 菜单层级最好不要超出三层!');
|
||||||
@ -44,6 +44,6 @@ const fillMenuByRoute = (menuConfig, routeConfig, dep = 0) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default fillMenuByRoute;
|
export default fillMenuByRoute;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { plugin } from '@@/core/coreExports';
|
import { plugin } from '@@/core/coreExports';
|
||||||
|
|
||||||
export const transTitle = (name) => {
|
export function transTitle(name) {
|
||||||
if (!/^\$\S+$/.test(name)) {
|
if (!/^\$\S+$/.test(name)) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
@ -10,10 +10,10 @@ export const transTitle = (name) => {
|
|||||||
return t(name.slice(1));
|
return t(name.slice(1));
|
||||||
}
|
}
|
||||||
return name;
|
return name;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const transform = (menus) =>
|
export function transform(menus) {
|
||||||
menus.map((menu) => {
|
return menus.map((menu) => {
|
||||||
const copy = {
|
const copy = {
|
||||||
...menu,
|
...menu,
|
||||||
label: transTitle(menu.label),
|
label: transTitle(menu.label),
|
||||||
@ -23,3 +23,4 @@ export const transform = (menus) =>
|
|||||||
}
|
}
|
||||||
return copy;
|
return copy;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ const isStr = function (str) {
|
|||||||
return typeof str === 'string';
|
return typeof str === 'string';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isValid = (elm) => {
|
export function isValid(elm) {
|
||||||
if (elm.nodeType === 1) {
|
if (elm.nodeType === 1) {
|
||||||
if (elm.nodeName.toLowerCase() === 'script') {
|
if (elm.nodeName.toLowerCase() === 'script') {
|
||||||
return false;
|
return false;
|
||||||
@ -22,9 +22,9 @@ export const isValid = (elm) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
|
|
||||||
export const validateContent = (svgContent) => {
|
export function validateContent(svgContent) {
|
||||||
const div = document.createElement('div');
|
const div = document.createElement('div');
|
||||||
div.innerHTML = svgContent;
|
div.innerHTML = svgContent;
|
||||||
|
|
||||||
@ -46,4 +46,4 @@ export const validateContent = (svgContent) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
};
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
export const flatNodes = (nodes = []) =>
|
export function flatNodes(nodes = []) {
|
||||||
nodes.reduce((res, node) => {
|
return nodes.reduce((res, node) => {
|
||||||
res.push(node);
|
res.push(node);
|
||||||
if (node.children) {
|
if (node.children) {
|
||||||
res = res.concat(flatNodes(node.children));
|
res = res.concat(flatNodes(node.children));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}, []);
|
}, []);
|
||||||
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
pluginLayout: {
|
||||||
|
closeOtherPage: 'Close Other Page',
|
||||||
|
reloadPage: 'Reload Page',
|
||||||
|
},
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
pluginLayout: {
|
||||||
|
closeOtherPage: '关闭其他页签',
|
||||||
|
reloadPage: '刷新当前页签',
|
||||||
|
},
|
||||||
|
};
|
@ -7,7 +7,7 @@
|
|||||||
type="card"
|
type="card"
|
||||||
class="layout-content-tabs"
|
class="layout-content-tabs"
|
||||||
@close="handleCloseTab"
|
@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">
|
<FTabPane v-for="page in pageList" :key="page.path" :value="page.path" :closable="pageList.length > 1">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
import { computed, ref, unref } from 'vue';
|
import { computed, ref, unref } from 'vue';
|
||||||
import { FDropdown, FTabPane, FTabs } from '@fesjs/fes-design';
|
import { FDropdown, FTabPane, FTabs } from '@fesjs/fes-design';
|
||||||
import { MoreOutlined, ReloadOutlined } from '@fesjs/fes-design/icon';
|
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 { transTitle } from '../helpers/pluginLocale';
|
||||||
import { deleteTitle, getTitle } from '../useTitle';
|
import { deleteTitle, getTitle } from '../useTitle';
|
||||||
import { useLayout } from '../useLayout';
|
import { useLayout } from '../useLayout';
|
||||||
@ -71,26 +71,45 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const pageList = ref([createPage(router.currentRoute.value)]);
|
const pageList = ref([createPage(router.currentRoute.value)]);
|
||||||
const actions = [
|
|
||||||
{
|
const actions = computed(() => {
|
||||||
value: 'closeOtherPage',
|
const sharedLocale = plugin.getShared('locale');
|
||||||
label: '关闭其他页签',
|
if (sharedLocale) {
|
||||||
},
|
const { t } = sharedLocale.locale;
|
||||||
{
|
return [
|
||||||
value: 'reloadPage',
|
{
|
||||||
label: '刷新当前页签',
|
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));
|
const findPage = path => pageList.value.find(item => unref(item.path) === unref(path));
|
||||||
|
|
||||||
router.beforeEach((to) => {
|
router.beforeEach((to) => {
|
||||||
const page = findPage(to.path);
|
const page = findPage(to.path);
|
||||||
if (!page)
|
if (!page) {
|
||||||
pageList.value = [...pageList.value, createPage(to)];
|
pageList.value = [...pageList.value, createPage(to)];
|
||||||
|
}
|
||||||
|
|
||||||
else
|
else {
|
||||||
page.route = to;
|
page.route = to;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
@ -113,11 +132,13 @@ export default {
|
|||||||
const index = list.indexOf(selectedPage);
|
const index = list.indexOf(selectedPage);
|
||||||
if (route.path === selectedPage.path) {
|
if (route.path === selectedPage.path) {
|
||||||
if (list.length > 1) {
|
if (list.length > 1) {
|
||||||
if (list.length - 1 === index)
|
if (list.length - 1 === index) {
|
||||||
await switchPage(list[index - 1].path);
|
await switchPage(list[index - 1].path);
|
||||||
|
}
|
||||||
|
|
||||||
else
|
else {
|
||||||
await switchPage(list[index + 1].path);
|
await switchPage(list[index + 1].path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.splice(index, 1);
|
list.splice(index, 1);
|
||||||
@ -129,8 +150,9 @@ export default {
|
|||||||
|
|
||||||
const reloadPage = (path) => {
|
const reloadPage = (path) => {
|
||||||
const selectedPage = findPage(path || unref(route.path));
|
const selectedPage = findPage(path || unref(route.path));
|
||||||
if (selectedPage)
|
if (selectedPage) {
|
||||||
selectedPage.key = getKey();
|
selectedPage.key = getKey();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const closeOtherPage = (path) => {
|
const closeOtherPage = (path) => {
|
||||||
const selectedPage = findPage(path || unref(route.path));
|
const selectedPage = findPage(path || unref(route.path));
|
||||||
@ -139,8 +161,9 @@ export default {
|
|||||||
};
|
};
|
||||||
const getPageKey = (_route) => {
|
const getPageKey = (_route) => {
|
||||||
const selectedPage = findPage(_route.path);
|
const selectedPage = findPage(_route.path);
|
||||||
if (selectedPage)
|
if (selectedPage) {
|
||||||
return selectedPage.key;
|
return selectedPage.key;
|
||||||
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'node:fs';
|
||||||
import { join } from 'path';
|
import { join } from 'node:path';
|
||||||
import { name } from '../package.json';
|
import { name } from '../package.json';
|
||||||
|
|
||||||
const namespace = 'plugin-locale';
|
const namespace = 'plugin-locale';
|
||||||
@ -32,10 +32,17 @@ export default (api) => {
|
|||||||
return join(api.paths.absSrcPath, api.config.singular ? 'locale' : 'locales');
|
return join(api.paths.absSrcPath, api.config.singular ? 'locale' : 'locales');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
api.register({
|
||||||
|
key: 'addExtraLocales',
|
||||||
|
fn: () => [
|
||||||
|
getLocaleFileBasePath(),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
// 监听 locale 文件改变,重新生成文件
|
// 监听 locale 文件改变,重新生成文件
|
||||||
api.addTmpGenerateWatcherPaths(getLocaleFileBasePath);
|
api.addTmpGenerateWatcherPaths(getLocaleFileBasePath);
|
||||||
|
|
||||||
api.onGenerateFiles(() => {
|
api.onGenerateFiles(async () => {
|
||||||
// .fes配置
|
// .fes配置
|
||||||
const userConfig = {
|
const userConfig = {
|
||||||
locale: 'zh-CN', // default locale
|
locale: 'zh-CN', // default locale
|
||||||
@ -45,9 +52,13 @@ export default (api) => {
|
|||||||
...api.config.locale,
|
...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;
|
const { baseNavigator, ...otherConfig } = userConfig;
|
||||||
|
|
||||||
@ -55,7 +66,7 @@ export default (api) => {
|
|||||||
path: join(namespace, 'locales.js'),
|
path: join(namespace, 'locales.js'),
|
||||||
content: Mustache.render(readFileSync(join(__dirname, 'runtime/locales.js.tpl'), 'utf-8'), {
|
content: Mustache.render(readFileSync(join(__dirname, 'runtime/locales.js.tpl'), 'utf-8'), {
|
||||||
REPLACE_IMPORTS: files,
|
REPLACE_IMPORTS: files,
|
||||||
REPLACE_LOCALES: locales.map((item) => ({
|
REPLACE_LOCALES: locales.map(item => ({
|
||||||
locale: item.locale,
|
locale: item.locale,
|
||||||
importNames: item.importNames.join(', '),
|
importNames: item.importNames.join(', '),
|
||||||
})),
|
})),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { plugin } from '@@/core/coreExports';
|
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';
|
import SelectLang from './views/SelectLang.vue';
|
||||||
|
|
||||||
// 共享出去
|
// 共享出去
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<FTooltip v-model="isOpened" popperClass="lang-popper" mode="popover">
|
<FTooltip v-model="isOpened" popper-class="lang-popper" mode="popover">
|
||||||
<div class="lang-icon">
|
<div class="lang-icon">
|
||||||
<LanguageOutlined />
|
<LanguageOutlined />
|
||||||
</div>
|
</div>
|
||||||
<template #content>
|
<template #content>
|
||||||
<FScrollbar height="274" class="lang-container">
|
<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.icon }}</span>
|
||||||
<span>{{ item.label }}</span>
|
<span>{{ item.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
@ -15,12 +15,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { FTooltip, FScrollbar } from '@fesjs/fes-design';
|
import { FScrollbar, FTooltip } from '@fesjs/fes-design';
|
||||||
import { LanguageOutlined } from '@fesjs/fes-design/icon';
|
import { LanguageOutlined } from '@fesjs/fes-design/icon';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import langUConfigMap from '../langUConfigMap';
|
import langUConfigMap from '../langUConfigMap';
|
||||||
// eslint-disable-next-line import/extensions
|
|
||||||
import { locale as _locale } from '../core';
|
import { locale as _locale } from '../core';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -55,11 +55,13 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.fes-tooltip.fes-tooltip-popover.lang-popper {
|
.fes-tooltip.fes-tooltip-popover.lang-popper {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.lang-icon {
|
.lang-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { join, basename } from 'path';
|
import { basename, join } from 'node:path';
|
||||||
import { glob, winPath } from '@fesjs/utils';
|
import { glob, winPath } from '@fesjs/utils';
|
||||||
|
|
||||||
const ignore = /\.(d\.ts|\.test\.(js|ts))$/;
|
const ignore = /\.(d\.ts|\.test\.(js|ts))$/;
|
||||||
@ -15,30 +15,32 @@ const getRouteName = function (path) {
|
|||||||
.replace(/\[...([a-zA-Z]*)\]/, 'FUZZYMATCH-$1');
|
.replace(/\[...([a-zA-Z]*)\]/, 'FUZZYMATCH-$1');
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getLocales(cwd) {
|
export function getLocales(cwdArray) {
|
||||||
const map = {};
|
const map = {};
|
||||||
const files = [];
|
const files = [];
|
||||||
glob.sync('**/*.js', {
|
cwdArray.forEach((cwd) => {
|
||||||
cwd,
|
glob.sync('**/*.js', {
|
||||||
})
|
cwd,
|
||||||
.filter((file) => !ignore.test(file))
|
})
|
||||||
.forEach((fileName) => {
|
.filter(file => !ignore.test(file))
|
||||||
const locale = basename(fileName, '.js');
|
.forEach((fileName) => {
|
||||||
const importName = getRouteName(fileName).replace('.js', '');
|
const locale = basename(fileName, '.js');
|
||||||
const result = {
|
const importName = getRouteName(fileName).replace('.js', '');
|
||||||
importName,
|
const result = {
|
||||||
// import语法的路径,必须处理win
|
importName,
|
||||||
path: winPath(join(cwd, fileName)),
|
// import语法的路径,必须处理win
|
||||||
};
|
path: winPath(join(cwd, fileName)),
|
||||||
files.push(result);
|
};
|
||||||
if (!map[locale]) {
|
files.push(result);
|
||||||
map[locale] = [];
|
if (!map[locale]) {
|
||||||
}
|
map[locale] = [];
|
||||||
map[locale].push(importName);
|
}
|
||||||
});
|
map[locale].push(importName);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
locales: Object.keys(map).map((key) => ({ locale: key, importNames: map[key] })),
|
locales: Object.keys(map).map(key => ({ locale: key, importNames: map[key] })),
|
||||||
files,
|
files,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user