Commit 8882d4e7eafabceb3c53cd5c22ad70ee166921c4

Authored by vben
1 parent 3a651767

wip: support multilingual configuration

build/vite/plugin/dynamicImport/index.ts renamed to build/vite/plugin/transform/dynamic-import/index.ts
1 // Used to import all files under `src/views` 1 // Used to import all files under `src/views`
2 -  
3 // The built-in dynamic import of vite cannot meet the needs of importing all files under views 2 // The built-in dynamic import of vite cannot meet the needs of importing all files under views
4 - 3 +// Special usage ,Only for this project
5 import glob from 'glob'; 4 import glob from 'glob';
6 import { Transform } from 'vite/dist/node/transform.js'; 5 import { Transform } from 'vite/dist/node/transform.js';
7 6
@@ -28,7 +27,6 @@ const dynamicImportTransform = function (env: any = {}): Transform { @@ -28,7 +27,6 @@ const dynamicImportTransform = function (env: any = {}): Transform {
28 return code; 27 return code;
29 } 28 }
30 29
31 - // if (!isBuild) return code;  
32 // Only convert the dir 30 // Only convert the dir
33 try { 31 try {
34 const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() }); 32 const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() });
build/vite/plugin/context/transform.ts renamed to build/vite/plugin/transform/globby/index.ts
1 // Modified from 1 // Modified from
2 // https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts 2 // https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
3 3
4 -// TODO Currently, it is not possible to monitor file addition and deletion. The content has been changed, the cache problem? 4 +// TODO Deleting files requires re-running the project
5 import { join } from 'path'; 5 import { join } from 'path';
6 import { lstatSync } from 'fs'; 6 import { lstatSync } from 'fs';
7 import glob from 'glob'; 7 import glob from 'glob';
  8 +import globrex from 'globrex';
  9 +import dotProp from 'dot-prop';
8 import { createResolver, Resolver } from 'vite/dist/node/resolver.js'; 10 import { createResolver, Resolver } from 'vite/dist/node/resolver.js';
9 import { Transform } from 'vite/dist/node/transform.js'; 11 import { Transform } from 'vite/dist/node/transform.js';
10 12
@@ -14,6 +16,8 @@ interface SharedConfig { @@ -14,6 +16,8 @@ interface SharedConfig {
14 root?: string; 16 root?: string;
15 alias?: Record<string, string>; 17 alias?: Record<string, string>;
16 resolvers?: Resolver[]; 18 resolvers?: Resolver[];
  19 +
  20 + includes?: string[];
17 } 21 }
18 22
19 function template(template: string) { 23 function template(template: string) {
@@ -22,22 +26,78 @@ function template(template: string) { @@ -22,22 +26,78 @@ function template(template: string) {
22 }; 26 };
23 } 27 }
24 28
25 -const globbyTransform = function (config: SharedConfig): Transform { 29 +// TODO support hmr
  30 +function hmr(isBuild = false) {
  31 + if (isBuild) return '';
  32 + return `
  33 + if (import.meta.hot) {
  34 + import.meta.hot.accept();
  35 + }`;
  36 +}
  37 +
  38 +// handle includes
  39 +function fileInclude(includes: string | string[] | undefined, filePath: string) {
  40 + return !includes || !Array.isArray(includes)
  41 + ? true
  42 + : includes.some((item) => filePath.startsWith(item));
  43 +}
  44 +
  45 +// Bare exporter
  46 +function compareString(modify: any, data: string[][]) {
  47 + return modify ? '\n' + data.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '';
  48 +}
  49 +
  50 +function varTemplate(data: string[][], name: string) {
  51 + //prepare deep data (for locales)
  52 + let deepData: Record<string, object | string> = {};
  53 + let hasDeepData = false;
  54 +
  55 + //data modify
  56 + data.map((v) => {
  57 + //check for has deep data
  58 + if (v[0].includes('/')) {
  59 + hasDeepData = true;
  60 + }
  61 +
  62 + // lastKey is a data
  63 + let pathValue = v[0].replace(/\//g, '.').split('.');
  64 + let lastKey: string | undefined = pathValue.pop();
  65 +
  66 + let deepValue: Record<any, any> = {};
  67 + if (lastKey) {
  68 + deepValue[lastKey.replace('_' + pathValue[0], '')] = lastKey;
  69 + }
  70 +
  71 + // Set Deep Value
  72 + deepValue = Object.assign(deepValue, dotProp.get(deepData, pathValue.join('.')));
  73 + dotProp.set(deepData, pathValue.join('.'), deepValue);
  74 + });
  75 +
  76 + if (hasDeepData) {
  77 + return `const ${name} = ` + JSON.stringify(deepData).replace(/\"|\'/g, '');
  78 + }
  79 +
  80 + return `const ${name} = { ${data.map((v) => v[0]).join(',')} }`;
  81 +}
  82 +
  83 +const globTransform = function (config: SharedConfig): Transform {
26 const resolver = createResolver( 84 const resolver = createResolver(
27 config.root || process.cwd(), 85 config.root || process.cwd(),
28 config.resolvers || [], 86 config.resolvers || [],
29 config.alias || {} 87 config.alias || {}
30 ); 88 );
  89 + const { includes } = config;
31 const cache = new Map(); 90 const cache = new Map();
32 -  
33 const urlMap = new Map(); 91 const urlMap = new Map();
34 return { 92 return {
35 test({ path }) { 93 test({ path }) {
36 const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'? 94 const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
  95 +
37 try { 96 try {
38 return ( 97 return (
39 !filePath.startsWith(modulesDir) && 98 !filePath.startsWith(modulesDir) &&
40 /\.(vue|js|jsx|ts|tsx)$/.test(filePath) && 99 /\.(vue|js|jsx|ts|tsx)$/.test(filePath) &&
  100 + fileInclude(includes, filePath) &&
41 lstatSync(filePath).isFile() 101 lstatSync(filePath).isFile()
42 ); 102 );
43 } catch { 103 } catch {
@@ -47,50 +107,95 @@ const globbyTransform = function (config: SharedConfig): Transform { @@ -47,50 +107,95 @@ const globbyTransform = function (config: SharedConfig): Transform {
47 transform({ code, path, isBuild }) { 107 transform({ code, path, isBuild }) {
48 let result = cache.get(path); 108 let result = cache.get(path);
49 if (!result) { 109 if (!result) {
50 - const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?path)?!([^'"]+)\2/g; 110 + const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?locale)?(\?path)?!([^'"]+)\2/g;
51 const match = code.match(reg); 111 const match = code.match(reg);
52 if (!match) return code; 112 if (!match) return code;
53 const lastImport = urlMap.get(path); 113 const lastImport = urlMap.get(path);
54 if (lastImport && match) { 114 if (lastImport && match) {
55 code = code.replace(lastImport, match[0]); 115 code = code.replace(lastImport, match[0]);
56 } 116 }
57 - result = code.replace(reg, (_, g1, g2, g3, g4) => {  
58 - const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?  
59 - // resolve path  
60 - const resolvedFilePath = g4.startsWith('.')  
61 - ? resolver.resolveRelativeRequest(filePath, g4)  
62 - : { pathname: resolver.requestToFile(g4) };  
63 - const files = glob.sync(resolvedFilePath.pathname, { dot: true });  
64 - let templateStr = 'import #name# from #file#'; // import default  
65 - let name = g1;  
66 - const m = g1.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module  
67 - const m2 = g1.match(/\*\s+as\s+(\w+)/); // import * as all module  
68 - if (m) {  
69 - templateStr = `import { ${m[1]} as #name# } from #file#`;  
70 - name = m[3] || m[1];  
71 - } else if (m2) {  
72 - templateStr = 'import * as #name# from #file#';  
73 - name = m2[1]; 117 + result = code.replace(
  118 + reg,
  119 + (
  120 + _,
  121 + // variable to export
  122 + exportName,
  123 + // bare export or not
  124 + bareExporter,
  125 + // is locale import
  126 + isLocale,
  127 + // inject _path attr
  128 + injectPath,
  129 + // path export
  130 + globPath
  131 + ) => {
  132 + const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
  133 + // resolve path
  134 +
  135 + const resolvedFilePath = globPath.startsWith('.')
  136 + ? resolver.resolveRelativeRequest(filePath, globPath)
  137 + : { pathname: resolver.requestToFile(globPath) };
  138 +
  139 + const files = glob.sync(resolvedFilePath.pathname, { dot: true });
  140 +
  141 + let templateStr = 'import #name# from #file#'; // import default
  142 + let name = exportName;
  143 + const m = exportName.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
  144 + const m2 = exportName.match(/\*\s+as\s+(\w+)/); // import * as all module
  145 + if (m) {
  146 + templateStr = `import { ${m[1]} as #name# } from #file#`;
  147 + name = m[3] || m[1];
  148 + } else if (m2) {
  149 + templateStr = 'import * as #name# from #file#';
  150 + name = m2[1];
  151 + }
  152 +
  153 + const templateRender = template(templateStr);
  154 +
  155 + const groups: Array<string>[] = [];
  156 + const replaceFiles = files.map((f, i) => {
  157 + const fileNameWithAlias = resolver.fileToRequest(f);
  158 +
  159 + const file = bareExporter + fileNameWithAlias + bareExporter;
  160 +
  161 + if (isLocale) {
  162 + const globrexRes = globrex(globPath, { extended: true, globstar: true });
  163 +
  164 + // Get segments for files like an en/system ch/modules for:
  165 + // ['en', 'system'] ['ch', 'modules']
  166 + const matchedGroups = globrexRes.regex.exec(fileNameWithAlias);
  167 +
  168 + if (matchedGroups && matchedGroups.length) {
  169 + const matchedSegments = matchedGroups[1]; //first everytime "Full Match"
  170 + const name = matchedGroups[2] + '_' + matchedSegments.split('/').shift();
  171 + //send deep way like an (en/modules/system/dashboard) into groups
  172 + groups.push([matchedSegments + name, file]);
  173 + return templateRender({
  174 + name,
  175 + file,
  176 + });
  177 + }
  178 + } else {
  179 + groups.push([name + i, file]);
  180 + return templateRender({ name: name + i, file });
  181 + }
  182 + });
  183 + // save in memory used result
  184 + const filesJoined = replaceFiles.join('\n');
  185 +
  186 + urlMap.set(path, filesJoined);
  187 + return [
  188 + filesJoined,
  189 + compareString(injectPath, groups),
  190 + varTemplate(groups, name),
  191 + '',
  192 + ].join('\n');
74 } 193 }
75 - const temRender = template(templateStr);  
76 -  
77 - const groups: Array<string>[] = [];  
78 - const replaceFiles = files.map((f, i) => {  
79 - const file = g2 + resolver.fileToRequest(f) + g2;  
80 - groups.push([name + i, file]);  
81 - return temRender({ name: name + i, file });  
82 - });  
83 - urlMap.set(path, replaceFiles.join('\n'));  
84 - return (  
85 - replaceFiles.join('\n') +  
86 - (g3 ? '\n' + groups.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '') +  
87 - `\nconst ${name} = { ${groups.map((v) => v[0]).join(',')} }\n`  
88 - );  
89 - }); 194 + );
90 if (isBuild) cache.set(path, result); 195 if (isBuild) cache.set(path, result);
91 } 196 }
92 - return result; 197 + return `${result}${hmr(isBuild)}`;
93 }, 198 },
94 }; 199 };
95 }; 200 };
96 -export default globbyTransform; 201 +export default globTransform;
package.json
@@ -22,7 +22,7 @@ @@ -22,7 +22,7 @@
22 }, 22 },
23 "dependencies": { 23 "dependencies": {
24 "@iconify/iconify": "^2.0.0-rc.2", 24 "@iconify/iconify": "^2.0.0-rc.2",
25 - "@vueuse/core": "4.0.0-beta.41", 25 + "@vueuse/core": "4.0.0-rc.3",
26 "ant-design-vue": "2.0.0-beta.15", 26 "ant-design-vue": "2.0.0-beta.15",
27 "apexcharts": "3.22.0", 27 "apexcharts": "3.22.0",
28 "axios": "^0.21.0", 28 "axios": "^0.21.0",
@@ -35,7 +35,7 @@ @@ -35,7 +35,7 @@
35 "qrcode": "^1.4.4", 35 "qrcode": "^1.4.4",
36 "vditor": "^3.6.2", 36 "vditor": "^3.6.2",
37 "vue": "^3.0.2", 37 "vue": "^3.0.2",
38 - "vue-i18n": "^9.0.0-beta.7", 38 + "vue-i18n": "^9.0.0-beta.8",
39 "vue-router": "^4.0.0-rc.3", 39 "vue-router": "^4.0.0-rc.3",
40 "vuex": "^4.0.0-rc.1", 40 "vuex": "^4.0.0-rc.1",
41 "vuex-module-decorators": "^1.0.1", 41 "vuex-module-decorators": "^1.0.1",
@@ -50,6 +50,7 @@ @@ -50,6 +50,7 @@
50 "@purge-icons/generated": "^0.4.1", 50 "@purge-icons/generated": "^0.4.1",
51 "@types/echarts": "^4.9.1", 51 "@types/echarts": "^4.9.1",
52 "@types/fs-extra": "^9.0.4", 52 "@types/fs-extra": "^9.0.4",
  53 + "@types/globrex": "^0.1.0",
53 "@types/koa-static": "^4.0.1", 54 "@types/koa-static": "^4.0.1",
54 "@types/lodash-es": "^4.17.3", 55 "@types/lodash-es": "^4.17.3",
55 "@types/mockjs": "^1.0.3", 56 "@types/mockjs": "^1.0.3",
@@ -68,6 +69,7 @@ @@ -68,6 +69,7 @@
68 "conventional-changelog-cli": "^2.1.1", 69 "conventional-changelog-cli": "^2.1.1",
69 "conventional-changelog-custom-config": "^0.3.1", 70 "conventional-changelog-custom-config": "^0.3.1",
70 "cross-env": "^7.0.2", 71 "cross-env": "^7.0.2",
  72 + "dot-prop": "^6.0.0",
71 "dotenv": "^8.2.0", 73 "dotenv": "^8.2.0",
72 "eslint": "^7.13.0", 74 "eslint": "^7.13.0",
73 "eslint-config-prettier": "^6.15.0", 75 "eslint-config-prettier": "^6.15.0",
@@ -75,6 +77,7 @@ @@ -75,6 +77,7 @@
75 "eslint-plugin-vue": "^7.1.0", 77 "eslint-plugin-vue": "^7.1.0",
76 "esno": "^0.2.4", 78 "esno": "^0.2.4",
77 "fs-extra": "^9.0.1", 79 "fs-extra": "^9.0.1",
  80 + "globrex": "^0.1.2",
78 "husky": "^4.3.0", 81 "husky": "^4.3.0",
79 "koa-static": "^5.0.0", 82 "koa-static": "^5.0.0",
80 "less": "^3.12.2", 83 "less": "^3.12.2",
src/hooks/web/useLocale.ts 0 → 100644
  1 +import type { LocaleType } from '/@/locales/types';
  2 +import { appStore } from '/@/store/modules/app';
  3 +
  4 +export function useLocale() {
  5 + /**
  6 + *
  7 + */
  8 + function getLocale(): string {
  9 + return appStore.getProjectConfig.locale;
  10 + }
  11 +
  12 + /**
  13 + *
  14 + * @param locale
  15 + */
  16 + async function changeLocale(locale: LocaleType): Promise<void> {
  17 + appStore.commitProjectConfigState({ locale: locale });
  18 + }
  19 +
  20 + return { getLocale, changeLocale };
  21 +}
src/locales/index.ts 0 → 100644
  1 +import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)';
  2 +
  3 +export default messages;
src/locales/lang/en/routes/menus/dashboard.ts 0 → 100644
  1 +export default {
  2 + someentry: 'some text',
  3 +};
src/locales/lang/en/system/basic.ts 0 → 100644
  1 +export default {
  2 + some: 'Get Out',
  3 +};
src/locales/lang/en/system/login.ts 0 → 100644
  1 +export default {
  2 + button: 'Login',
  3 +};
src/locales/lang/ru/routes/menus/dashboard.ts 0 → 100644
  1 +export default {
  2 + someentry: 'some text',
  3 +};
src/locales/lang/ru/system/basic.ts 0 → 100644
  1 +export default {
  2 + some: 'Get Out',
  3 +};
src/locales/lang/ru/system/login.ts 0 → 100644
  1 +export default {
  2 + button: 'Login',
  3 + validation: {
  4 + account: 'Required Field account',
  5 + password: 'Required Field password',
  6 + },
  7 +};
src/locales/lang/zhCN/routes/menus/dashboard.ts 0 → 100644
  1 +export default {
  2 + someentry: '一些文本',
  3 +};
src/locales/lang/zhCN/system/basic.ts 0 → 100644
  1 +export default {
  2 + some: '出去',
  3 +};
src/locales/lang/zhCN/system/login.ts 0 → 100644
  1 +export default {
  2 + button: '登录',
  3 +};
src/locales/types.ts 0 → 100644
  1 +export type LocaleType = 'zhCN' | 'en' | 'ru' | 'ja';
src/main.ts
@@ -5,6 +5,7 @@ import { setupStore } from &#39;/@/store&#39;; @@ -5,6 +5,7 @@ import { setupStore } from &#39;/@/store&#39;;
5 import { setupAntd } from '/@/setup/ant-design-vue'; 5 import { setupAntd } from '/@/setup/ant-design-vue';
6 import { setupErrorHandle } from '/@/setup/error-handle'; 6 import { setupErrorHandle } from '/@/setup/error-handle';
7 import { setupGlobDirectives } from '/@/setup/directives'; 7 import { setupGlobDirectives } from '/@/setup/directives';
  8 +import { setupI18n } from '/@/setup/i18n';
8 9
9 import { setupProdMockServer } from '../mock/_createProductionServer'; 10 import { setupProdMockServer } from '../mock/_createProductionServer';
10 import { setApp } from '/@/setup/App'; 11 import { setApp } from '/@/setup/App';
@@ -15,11 +16,16 @@ import { isDevMode, isProdMode, isUseMock } from &#39;/@/utils/env&#39;; @@ -15,11 +16,16 @@ import { isDevMode, isProdMode, isUseMock } from &#39;/@/utils/env&#39;;
15 16
16 import '/@/design/index.less'; 17 import '/@/design/index.less';
17 18
  19 +import '/@/locales/index';
  20 +
18 const app = createApp(App); 21 const app = createApp(App);
19 22
20 // Configure component library 23 // Configure component library
21 setupAntd(app); 24 setupAntd(app);
22 25
  26 +// Multilingual configuration
  27 +setupI18n(app);
  28 +
23 // Configure routing 29 // Configure routing
24 setupRouter(app); 30 setupRouter(app);
25 31
src/settings/projectSetting.ts
@@ -7,6 +7,7 @@ import { isProdMode } from &#39;/@/utils/env&#39;; @@ -7,6 +7,7 @@ import { isProdMode } from &#39;/@/utils/env&#39;;
7 7
8 // ! You need to clear the browser cache after the change 8 // ! You need to clear the browser cache after the change
9 const setting: ProjectConfig = { 9 const setting: ProjectConfig = {
  10 + locale: 'en',
10 // color 11 // color
11 // TODO 主题色 12 // TODO 主题色
12 themeColor: primaryColor, 13 themeColor: primaryColor,
src/setup/i18n/index.ts 0 → 100644
  1 +import type { App } from 'vue';
  2 +import type { I18n, Locale, I18nOptions } from 'vue-i18n';
  3 +
  4 +import { createI18n } from 'vue-i18n';
  5 +import localeMessages from '/@/locales';
  6 +import { useLocale } from '/@/hooks/web/useLocale';
  7 +
  8 +const { getLocale } = useLocale();
  9 +
  10 +const localeData: I18nOptions = {
  11 + legacy: false,
  12 + locale: getLocale(),
  13 + // TODO: setting fallback inside settings
  14 + fallbackLocale: 'en',
  15 + messages: localeMessages,
  16 + // availableLocales: ['ru'],
  17 + sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
  18 + silentTranslationWarn: false, // true - warning off
  19 + silentFallbackWarn: true,
  20 +};
  21 +
  22 +let i18n: I18n;
  23 +
  24 +// setup i18n instance with glob
  25 +export function setupI18n(app: App) {
  26 + i18n = createI18n(localeData) as I18n;
  27 + setI18nLanguage(getLocale());
  28 + app.use(i18n);
  29 +}
  30 +
  31 +export function setI18nLanguage(locale: Locale): void {
  32 + // @ts-ignore
  33 + i18n.global.locale.value = locale;
  34 + // i18n.global.setLocaleMessage(locale, messages);
  35 +}
src/store/index.ts
1 import type { App } from 'vue'; 1 import type { App } from 'vue';
2 -import { createStore, createLogger, Plugin } from 'vuex'; 2 +import {
  3 + createStore,
  4 + // createLogger, Plugin
  5 +} from 'vuex';
3 import { config } from 'vuex-module-decorators'; 6 import { config } from 'vuex-module-decorators';
4 import { isDevMode } from '/@/utils/env'; 7 import { isDevMode } from '/@/utils/env';
5 8
6 config.rawError = true; 9 config.rawError = true;
7 const isDev = isDevMode(); 10 const isDev = isDevMode();
8 -const plugins: Plugin<any>[] = isDev ? [createLogger()] : []; 11 +// const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
9 12
10 const store = createStore({ 13 const store = createStore({
11 // modules: {}, 14 // modules: {},
12 strict: isDev, 15 strict: isDev,
13 - plugins, 16 + // plugins,
14 }); 17 });
15 18
16 export function setupStore(app: App<Element>) { 19 export function setupStore(app: App<Element>) {
src/types/config.d.ts
1 // 左侧菜单, 顶部菜单 1 // 左侧菜单, 顶部菜单
2 import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum'; 2 import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
3 import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum'; 3 import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
4 - 4 +import type { LocaleType } from '/@/locales/types';
5 export interface MessageSetting { 5 export interface MessageSetting {
6 title: string; 6 title: string;
7 // 取消按钮的文字, 7 // 取消按钮的文字,
@@ -55,6 +55,7 @@ export interface HeaderSetting { @@ -55,6 +55,7 @@ export interface HeaderSetting {
55 showNotice: boolean; 55 showNotice: boolean;
56 } 56 }
57 export interface ProjectConfig { 57 export interface ProjectConfig {
  58 + locale: LocaleType;
58 // header背景色 59 // header背景色
59 headerBgColor: string; 60 headerBgColor: string;
60 // 左侧菜单背景色 61 // 左侧菜单背景色
src/types/module.d.ts
@@ -4,4 +4,6 @@ declare module &#39;globby!/@/router/routes/modules/**/*.@(ts)&#39;; @@ -4,4 +4,6 @@ declare module &#39;globby!/@/router/routes/modules/**/*.@(ts)&#39;;
4 4
5 declare module 'globby!/@/router/menus/modules/**/*.@(ts)'; 5 declare module 'globby!/@/router/menus/modules/**/*.@(ts)';
6 6
  7 +declare module 'globby?locale!/@/locales/lang/**/*.@(ts)';
  8 +
7 declare const React: string; 9 declare const React: string;
src/views/sys/login/Login.vue
@@ -11,14 +11,14 @@ @@ -11,14 +11,14 @@
11 11
12 <a-form class="mx-auto mt-10" :model="formData" :rules="formRules" ref="formRef"> 12 <a-form class="mx-auto mt-10" :model="formData" :rules="formRules" ref="formRef">
13 <a-form-item name="account"> 13 <a-form-item name="account">
14 - <a-input size="large" v-model:value="formData.account" placeholder="Username: vben" /> 14 + <a-input size="large" v-model:value="formData.account" placeholder="username: vben" />
15 </a-form-item> 15 </a-form-item>
16 <a-form-item name="password"> 16 <a-form-item name="password">
17 <a-input-password 17 <a-input-password
18 size="large" 18 size="large"
19 visibilityToggle 19 visibilityToggle
20 v-model:value="formData.password" 20 v-model:value="formData.password"
21 - placeholder="Password: 123456" 21 + placeholder="password: 123456"
22 /> 22 />
23 </a-form-item> 23 </a-form-item>
24 24
@@ -28,13 +28,13 @@ @@ -28,13 +28,13 @@
28 <a-row> 28 <a-row>
29 <a-col :span="12"> 29 <a-col :span="12">
30 <a-form-item> 30 <a-form-item>
31 - <!-- 未做逻辑,需要自行处理 --> 31 + <!-- No logic, you need to deal with it yourself -->
32 <a-checkbox v-model:checked="autoLogin" size="small">自动登录</a-checkbox> 32 <a-checkbox v-model:checked="autoLogin" size="small">自动登录</a-checkbox>
33 </a-form-item> 33 </a-form-item>
34 </a-col> 34 </a-col>
35 <a-col :span="12"> 35 <a-col :span="12">
36 <a-form-item :style="{ 'text-align': 'right' }"> 36 <a-form-item :style="{ 'text-align': 'right' }">
37 - <!-- 未做逻辑,需要自行处理 --> 37 + <!-- No logic, you need to deal with it yourself -->
38 <a-button type="link" size="small">忘记密码</a-button> 38 <a-button type="link" size="small">忘记密码</a-button>
39 </a-form-item> 39 </a-form-item>
40 </a-col> 40 </a-col>
@@ -47,7 +47,7 @@ @@ -47,7 +47,7 @@
47 :block="true" 47 :block="true"
48 @click="login" 48 @click="login"
49 :loading="formState.loading" 49 :loading="formState.loading"
50 - >登录</a-button 50 + >{{ t('system.login.button') }}</a-button
51 > 51 >
52 </a-form-item> 52 </a-form-item>
53 </a-form> 53 </a-form>
@@ -57,20 +57,15 @@ @@ -57,20 +57,15 @@
57 </div> 57 </div>
58 </template> 58 </template>
59 <script lang="ts"> 59 <script lang="ts">
60 - import {  
61 - defineComponent,  
62 - reactive,  
63 - ref,  
64 - unref,  
65 - toRaw,  
66 - // computed  
67 - } from 'vue'; 60 + import { defineComponent, reactive, ref, unref, toRaw } from 'vue';
68 import { Checkbox } from 'ant-design-vue'; 61 import { Checkbox } from 'ant-design-vue';
69 62
70 import Button from '/@/components/Button/index.vue'; 63 import Button from '/@/components/Button/index.vue';
71 // import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index'; 64 // import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
72 65
73 import { userStore } from '/@/store/modules/user'; 66 import { userStore } from '/@/store/modules/user';
  67 + import { useI18n } from 'vue-i18n';
  68 +
74 // import { appStore } from '/@/store/modules/app'; 69 // import { appStore } from '/@/store/modules/app';
75 import { useMessage } from '/@/hooks/web/useMessage'; 70 import { useMessage } from '/@/hooks/web/useMessage';
76 import { useSetting } from '/@/hooks/core/useSetting'; 71 import { useSetting } from '/@/hooks/core/useSetting';
@@ -139,7 +134,7 @@ @@ -139,7 +134,7 @@
139 formState.loading = false; 134 formState.loading = false;
140 } 135 }
141 } 136 }
142 - 137 + const { t } = useI18n();
143 return { 138 return {
144 formRef, 139 formRef,
145 // verifyRef, 140 // verifyRef,
@@ -151,6 +146,7 @@ @@ -151,6 +146,7 @@
151 // openLoginVerify: openLoginVerifyRef, 146 // openLoginVerify: openLoginVerifyRef,
152 title: globSetting && globSetting.title, 147 title: globSetting && globSetting.title,
153 logo, 148 logo,
  149 + t,
154 }; 150 };
155 }, 151 },
156 }); 152 });
vite.config.ts
@@ -4,8 +4,9 @@ import { resolve } from &#39;path&#39;; @@ -4,8 +4,9 @@ import { resolve } from &#39;path&#39;;
4 4
5 import { modifyVars } from './build/config/lessModifyVars'; 5 import { modifyVars } from './build/config/lessModifyVars';
6 import { createProxy } from './build/vite/proxy'; 6 import { createProxy } from './build/vite/proxy';
7 -import globbyTransform from './build/vite/plugin/context/transform';  
8 -import dynamicImportTransform from './build/vite/plugin/dynamicImport/index'; 7 +
  8 +import globbyTransform from './build/vite/plugin/transform/globby';
  9 +import dynamicImportTransform from './build/vite/plugin/transform/dynamic-import';
9 10
10 import { isDevFn, loadEnv } from './build/utils'; 11 import { isDevFn, loadEnv } from './build/utils';
11 12
@@ -111,6 +112,11 @@ const viteConfig: UserConfig = { @@ -111,6 +112,11 @@ const viteConfig: UserConfig = {
111 }, 112 },
112 define: { 113 define: {
113 __VERSION__: pkg.version, 114 __VERSION__: pkg.version,
  115 + // use vue-i18-next
  116 + // Suppress warning
  117 + __VUE_I18N_LEGACY_API__: false,
  118 + __VUE_I18N_FULL_INSTALL__: false,
  119 + __INTLIFY_PROD_DEVTOOLS__: false,
114 }, 120 },
115 cssPreprocessOptions: { 121 cssPreprocessOptions: {
116 less: { 122 less: {
@@ -135,5 +141,13 @@ const viteConfig: UserConfig = { @@ -135,5 +141,13 @@ const viteConfig: UserConfig = {
135 141
136 export default { 142 export default {
137 ...viteConfig, 143 ...viteConfig,
138 - transforms: [globbyTransform(viteConfig), dynamicImportTransform(viteEnv)], 144 + transforms: [
  145 + globbyTransform({
  146 + resolvers: viteConfig.resolvers,
  147 + root: viteConfig.root,
  148 + alias: viteConfig.alias,
  149 + includes: [resolve('src/router'), resolve('src/locales')],
  150 + }),
  151 + dynamicImportTransform(viteEnv),
  152 + ],
139 } as UserConfig; 153 } as UserConfig;
yarn.lock
@@ -1323,6 +1323,11 @@ @@ -1323,6 +1323,11 @@
1323 dependencies: 1323 dependencies:
1324 "@types/node" "*" 1324 "@types/node" "*"
1325 1325
  1326 +"@types/globrex@^0.1.0":
  1327 + version "0.1.0"
  1328 + resolved "https://registry.npmjs.org/@types/globrex/-/globrex-0.1.0.tgz#baf4ac8e36947017612c01fde7c7b641dc0b6c89"
  1329 + integrity sha512-aBkxDgp/UbnluE+CIT3V3PoNewwOlLCzXSF3ipD86Slv8xVjwxrDAfSGbsfGgMzPo/fEMPXc+gNUJbtiugwfoA==
  1330 +
1326 "@types/http-assert@*": 1331 "@types/http-assert@*":
1327 version "1.5.1" 1332 version "1.5.1"
1328 resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" 1333 resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
@@ -1732,18 +1737,18 @@ @@ -1732,18 +1737,18 @@
1732 vscode-languageserver-textdocument "^1.0.1" 1737 vscode-languageserver-textdocument "^1.0.1"
1733 vscode-uri "^2.1.2" 1738 vscode-uri "^2.1.2"
1734 1739
1735 -"@vueuse/core@4.0.0-beta.41":  
1736 - version "4.0.0-beta.41"  
1737 - resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-beta.41.tgz#0058aed5ade75ae2866283498009ad5172cbae84"  
1738 - integrity sha512-CgUih65PzYScorm1S4F93e6XXm+qxA8GrRLOSB1kXaqtP6vXedwkBxKkNEYNACx4reL4VEHqM/BrM6FajXkQUg== 1740 +"@vueuse/core@4.0.0-rc.3":
  1741 + version "4.0.0-rc.3"
  1742 + resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-rc.3.tgz#5381ca657e10df596cd7027fc5c96b2d4b3a090c"
  1743 + integrity sha512-dQ/FZgo0z7kBFOvDWxuzaUrmuO8X1AlQk17e3PU1TVtG2Uu+mCvjPNbuvI2fjhTjl5rzPJawwoU2WZFj+nlFvw==
1739 dependencies: 1744 dependencies:
1740 - "@vueuse/shared" "4.0.0-beta.41" 1745 + "@vueuse/shared" "4.0.0-rc.3"
1741 vue-demi latest 1746 vue-demi latest
1742 1747
1743 -"@vueuse/shared@4.0.0-beta.41":  
1744 - version "4.0.0-beta.41"  
1745 - resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-beta.41.tgz#395782ea2e580f1fc9488d25c89bd09f70170b25"  
1746 - integrity sha512-dqnuEPPC3OUJ6L6rhMiOCuPWIR698DtdwOydwCZBISsG2V6gZ2QFND6xtRwLib6/lhUMYVYPwIz3hPjlx7BIzw== 1748 +"@vueuse/shared@4.0.0-rc.3":
  1749 + version "4.0.0-rc.3"
  1750 + resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-rc.3.tgz#42fb56fed3779f3b8a17a82c16a364bad20d01b7"
  1751 + integrity sha512-VY0x/XxpeTMHp/0FDiv1cgUUxkJGQl7liiM2AjR/J7+Ys/2Y2dijD5cAKViq9FGUPQQsOcLptMvMvUsDMoN4DA==
1747 dependencies: 1752 dependencies:
1748 vue-demi latest 1753 vue-demi latest
1749 1754
@@ -3222,6 +3227,13 @@ dot-prop@^5.1.0: @@ -3222,6 +3227,13 @@ dot-prop@^5.1.0:
3222 dependencies: 3227 dependencies:
3223 is-obj "^2.0.0" 3228 is-obj "^2.0.0"
3224 3229
  3230 +dot-prop@^6.0.0:
  3231 + version "6.0.0"
  3232 + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.0.tgz#bd579fd704d970981c4b05de591db648959f2ebb"
  3233 + integrity sha512-xCbB8IN3IT+tdgoEPOnJmYTNJDrygGFOmiQEiVa5eAD+JEB1vTgMNhVGRnN5Eex/6amck7cdcrixb1qN9Go+GQ==
  3234 + dependencies:
  3235 + is-obj "^2.0.0"
  3236 +
3225 dotenv-expand@^5.1.0: 3237 dotenv-expand@^5.1.0:
3226 version "5.1.0" 3238 version "5.1.0"
3227 resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" 3239 resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
@@ -4092,6 +4104,11 @@ globjoin@^0.1.4: @@ -4092,6 +4104,11 @@ globjoin@^0.1.4:
4092 resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" 4104 resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
4093 integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM= 4105 integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=
4094 4106
  4107 +globrex@^0.1.2:
  4108 + version "0.1.2"
  4109 + resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
  4110 + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
  4111 +
4095 gonzales-pe@^4.3.0: 4112 gonzales-pe@^4.3.0:
4096 version "4.3.0" 4113 version "4.3.0"
4097 resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3" 4114 resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3"
@@ -8167,10 +8184,10 @@ vue-eslint-parser@^7.1.1: @@ -8167,10 +8184,10 @@ vue-eslint-parser@^7.1.1:
8167 esquery "^1.0.1" 8184 esquery "^1.0.1"
8168 lodash "^4.17.15" 8185 lodash "^4.17.15"
8169 8186
8170 -vue-i18n@^9.0.0-beta.7:  
8171 - version "9.0.0-beta.7"  
8172 - resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.7.tgz#f6fad5b4be218018aab4797f80dd2a95ee5236f9"  
8173 - integrity sha512-hFl0XnV91P/4UyWvHYvdYxuk3GRnKIW9zXAm6hrUU4mOIwpqchi7jVQva2TJLr52Mpsu4zYXmzL1h5pgrKmCfQ== 8187 +vue-i18n@^9.0.0-beta.8:
  8188 + version "9.0.0-beta.8"
  8189 + resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.8.tgz#92282d5b5e0e6f15cc04943ce11bf49db610468f"
  8190 + integrity sha512-tViSN96jLi0AKvAVi4twcYYN5Ld++SqN1/140ua+YWm/iRbO2M0rAcsZ7e6/4LTm6Pd1ldSwWihSuv2bSQmlnw==
8174 dependencies: 8191 dependencies:
8175 source-map "0.6.1" 8192 source-map "0.6.1"
8176 8193