mirror of
https://github.com/vbenjs/gf-vben-admin.git
synced 2025-02-02 19:08:40 +08:00
wip: support multilingual configuration
This commit is contained in:
parent
3a651767fe
commit
8882d4e7ea
@ -1,96 +0,0 @@
|
|||||||
// Modified from
|
|
||||||
// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
|
|
||||||
|
|
||||||
// TODO Currently, it is not possible to monitor file addition and deletion. The content has been changed, the cache problem?
|
|
||||||
import { join } from 'path';
|
|
||||||
import { lstatSync } from 'fs';
|
|
||||||
import glob from 'glob';
|
|
||||||
import { createResolver, Resolver } from 'vite/dist/node/resolver.js';
|
|
||||||
import { Transform } from 'vite/dist/node/transform.js';
|
|
||||||
|
|
||||||
const modulesDir: string = join(process.cwd(), '/node_modules/');
|
|
||||||
|
|
||||||
interface SharedConfig {
|
|
||||||
root?: string;
|
|
||||||
alias?: Record<string, string>;
|
|
||||||
resolvers?: Resolver[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function template(template: string) {
|
|
||||||
return (data: { [x: string]: any }) => {
|
|
||||||
return template.replace(/#([^#]+)#/g, (_, g1) => data[g1] || g1);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const globbyTransform = function (config: SharedConfig): Transform {
|
|
||||||
const resolver = createResolver(
|
|
||||||
config.root || process.cwd(),
|
|
||||||
config.resolvers || [],
|
|
||||||
config.alias || {}
|
|
||||||
);
|
|
||||||
const cache = new Map();
|
|
||||||
|
|
||||||
const urlMap = new Map();
|
|
||||||
return {
|
|
||||||
test({ path }) {
|
|
||||||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
|
||||||
try {
|
|
||||||
return (
|
|
||||||
!filePath.startsWith(modulesDir) &&
|
|
||||||
/\.(vue|js|jsx|ts|tsx)$/.test(filePath) &&
|
|
||||||
lstatSync(filePath).isFile()
|
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
transform({ code, path, isBuild }) {
|
|
||||||
let result = cache.get(path);
|
|
||||||
if (!result) {
|
|
||||||
const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?path)?!([^'"]+)\2/g;
|
|
||||||
const match = code.match(reg);
|
|
||||||
if (!match) return code;
|
|
||||||
const lastImport = urlMap.get(path);
|
|
||||||
if (lastImport && match) {
|
|
||||||
code = code.replace(lastImport, match[0]);
|
|
||||||
}
|
|
||||||
result = code.replace(reg, (_, g1, g2, g3, g4) => {
|
|
||||||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
|
||||||
// resolve path
|
|
||||||
const resolvedFilePath = g4.startsWith('.')
|
|
||||||
? resolver.resolveRelativeRequest(filePath, g4)
|
|
||||||
: { pathname: resolver.requestToFile(g4) };
|
|
||||||
const files = glob.sync(resolvedFilePath.pathname, { dot: true });
|
|
||||||
let templateStr = 'import #name# from #file#'; // import default
|
|
||||||
let name = g1;
|
|
||||||
const m = g1.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
|
|
||||||
const m2 = g1.match(/\*\s+as\s+(\w+)/); // import * as all module
|
|
||||||
if (m) {
|
|
||||||
templateStr = `import { ${m[1]} as #name# } from #file#`;
|
|
||||||
name = m[3] || m[1];
|
|
||||||
} else if (m2) {
|
|
||||||
templateStr = 'import * as #name# from #file#';
|
|
||||||
name = m2[1];
|
|
||||||
}
|
|
||||||
const temRender = template(templateStr);
|
|
||||||
|
|
||||||
const groups: Array<string>[] = [];
|
|
||||||
const replaceFiles = files.map((f, i) => {
|
|
||||||
const file = g2 + resolver.fileToRequest(f) + g2;
|
|
||||||
groups.push([name + i, file]);
|
|
||||||
return temRender({ name: name + i, file });
|
|
||||||
});
|
|
||||||
urlMap.set(path, replaceFiles.join('\n'));
|
|
||||||
return (
|
|
||||||
replaceFiles.join('\n') +
|
|
||||||
(g3 ? '\n' + groups.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '') +
|
|
||||||
`\nconst ${name} = { ${groups.map((v) => v[0]).join(',')} }\n`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
if (isBuild) cache.set(path, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
export default globbyTransform;
|
|
@ -1,7 +1,6 @@
|
|||||||
// Used to import all files under `src/views`
|
// Used to import all files under `src/views`
|
||||||
|
|
||||||
// The built-in dynamic import of vite cannot meet the needs of importing all files under views
|
// The built-in dynamic import of vite cannot meet the needs of importing all files under views
|
||||||
|
// Special usage ,Only for this project
|
||||||
import glob from 'glob';
|
import glob from 'glob';
|
||||||
import { Transform } from 'vite/dist/node/transform.js';
|
import { Transform } from 'vite/dist/node/transform.js';
|
||||||
|
|
||||||
@ -28,7 +27,6 @@ const dynamicImportTransform = function (env: any = {}): Transform {
|
|||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!isBuild) return code;
|
|
||||||
// Only convert the dir
|
// Only convert the dir
|
||||||
try {
|
try {
|
||||||
const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() });
|
const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() });
|
201
build/vite/plugin/transform/globby/index.ts
Normal file
201
build/vite/plugin/transform/globby/index.ts
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
// Modified from
|
||||||
|
// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
|
||||||
|
|
||||||
|
// TODO Deleting files requires re-running the project
|
||||||
|
import { join } from 'path';
|
||||||
|
import { lstatSync } from 'fs';
|
||||||
|
import glob from 'glob';
|
||||||
|
import globrex from 'globrex';
|
||||||
|
import dotProp from 'dot-prop';
|
||||||
|
import { createResolver, Resolver } from 'vite/dist/node/resolver.js';
|
||||||
|
import { Transform } from 'vite/dist/node/transform.js';
|
||||||
|
|
||||||
|
const modulesDir: string = join(process.cwd(), '/node_modules/');
|
||||||
|
|
||||||
|
interface SharedConfig {
|
||||||
|
root?: string;
|
||||||
|
alias?: Record<string, string>;
|
||||||
|
resolvers?: Resolver[];
|
||||||
|
|
||||||
|
includes?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function template(template: string) {
|
||||||
|
return (data: { [x: string]: any }) => {
|
||||||
|
return template.replace(/#([^#]+)#/g, (_, g1) => data[g1] || g1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO support hmr
|
||||||
|
function hmr(isBuild = false) {
|
||||||
|
if (isBuild) return '';
|
||||||
|
return `
|
||||||
|
if (import.meta.hot) {
|
||||||
|
import.meta.hot.accept();
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle includes
|
||||||
|
function fileInclude(includes: string | string[] | undefined, filePath: string) {
|
||||||
|
return !includes || !Array.isArray(includes)
|
||||||
|
? true
|
||||||
|
: includes.some((item) => filePath.startsWith(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bare exporter
|
||||||
|
function compareString(modify: any, data: string[][]) {
|
||||||
|
return modify ? '\n' + data.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function varTemplate(data: string[][], name: string) {
|
||||||
|
//prepare deep data (for locales)
|
||||||
|
let deepData: Record<string, object | string> = {};
|
||||||
|
let hasDeepData = false;
|
||||||
|
|
||||||
|
//data modify
|
||||||
|
data.map((v) => {
|
||||||
|
//check for has deep data
|
||||||
|
if (v[0].includes('/')) {
|
||||||
|
hasDeepData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lastKey is a data
|
||||||
|
let pathValue = v[0].replace(/\//g, '.').split('.');
|
||||||
|
let lastKey: string | undefined = pathValue.pop();
|
||||||
|
|
||||||
|
let deepValue: Record<any, any> = {};
|
||||||
|
if (lastKey) {
|
||||||
|
deepValue[lastKey.replace('_' + pathValue[0], '')] = lastKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set Deep Value
|
||||||
|
deepValue = Object.assign(deepValue, dotProp.get(deepData, pathValue.join('.')));
|
||||||
|
dotProp.set(deepData, pathValue.join('.'), deepValue);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasDeepData) {
|
||||||
|
return `const ${name} = ` + JSON.stringify(deepData).replace(/\"|\'/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return `const ${name} = { ${data.map((v) => v[0]).join(',')} }`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const globTransform = function (config: SharedConfig): Transform {
|
||||||
|
const resolver = createResolver(
|
||||||
|
config.root || process.cwd(),
|
||||||
|
config.resolvers || [],
|
||||||
|
config.alias || {}
|
||||||
|
);
|
||||||
|
const { includes } = config;
|
||||||
|
const cache = new Map();
|
||||||
|
const urlMap = new Map();
|
||||||
|
return {
|
||||||
|
test({ path }) {
|
||||||
|
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (
|
||||||
|
!filePath.startsWith(modulesDir) &&
|
||||||
|
/\.(vue|js|jsx|ts|tsx)$/.test(filePath) &&
|
||||||
|
fileInclude(includes, filePath) &&
|
||||||
|
lstatSync(filePath).isFile()
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
transform({ code, path, isBuild }) {
|
||||||
|
let result = cache.get(path);
|
||||||
|
if (!result) {
|
||||||
|
const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?locale)?(\?path)?!([^'"]+)\2/g;
|
||||||
|
const match = code.match(reg);
|
||||||
|
if (!match) return code;
|
||||||
|
const lastImport = urlMap.get(path);
|
||||||
|
if (lastImport && match) {
|
||||||
|
code = code.replace(lastImport, match[0]);
|
||||||
|
}
|
||||||
|
result = code.replace(
|
||||||
|
reg,
|
||||||
|
(
|
||||||
|
_,
|
||||||
|
// variable to export
|
||||||
|
exportName,
|
||||||
|
// bare export or not
|
||||||
|
bareExporter,
|
||||||
|
// is locale import
|
||||||
|
isLocale,
|
||||||
|
// inject _path attr
|
||||||
|
injectPath,
|
||||||
|
// path export
|
||||||
|
globPath
|
||||||
|
) => {
|
||||||
|
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
||||||
|
// resolve path
|
||||||
|
|
||||||
|
const resolvedFilePath = globPath.startsWith('.')
|
||||||
|
? resolver.resolveRelativeRequest(filePath, globPath)
|
||||||
|
: { pathname: resolver.requestToFile(globPath) };
|
||||||
|
|
||||||
|
const files = glob.sync(resolvedFilePath.pathname, { dot: true });
|
||||||
|
|
||||||
|
let templateStr = 'import #name# from #file#'; // import default
|
||||||
|
let name = exportName;
|
||||||
|
const m = exportName.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
|
||||||
|
const m2 = exportName.match(/\*\s+as\s+(\w+)/); // import * as all module
|
||||||
|
if (m) {
|
||||||
|
templateStr = `import { ${m[1]} as #name# } from #file#`;
|
||||||
|
name = m[3] || m[1];
|
||||||
|
} else if (m2) {
|
||||||
|
templateStr = 'import * as #name# from #file#';
|
||||||
|
name = m2[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateRender = template(templateStr);
|
||||||
|
|
||||||
|
const groups: Array<string>[] = [];
|
||||||
|
const replaceFiles = files.map((f, i) => {
|
||||||
|
const fileNameWithAlias = resolver.fileToRequest(f);
|
||||||
|
|
||||||
|
const file = bareExporter + fileNameWithAlias + bareExporter;
|
||||||
|
|
||||||
|
if (isLocale) {
|
||||||
|
const globrexRes = globrex(globPath, { extended: true, globstar: true });
|
||||||
|
|
||||||
|
// Get segments for files like an en/system ch/modules for:
|
||||||
|
// ['en', 'system'] ['ch', 'modules']
|
||||||
|
const matchedGroups = globrexRes.regex.exec(fileNameWithAlias);
|
||||||
|
|
||||||
|
if (matchedGroups && matchedGroups.length) {
|
||||||
|
const matchedSegments = matchedGroups[1]; //first everytime "Full Match"
|
||||||
|
const name = matchedGroups[2] + '_' + matchedSegments.split('/').shift();
|
||||||
|
//send deep way like an (en/modules/system/dashboard) into groups
|
||||||
|
groups.push([matchedSegments + name, file]);
|
||||||
|
return templateRender({
|
||||||
|
name,
|
||||||
|
file,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
groups.push([name + i, file]);
|
||||||
|
return templateRender({ name: name + i, file });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// save in memory used result
|
||||||
|
const filesJoined = replaceFiles.join('\n');
|
||||||
|
|
||||||
|
urlMap.set(path, filesJoined);
|
||||||
|
return [
|
||||||
|
filesJoined,
|
||||||
|
compareString(injectPath, groups),
|
||||||
|
varTemplate(groups, name),
|
||||||
|
'',
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (isBuild) cache.set(path, result);
|
||||||
|
}
|
||||||
|
return `${result}${hmr(isBuild)}`;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export default globTransform;
|
@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/iconify": "^2.0.0-rc.2",
|
"@iconify/iconify": "^2.0.0-rc.2",
|
||||||
"@vueuse/core": "4.0.0-beta.41",
|
"@vueuse/core": "4.0.0-rc.3",
|
||||||
"ant-design-vue": "2.0.0-beta.15",
|
"ant-design-vue": "2.0.0-beta.15",
|
||||||
"apexcharts": "3.22.0",
|
"apexcharts": "3.22.0",
|
||||||
"axios": "^0.21.0",
|
"axios": "^0.21.0",
|
||||||
@ -35,7 +35,7 @@
|
|||||||
"qrcode": "^1.4.4",
|
"qrcode": "^1.4.4",
|
||||||
"vditor": "^3.6.2",
|
"vditor": "^3.6.2",
|
||||||
"vue": "^3.0.2",
|
"vue": "^3.0.2",
|
||||||
"vue-i18n": "^9.0.0-beta.7",
|
"vue-i18n": "^9.0.0-beta.8",
|
||||||
"vue-router": "^4.0.0-rc.3",
|
"vue-router": "^4.0.0-rc.3",
|
||||||
"vuex": "^4.0.0-rc.1",
|
"vuex": "^4.0.0-rc.1",
|
||||||
"vuex-module-decorators": "^1.0.1",
|
"vuex-module-decorators": "^1.0.1",
|
||||||
@ -50,6 +50,7 @@
|
|||||||
"@purge-icons/generated": "^0.4.1",
|
"@purge-icons/generated": "^0.4.1",
|
||||||
"@types/echarts": "^4.9.1",
|
"@types/echarts": "^4.9.1",
|
||||||
"@types/fs-extra": "^9.0.4",
|
"@types/fs-extra": "^9.0.4",
|
||||||
|
"@types/globrex": "^0.1.0",
|
||||||
"@types/koa-static": "^4.0.1",
|
"@types/koa-static": "^4.0.1",
|
||||||
"@types/lodash-es": "^4.17.3",
|
"@types/lodash-es": "^4.17.3",
|
||||||
"@types/mockjs": "^1.0.3",
|
"@types/mockjs": "^1.0.3",
|
||||||
@ -68,6 +69,7 @@
|
|||||||
"conventional-changelog-cli": "^2.1.1",
|
"conventional-changelog-cli": "^2.1.1",
|
||||||
"conventional-changelog-custom-config": "^0.3.1",
|
"conventional-changelog-custom-config": "^0.3.1",
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
|
"dot-prop": "^6.0.0",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"eslint": "^7.13.0",
|
"eslint": "^7.13.0",
|
||||||
"eslint-config-prettier": "^6.15.0",
|
"eslint-config-prettier": "^6.15.0",
|
||||||
@ -75,6 +77,7 @@
|
|||||||
"eslint-plugin-vue": "^7.1.0",
|
"eslint-plugin-vue": "^7.1.0",
|
||||||
"esno": "^0.2.4",
|
"esno": "^0.2.4",
|
||||||
"fs-extra": "^9.0.1",
|
"fs-extra": "^9.0.1",
|
||||||
|
"globrex": "^0.1.2",
|
||||||
"husky": "^4.3.0",
|
"husky": "^4.3.0",
|
||||||
"koa-static": "^5.0.0",
|
"koa-static": "^5.0.0",
|
||||||
"less": "^3.12.2",
|
"less": "^3.12.2",
|
||||||
|
21
src/hooks/web/useLocale.ts
Normal file
21
src/hooks/web/useLocale.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import type { LocaleType } from '/@/locales/types';
|
||||||
|
import { appStore } from '/@/store/modules/app';
|
||||||
|
|
||||||
|
export function useLocale() {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function getLocale(): string {
|
||||||
|
return appStore.getProjectConfig.locale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param locale
|
||||||
|
*/
|
||||||
|
async function changeLocale(locale: LocaleType): Promise<void> {
|
||||||
|
appStore.commitProjectConfigState({ locale: locale });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { getLocale, changeLocale };
|
||||||
|
}
|
3
src/locales/index.ts
Normal file
3
src/locales/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)';
|
||||||
|
|
||||||
|
export default messages;
|
3
src/locales/lang/en/routes/menus/dashboard.ts
Normal file
3
src/locales/lang/en/routes/menus/dashboard.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
someentry: 'some text',
|
||||||
|
};
|
3
src/locales/lang/en/system/basic.ts
Normal file
3
src/locales/lang/en/system/basic.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
some: 'Get Out',
|
||||||
|
};
|
3
src/locales/lang/en/system/login.ts
Normal file
3
src/locales/lang/en/system/login.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
button: 'Login',
|
||||||
|
};
|
3
src/locales/lang/ru/routes/menus/dashboard.ts
Normal file
3
src/locales/lang/ru/routes/menus/dashboard.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
someentry: 'some text',
|
||||||
|
};
|
3
src/locales/lang/ru/system/basic.ts
Normal file
3
src/locales/lang/ru/system/basic.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
some: 'Get Out',
|
||||||
|
};
|
7
src/locales/lang/ru/system/login.ts
Normal file
7
src/locales/lang/ru/system/login.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default {
|
||||||
|
button: 'Login',
|
||||||
|
validation: {
|
||||||
|
account: 'Required Field account',
|
||||||
|
password: 'Required Field password',
|
||||||
|
},
|
||||||
|
};
|
3
src/locales/lang/zhCN/routes/menus/dashboard.ts
Normal file
3
src/locales/lang/zhCN/routes/menus/dashboard.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
someentry: '一些文本',
|
||||||
|
};
|
3
src/locales/lang/zhCN/system/basic.ts
Normal file
3
src/locales/lang/zhCN/system/basic.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
some: '出去',
|
||||||
|
};
|
3
src/locales/lang/zhCN/system/login.ts
Normal file
3
src/locales/lang/zhCN/system/login.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default {
|
||||||
|
button: '登录',
|
||||||
|
};
|
1
src/locales/types.ts
Normal file
1
src/locales/types.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export type LocaleType = 'zhCN' | 'en' | 'ru' | 'ja';
|
@ -5,6 +5,7 @@ import { setupStore } from '/@/store';
|
|||||||
import { setupAntd } from '/@/setup/ant-design-vue';
|
import { setupAntd } from '/@/setup/ant-design-vue';
|
||||||
import { setupErrorHandle } from '/@/setup/error-handle';
|
import { setupErrorHandle } from '/@/setup/error-handle';
|
||||||
import { setupGlobDirectives } from '/@/setup/directives';
|
import { setupGlobDirectives } from '/@/setup/directives';
|
||||||
|
import { setupI18n } from '/@/setup/i18n';
|
||||||
|
|
||||||
import { setupProdMockServer } from '../mock/_createProductionServer';
|
import { setupProdMockServer } from '../mock/_createProductionServer';
|
||||||
import { setApp } from '/@/setup/App';
|
import { setApp } from '/@/setup/App';
|
||||||
@ -15,11 +16,16 @@ import { isDevMode, isProdMode, isUseMock } from '/@/utils/env';
|
|||||||
|
|
||||||
import '/@/design/index.less';
|
import '/@/design/index.less';
|
||||||
|
|
||||||
|
import '/@/locales/index';
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App);
|
||||||
|
|
||||||
// Configure component library
|
// Configure component library
|
||||||
setupAntd(app);
|
setupAntd(app);
|
||||||
|
|
||||||
|
// Multilingual configuration
|
||||||
|
setupI18n(app);
|
||||||
|
|
||||||
// Configure routing
|
// Configure routing
|
||||||
setupRouter(app);
|
setupRouter(app);
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import { isProdMode } from '/@/utils/env';
|
|||||||
|
|
||||||
// ! You need to clear the browser cache after the change
|
// ! You need to clear the browser cache after the change
|
||||||
const setting: ProjectConfig = {
|
const setting: ProjectConfig = {
|
||||||
|
locale: 'en',
|
||||||
// color
|
// color
|
||||||
// TODO 主题色
|
// TODO 主题色
|
||||||
themeColor: primaryColor,
|
themeColor: primaryColor,
|
||||||
|
35
src/setup/i18n/index.ts
Normal file
35
src/setup/i18n/index.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import type { App } from 'vue';
|
||||||
|
import type { I18n, Locale, I18nOptions } from 'vue-i18n';
|
||||||
|
|
||||||
|
import { createI18n } from 'vue-i18n';
|
||||||
|
import localeMessages from '/@/locales';
|
||||||
|
import { useLocale } from '/@/hooks/web/useLocale';
|
||||||
|
|
||||||
|
const { getLocale } = useLocale();
|
||||||
|
|
||||||
|
const localeData: I18nOptions = {
|
||||||
|
legacy: false,
|
||||||
|
locale: getLocale(),
|
||||||
|
// TODO: setting fallback inside settings
|
||||||
|
fallbackLocale: 'en',
|
||||||
|
messages: localeMessages,
|
||||||
|
// availableLocales: ['ru'],
|
||||||
|
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
|
||||||
|
silentTranslationWarn: false, // true - warning off
|
||||||
|
silentFallbackWarn: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let i18n: I18n;
|
||||||
|
|
||||||
|
// setup i18n instance with glob
|
||||||
|
export function setupI18n(app: App) {
|
||||||
|
i18n = createI18n(localeData) as I18n;
|
||||||
|
setI18nLanguage(getLocale());
|
||||||
|
app.use(i18n);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setI18nLanguage(locale: Locale): void {
|
||||||
|
// @ts-ignore
|
||||||
|
i18n.global.locale.value = locale;
|
||||||
|
// i18n.global.setLocaleMessage(locale, messages);
|
||||||
|
}
|
@ -1,16 +1,19 @@
|
|||||||
import type { App } from 'vue';
|
import type { App } from 'vue';
|
||||||
import { createStore, createLogger, Plugin } from 'vuex';
|
import {
|
||||||
|
createStore,
|
||||||
|
// createLogger, Plugin
|
||||||
|
} from 'vuex';
|
||||||
import { config } from 'vuex-module-decorators';
|
import { config } from 'vuex-module-decorators';
|
||||||
import { isDevMode } from '/@/utils/env';
|
import { isDevMode } from '/@/utils/env';
|
||||||
|
|
||||||
config.rawError = true;
|
config.rawError = true;
|
||||||
const isDev = isDevMode();
|
const isDev = isDevMode();
|
||||||
const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
|
// const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
|
||||||
|
|
||||||
const store = createStore({
|
const store = createStore({
|
||||||
// modules: {},
|
// modules: {},
|
||||||
strict: isDev,
|
strict: isDev,
|
||||||
plugins,
|
// plugins,
|
||||||
});
|
});
|
||||||
|
|
||||||
export function setupStore(app: App<Element>) {
|
export function setupStore(app: App<Element>) {
|
||||||
|
3
src/types/config.d.ts
vendored
3
src/types/config.d.ts
vendored
@ -1,7 +1,7 @@
|
|||||||
// 左侧菜单, 顶部菜单
|
// 左侧菜单, 顶部菜单
|
||||||
import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
|
import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
|
||||||
import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
|
import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
|
||||||
|
import type { LocaleType } from '/@/locales/types';
|
||||||
export interface MessageSetting {
|
export interface MessageSetting {
|
||||||
title: string;
|
title: string;
|
||||||
// 取消按钮的文字,
|
// 取消按钮的文字,
|
||||||
@ -55,6 +55,7 @@ export interface HeaderSetting {
|
|||||||
showNotice: boolean;
|
showNotice: boolean;
|
||||||
}
|
}
|
||||||
export interface ProjectConfig {
|
export interface ProjectConfig {
|
||||||
|
locale: LocaleType;
|
||||||
// header背景色
|
// header背景色
|
||||||
headerBgColor: string;
|
headerBgColor: string;
|
||||||
// 左侧菜单背景色
|
// 左侧菜单背景色
|
||||||
|
2
src/types/module.d.ts
vendored
2
src/types/module.d.ts
vendored
@ -4,4 +4,6 @@ declare module 'globby!/@/router/routes/modules/**/*.@(ts)';
|
|||||||
|
|
||||||
declare module 'globby!/@/router/menus/modules/**/*.@(ts)';
|
declare module 'globby!/@/router/menus/modules/**/*.@(ts)';
|
||||||
|
|
||||||
|
declare module 'globby?locale!/@/locales/lang/**/*.@(ts)';
|
||||||
|
|
||||||
declare const React: string;
|
declare const React: string;
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
|
|
||||||
<a-form class="mx-auto mt-10" :model="formData" :rules="formRules" ref="formRef">
|
<a-form class="mx-auto mt-10" :model="formData" :rules="formRules" ref="formRef">
|
||||||
<a-form-item name="account">
|
<a-form-item name="account">
|
||||||
<a-input size="large" v-model:value="formData.account" placeholder="Username: vben" />
|
<a-input size="large" v-model:value="formData.account" placeholder="username: vben" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item name="password">
|
<a-form-item name="password">
|
||||||
<a-input-password
|
<a-input-password
|
||||||
size="large"
|
size="large"
|
||||||
visibilityToggle
|
visibilityToggle
|
||||||
v-model:value="formData.password"
|
v-model:value="formData.password"
|
||||||
placeholder="Password: 123456"
|
placeholder="password: 123456"
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
|
||||||
@ -28,13 +28,13 @@
|
|||||||
<a-row>
|
<a-row>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<!-- 未做逻辑,需要自行处理 -->
|
<!-- No logic, you need to deal with it yourself -->
|
||||||
<a-checkbox v-model:checked="autoLogin" size="small">自动登录</a-checkbox>
|
<a-checkbox v-model:checked="autoLogin" size="small">自动登录</a-checkbox>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<a-form-item :style="{ 'text-align': 'right' }">
|
<a-form-item :style="{ 'text-align': 'right' }">
|
||||||
<!-- 未做逻辑,需要自行处理 -->
|
<!-- No logic, you need to deal with it yourself -->
|
||||||
<a-button type="link" size="small">忘记密码</a-button>
|
<a-button type="link" size="small">忘记密码</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-col>
|
</a-col>
|
||||||
@ -47,7 +47,7 @@
|
|||||||
:block="true"
|
:block="true"
|
||||||
@click="login"
|
@click="login"
|
||||||
:loading="formState.loading"
|
:loading="formState.loading"
|
||||||
>登录</a-button
|
>{{ t('system.login.button') }}</a-button
|
||||||
>
|
>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
@ -57,20 +57,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { defineComponent, reactive, ref, unref, toRaw } from 'vue';
|
||||||
defineComponent,
|
|
||||||
reactive,
|
|
||||||
ref,
|
|
||||||
unref,
|
|
||||||
toRaw,
|
|
||||||
// computed
|
|
||||||
} from 'vue';
|
|
||||||
import { Checkbox } from 'ant-design-vue';
|
import { Checkbox } from 'ant-design-vue';
|
||||||
|
|
||||||
import Button from '/@/components/Button/index.vue';
|
import Button from '/@/components/Button/index.vue';
|
||||||
// import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
|
// import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
|
||||||
|
|
||||||
import { userStore } from '/@/store/modules/user';
|
import { userStore } from '/@/store/modules/user';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
// import { appStore } from '/@/store/modules/app';
|
// import { appStore } from '/@/store/modules/app';
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import { useSetting } from '/@/hooks/core/useSetting';
|
import { useSetting } from '/@/hooks/core/useSetting';
|
||||||
@ -139,7 +134,7 @@
|
|||||||
formState.loading = false;
|
formState.loading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const { t } = useI18n();
|
||||||
return {
|
return {
|
||||||
formRef,
|
formRef,
|
||||||
// verifyRef,
|
// verifyRef,
|
||||||
@ -151,6 +146,7 @@
|
|||||||
// openLoginVerify: openLoginVerifyRef,
|
// openLoginVerify: openLoginVerifyRef,
|
||||||
title: globSetting && globSetting.title,
|
title: globSetting && globSetting.title,
|
||||||
logo,
|
logo,
|
||||||
|
t,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -4,8 +4,9 @@ import { resolve } from 'path';
|
|||||||
|
|
||||||
import { modifyVars } from './build/config/lessModifyVars';
|
import { modifyVars } from './build/config/lessModifyVars';
|
||||||
import { createProxy } from './build/vite/proxy';
|
import { createProxy } from './build/vite/proxy';
|
||||||
import globbyTransform from './build/vite/plugin/context/transform';
|
|
||||||
import dynamicImportTransform from './build/vite/plugin/dynamicImport/index';
|
import globbyTransform from './build/vite/plugin/transform/globby';
|
||||||
|
import dynamicImportTransform from './build/vite/plugin/transform/dynamic-import';
|
||||||
|
|
||||||
import { isDevFn, loadEnv } from './build/utils';
|
import { isDevFn, loadEnv } from './build/utils';
|
||||||
|
|
||||||
@ -111,6 +112,11 @@ const viteConfig: UserConfig = {
|
|||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
__VERSION__: pkg.version,
|
__VERSION__: pkg.version,
|
||||||
|
// use vue-i18-next
|
||||||
|
// Suppress warning
|
||||||
|
__VUE_I18N_LEGACY_API__: false,
|
||||||
|
__VUE_I18N_FULL_INSTALL__: false,
|
||||||
|
__INTLIFY_PROD_DEVTOOLS__: false,
|
||||||
},
|
},
|
||||||
cssPreprocessOptions: {
|
cssPreprocessOptions: {
|
||||||
less: {
|
less: {
|
||||||
@ -135,5 +141,13 @@ const viteConfig: UserConfig = {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
...viteConfig,
|
...viteConfig,
|
||||||
transforms: [globbyTransform(viteConfig), dynamicImportTransform(viteEnv)],
|
transforms: [
|
||||||
|
globbyTransform({
|
||||||
|
resolvers: viteConfig.resolvers,
|
||||||
|
root: viteConfig.root,
|
||||||
|
alias: viteConfig.alias,
|
||||||
|
includes: [resolve('src/router'), resolve('src/locales')],
|
||||||
|
}),
|
||||||
|
dynamicImportTransform(viteEnv),
|
||||||
|
],
|
||||||
} as UserConfig;
|
} as UserConfig;
|
||||||
|
43
yarn.lock
43
yarn.lock
@ -1323,6 +1323,11 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/globrex@^0.1.0":
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.npmjs.org/@types/globrex/-/globrex-0.1.0.tgz#baf4ac8e36947017612c01fde7c7b641dc0b6c89"
|
||||||
|
integrity sha512-aBkxDgp/UbnluE+CIT3V3PoNewwOlLCzXSF3ipD86Slv8xVjwxrDAfSGbsfGgMzPo/fEMPXc+gNUJbtiugwfoA==
|
||||||
|
|
||||||
"@types/http-assert@*":
|
"@types/http-assert@*":
|
||||||
version "1.5.1"
|
version "1.5.1"
|
||||||
resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
|
resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
|
||||||
@ -1732,18 +1737,18 @@
|
|||||||
vscode-languageserver-textdocument "^1.0.1"
|
vscode-languageserver-textdocument "^1.0.1"
|
||||||
vscode-uri "^2.1.2"
|
vscode-uri "^2.1.2"
|
||||||
|
|
||||||
"@vueuse/core@4.0.0-beta.41":
|
"@vueuse/core@4.0.0-rc.3":
|
||||||
version "4.0.0-beta.41"
|
version "4.0.0-rc.3"
|
||||||
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-beta.41.tgz#0058aed5ade75ae2866283498009ad5172cbae84"
|
resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-rc.3.tgz#5381ca657e10df596cd7027fc5c96b2d4b3a090c"
|
||||||
integrity sha512-CgUih65PzYScorm1S4F93e6XXm+qxA8GrRLOSB1kXaqtP6vXedwkBxKkNEYNACx4reL4VEHqM/BrM6FajXkQUg==
|
integrity sha512-dQ/FZgo0z7kBFOvDWxuzaUrmuO8X1AlQk17e3PU1TVtG2Uu+mCvjPNbuvI2fjhTjl5rzPJawwoU2WZFj+nlFvw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@vueuse/shared" "4.0.0-beta.41"
|
"@vueuse/shared" "4.0.0-rc.3"
|
||||||
vue-demi latest
|
vue-demi latest
|
||||||
|
|
||||||
"@vueuse/shared@4.0.0-beta.41":
|
"@vueuse/shared@4.0.0-rc.3":
|
||||||
version "4.0.0-beta.41"
|
version "4.0.0-rc.3"
|
||||||
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-beta.41.tgz#395782ea2e580f1fc9488d25c89bd09f70170b25"
|
resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-rc.3.tgz#42fb56fed3779f3b8a17a82c16a364bad20d01b7"
|
||||||
integrity sha512-dqnuEPPC3OUJ6L6rhMiOCuPWIR698DtdwOydwCZBISsG2V6gZ2QFND6xtRwLib6/lhUMYVYPwIz3hPjlx7BIzw==
|
integrity sha512-VY0x/XxpeTMHp/0FDiv1cgUUxkJGQl7liiM2AjR/J7+Ys/2Y2dijD5cAKViq9FGUPQQsOcLptMvMvUsDMoN4DA==
|
||||||
dependencies:
|
dependencies:
|
||||||
vue-demi latest
|
vue-demi latest
|
||||||
|
|
||||||
@ -3222,6 +3227,13 @@ dot-prop@^5.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-obj "^2.0.0"
|
is-obj "^2.0.0"
|
||||||
|
|
||||||
|
dot-prop@^6.0.0:
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.0.tgz#bd579fd704d970981c4b05de591db648959f2ebb"
|
||||||
|
integrity sha512-xCbB8IN3IT+tdgoEPOnJmYTNJDrygGFOmiQEiVa5eAD+JEB1vTgMNhVGRnN5Eex/6amck7cdcrixb1qN9Go+GQ==
|
||||||
|
dependencies:
|
||||||
|
is-obj "^2.0.0"
|
||||||
|
|
||||||
dotenv-expand@^5.1.0:
|
dotenv-expand@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
|
resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
|
||||||
@ -4092,6 +4104,11 @@ globjoin@^0.1.4:
|
|||||||
resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
|
resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
|
||||||
integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=
|
integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=
|
||||||
|
|
||||||
|
globrex@^0.1.2:
|
||||||
|
version "0.1.2"
|
||||||
|
resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
|
||||||
|
integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
|
||||||
|
|
||||||
gonzales-pe@^4.3.0:
|
gonzales-pe@^4.3.0:
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3"
|
resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3"
|
||||||
@ -8167,10 +8184,10 @@ vue-eslint-parser@^7.1.1:
|
|||||||
esquery "^1.0.1"
|
esquery "^1.0.1"
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
|
|
||||||
vue-i18n@^9.0.0-beta.7:
|
vue-i18n@^9.0.0-beta.8:
|
||||||
version "9.0.0-beta.7"
|
version "9.0.0-beta.8"
|
||||||
resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.7.tgz#f6fad5b4be218018aab4797f80dd2a95ee5236f9"
|
resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.8.tgz#92282d5b5e0e6f15cc04943ce11bf49db610468f"
|
||||||
integrity sha512-hFl0XnV91P/4UyWvHYvdYxuk3GRnKIW9zXAm6hrUU4mOIwpqchi7jVQva2TJLr52Mpsu4zYXmzL1h5pgrKmCfQ==
|
integrity sha512-tViSN96jLi0AKvAVi4twcYYN5Ld++SqN1/140ua+YWm/iRbO2M0rAcsZ7e6/4LTm6Pd1ldSwWihSuv2bSQmlnw==
|
||||||
dependencies:
|
dependencies:
|
||||||
source-map "0.6.1"
|
source-map "0.6.1"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user