提交 737b1b19 编写于 作者: V vben

wip: multi-language support

上级 b49950a3
## Wip
### 🎫 Chores
- 移除 messageSetting 配置
## 2.0.0-rc.11 (2020-11-18) ## 2.0.0-rc.11 (2020-11-18)
### ✨ Features ### ✨ Features
......
<template> <template>
<ConfigProvider v-bind="lockEvent" :locale="zhCN" :transform-cell-text="transformCellText"> <ConfigProvider
v-bind="lockEvent"
:locale="antConfigLocale"
:transform-cell-text="transformCellText"
>
<router-view /> <router-view />
</ConfigProvider> </ConfigProvider>
</template> </template>
...@@ -7,16 +11,12 @@ ...@@ -7,16 +11,12 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { ConfigProvider } from 'ant-design-vue'; import { ConfigProvider } from 'ant-design-vue';
import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
import zhCN from 'ant-design-vue/es/locale/zh_CN';
import moment from 'moment';
import 'moment/dist/locale/zh-cn';
import { getConfigProvider, initAppConfigStore } from '/@/setup/App'; import { getConfigProvider, initAppConfigStore } from '/@/setup/App';
import { useLockPage } from '/@/hooks/web/useLockPage';
moment.locale('zh-cn'); import { useLockPage } from '/@/hooks/web/useLockPage';
import { useLocale } from '/@/hooks/web/useLocale';
import { createBreakpointListen } from '/@/hooks/event/useBreakpoint';
export default defineComponent({ export default defineComponent({
name: 'App', name: 'App',
...@@ -34,9 +34,12 @@ ...@@ -34,9 +34,12 @@
// Create a lock screen monitor // Create a lock screen monitor
const lockEvent = useLockPage(); const lockEvent = useLockPage();
// support Multi-language
const { antConfigLocale } = useLocale();
return { return {
transformCellText, transformCellText,
zhCN, antConfigLocale,
lockEvent, lockEvent,
}; };
}, },
......
import AppLocalPicker from './src/AppLocalPicker.vue';
export { AppLocalPicker };
<template>
<Dropdown
:trigger="['click']"
:dropMenuList="localeList"
:selectedKeys="selectedKeys"
@menuEvent="handleMenuEvent"
>
<GlobalOutlined class="app-locale" />
</Dropdown>
</template>
<script lang="ts">
import { defineComponent, ref, watchEffect, unref } from 'vue';
import { Dropdown, DropMenu } from '/@/components/Dropdown';
import { GlobalOutlined } from '@ant-design/icons-vue';
import { useLocale } from '/@/hooks/web/useLocale';
import { useLocaleSetting } from '/@/settings/use/useLocaleSetting';
import { LocaleType } from '/@/locales/types';
export default defineComponent({
name: 'AppLocalPicker',
components: { GlobalOutlined, Dropdown },
setup() {
const { localeList } = useLocaleSetting();
const selectedKeys = ref<string[]>([]);
const { changeLocale, getLang } = useLocale();
watchEffect(() => {
selectedKeys.value = [unref(getLang)];
});
function toggleLocale(lang: LocaleType | string) {
changeLocale(lang as LocaleType);
selectedKeys.value = [lang as string];
}
function handleMenuEvent(menu: DropMenu) {
toggleLocale(menu.event as string);
}
return { localeList, handleMenuEvent, selectedKeys };
},
});
</script>
<style lang="less" scoped>
.app-locale {
cursor: pointer;
}
</style>
export { default as Dropdown } from './Dropdown'; export { default as Dropdown } from './src/Dropdown';
export * from './types'; export * from './src/types';
import { defineComponent, computed, unref } from 'vue'; import { defineComponent, computed, unref } from 'vue';
import { Dropdown, Menu } from 'ant-design-vue'; import { Dropdown, Menu, Divider } from 'ant-design-vue';
import Icon from '/@/components/Icon/index'; import Icon from '/@/components/Icon/index';
import { basicDropdownProps } from './props'; import { basicDropdownProps } from './props';
import { getSlot } from '/@/utils/helper/tsxHelper'; import { getSlot } from '/@/utils/helper/tsxHelper';
import { Trigger } from './types';
export default defineComponent({ export default defineComponent({
name: 'Dropdown', name: 'Dropdown',
props: basicDropdownProps, props: basicDropdownProps,
emits: ['menuEvent'],
setup(props, { slots, emit, attrs }) { setup(props, { slots, emit, attrs }) {
const getMenuList = computed(() => props.dropMenuList); const getMenuList = computed(() => props.dropMenuList);
function handleClickMenu({ key }: any) { function handleClickMenu({ key }: any) {
const menu = unref(getMenuList)[key]; const menu = unref(getMenuList).find((item) => item.event === key);
emit('menuEvent', menu); emit('menuEvent', menu);
} }
function renderMenus() { function renderMenus() {
return ( return (
<Menu onClick={handleClickMenu}> <Menu onClick={handleClickMenu} selectedKeys={props.selectedKeys}>
{() => ( {() => (
<> <>
{unref(getMenuList).map((item, index) => { {unref(getMenuList).map((item, index) => {
const { disabled, icon, text, divider } = item; const { disabled, icon, text, divider, event } = item;
return [ return [
<Menu.Item key={`${index}`} disabled={disabled}> <Menu.Item key={`${event}`} disabled={disabled}>
{() => ( {() => (
<> <>
{icon && <Icon icon={icon} />} {icon && <Icon icon={icon} />}
...@@ -34,8 +35,7 @@ export default defineComponent({ ...@@ -34,8 +35,7 @@ export default defineComponent({
</> </>
)} )}
</Menu.Item>, </Menu.Item>,
// @ts-ignore divider && <Divider key={`d-${index}`} />,
divider && <Menu.Divider key={`d-${index}`} />,
]; ];
})} })}
</> </>
...@@ -45,7 +45,7 @@ export default defineComponent({ ...@@ -45,7 +45,7 @@ export default defineComponent({
} }
return () => ( return () => (
<Dropdown trigger={props.trigger as any} {...attrs}> <Dropdown trigger={props.trigger as Trigger[]} {...attrs}>
{{ {{
default: () => <span>{getSlot(slots)}</span>, default: () => <span>{getSlot(slots)}</span>,
overlay: () => renderMenus(), overlay: () => renderMenus(),
......
import type { PropType } from 'vue'; import type { PropType } from 'vue';
import type { DropMenu } from './types';
export const dropdownProps = { export const dropdownProps = {
/** /**
...@@ -15,7 +16,11 @@ export const dropdownProps = { ...@@ -15,7 +16,11 @@ export const dropdownProps = {
}; };
export const basicDropdownProps = Object.assign({}, dropdownProps, { export const basicDropdownProps = Object.assign({}, dropdownProps, {
dropMenuList: { dropMenuList: {
type: Array as PropType<any[]>, type: Array as PropType<DropMenu[]>,
default: () => [],
},
selectedKeys: {
type: Array as PropType<string[]>,
default: () => [], default: () => [],
}, },
}); });
...@@ -6,3 +6,5 @@ export interface DropMenu { ...@@ -6,3 +6,5 @@ export interface DropMenu {
disabled?: boolean; disabled?: boolean;
divider?: boolean; divider?: boolean;
} }
export type Trigger = 'click' | 'hover' | 'contextMenu';
import { createI18n } from 'vue-i18n';
import { ref, watch } from 'vue';
import type { I18nOptions } from 'vue-i18n';
export function useI18n(options?: I18nOptions) {
const i18n = createI18n(options);
const localeRef = ref(i18n.global.locale);
watch(localeRef, () => {
i18n.global.locale = localeRef.value as any;
});
return {
t: i18n.global.t,
localeRef,
};
}
/**
* Multi-language related operations
*/
import type { LocaleType } from '/@/locales/types'; import type { LocaleType } from '/@/locales/types';
import { appStore } from '/@/store/modules/app';
import { unref, ref } from 'vue';
import { getI18n } from '/@/setup/i18n';
import { useLocaleSetting } from '/@/settings/use/useLocaleSetting';
import moment from 'moment';
import 'moment/dist/locale/zh-cn';
moment.locale('zh-cn');
const antConfigLocaleRef = ref<any>(null);
export function useLocale() { export function useLocale() {
/** const { getLang, getLocale, setLocale: setLocalSetting } = useLocaleSetting();
*
*/ // Switching the language will change the locale of useI18n
function getLocale(): string { // And submit to configuration modification
return appStore.getProjectConfig.locale; function changeLocale(lang: LocaleType): void {
(getI18n().global.locale as any).value = lang;
setLocalSetting({ lang });
// i18n.global.setLocaleMessage(locale, messages);
antConfigLocaleRef.value = { a: 1 };
switch (lang) {
// Simplified Chinese
case 'zh_CN':
import('ant-design-vue/es/locale/zh_CN').then((locale) => {
antConfigLocaleRef.value = locale.default;
});
moment.locale('cn');
break;
// English
case 'en':
import('ant-design-vue/es/locale/en_US').then((locale) => {
antConfigLocaleRef.value = locale.default;
});
moment.locale('en-us');
break;
// other
default:
break;
}
} }
/** // initialization
* function setupLocale() {
* @param locale const lang = unref(getLang);
*/ lang && changeLocale(lang);
async function changeLocale(locale: LocaleType): Promise<void> {
appStore.commitProjectConfigState({ locale: locale });
} }
return { getLocale, changeLocale }; return {
setupLocale,
getLocale,
getLang,
changeLocale,
antConfigLocale: antConfigLocaleRef,
};
}
/**
* For non-setup use
*/
export function useExternalI18n() {
return getI18n().global;
} }
...@@ -3,7 +3,6 @@ import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal'; ...@@ -3,7 +3,6 @@ import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal';
import { Modal, message as Message, notification } from 'ant-design-vue'; import { Modal, message as Message, notification } from 'ant-design-vue';
import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue'; import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue';
import { useSetting } from '/@/hooks/core/useSetting';
import { ArgsProps, ConfigProps } from 'ant-design-vue/lib/notification'; import { ArgsProps, ConfigProps } from 'ant-design-vue/lib/notification';
export interface NotifyApi { export interface NotifyApi {
...@@ -33,8 +32,6 @@ interface ConfirmOptions { ...@@ -33,8 +32,6 @@ interface ConfirmOptions {
warning: ModalFunc; warning: ModalFunc;
} }
const { projectSetting } = useSetting();
function getIcon(iconType: string) { function getIcon(iconType: string) {
if (iconType === 'warning') { if (iconType === 'warning') {
return <InfoCircleFilled class="modal-icon-warning" />; return <InfoCircleFilled class="modal-icon-warning" />;
...@@ -60,7 +57,6 @@ function createConfirm(options: ModalOptionsEx): ConfirmOptions { ...@@ -60,7 +57,6 @@ function createConfirm(options: ModalOptionsEx): ConfirmOptions {
const opt: ModalFuncProps = { const opt: ModalFuncProps = {
centered: true, centered: true,
icon: getIcon(iconType), icon: getIcon(iconType),
...projectSetting.messageSetting,
...options, ...options,
}; };
return Modal.confirm(opt) as any; return Modal.confirm(opt) as any;
......
...@@ -5,7 +5,6 @@ import Button from '/@/components/Button/index.vue'; ...@@ -5,7 +5,6 @@ import Button from '/@/components/Button/index.vue';
import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue'; import { CopyOutlined, RedoOutlined, CheckOutlined } from '@ant-design/icons-vue';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
import { userStore } from '/@/store/modules/user';
import { ProjectConfig } from '/@/types/config'; import { ProjectConfig } from '/@/types/config';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
...@@ -97,7 +96,7 @@ export default defineComponent({ ...@@ -97,7 +96,7 @@ export default defineComponent({
function handleClearAndRedo() { function handleClearAndRedo() {
localStorage.clear(); localStorage.clear();
userStore.resumeAllState(); appStore.resumeAllState();
location.reload(); location.reload();
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent, PropType, ref, watch } from 'vue'; import { computed, defineComponent, PropType, ref, watch } from 'vue';
// hooks // hooks
import { useSetting } from '/@/hooks/core/useSetting'; import { useGlobSetting } from '/@/settings/use';
import { useTimeoutFn } from '/@/hooks/core/useTimeout'; import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { useGo } from '/@/hooks/web/usePage'; import { useGo } from '/@/hooks/web/usePage';
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
}, },
setup(props) { setup(props) {
const showRef = ref<boolean>(!!props.showTitle); const showRef = ref<boolean>(!!props.showTitle);
const { globSetting } = useSetting(); const globSetting = useGlobSetting();
const go = useGo(); const go = useGo();
function handleGoHome() { function handleGoHome() {
......
...@@ -4,7 +4,7 @@ import { RouterView, RouteLocation } from 'vue-router'; ...@@ -4,7 +4,7 @@ import { RouterView, RouteLocation } from 'vue-router';
import FrameLayout from '/@/layouts/iframe/index.vue'; import FrameLayout from '/@/layouts/iframe/index.vue';
import { useTransition } from './useTransition'; import { useTransition } from './useTransition';
import { useSetting } from '/@/hooks/core/useSetting'; import { useProjectSetting } from '/@/settings/use';
import { tabStore } from '/@/store/modules/tab'; import { tabStore } from '/@/store/modules/tab';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
...@@ -29,7 +29,7 @@ export default defineComponent({ ...@@ -29,7 +29,7 @@ export default defineComponent({
const { on: transitionOn } = useTransition(); const { on: transitionOn } = useTransition();
on = transitionOn; on = transitionOn;
} }
const { projectSetting } = useSetting(); const projectSetting = useProjectSetting();
return () => { return () => {
const { const {
routerTransition, routerTransition,
......
import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)'; import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)';
import type { DropMenu } from '/@/components/Dropdown';
// locale list
export const localeList: DropMenu[] = [
{
text: '简体中文',
event: 'zh_CN',
},
{
text: 'English',
event: 'en',
},
];
export default messages; export default messages;
export default {
tableTitle: 'Error log list',
tableColumnType: 'Type',
tableColumnDate: 'Time',
tableColumnFile: 'File',
tableColumnMsg: 'Error message',
tableColumnStackMsg: 'Stack info',
tableActionDesc: 'Details',
modalTitle: 'Error details',
fireVueError: 'Fire vue error',
fireResourceError: 'Fire resource error',
fireAjaxError: 'Fire ajax error',
enableMessage: 'Only effective when useErrorHandle=true in `/src/settings/projectSetting.ts`.',
};
export default {
backLogin: 'Back Login',
backHome: 'Back Home',
redo: 'Refresh',
subTitle403: "Sorry, you don't have access to this page.",
subTitle404: 'Sorry, the page you visited does not exist.',
subTitle500: 'Sorry, the server is reporting an error.',
noDataTitle: 'No data on the current page.',
networkErrorTitle: 'Network Error',
networkErrorSubTitle:
'Sorry,Your network connection has been disconnected, please check your network!',
};
export default {
alert: 'Lock screen password error',
backToLogin: 'Back to login',
entry: 'Enter the system',
placeholder: 'Please enter the lock screen password or user password',
};
export default {
loginButton: 'Login',
autoLogin: 'AutoLogin',
forgetPassword: 'Forget Password',
// notify
loginSuccessTitle: 'Login successful',
loginSuccessDesc: 'Welcome back',
// placeholder
accountPlaceholder: 'Please input Username',
passwordPlaceholder: 'Please input Password',
};
export default {
some: 'Get Out',
};
export default {
button: 'Login',
};
export default {
someentry: 'some text',
};
export default {
some: 'Get Out',
};
export default {
button: 'Login',
validation: {
account: 'Required Field account',
password: 'Required Field password',
},
};
export default {
some: '出去',
};
export default {
button: '登录',
};
export default {
tableTitle: '错误日志列表',
tableColumnType: '类型',
tableColumnDate: '时间',
tableColumnFile: '文件',
tableColumnMsg: '错误信息',
tableColumnStackMsg: 'stack信息',
tableActionDesc: '详情',
modalTitle: '错误详情',
fireVueError: '点击触发vue错误',
fireResourceError: '点击触发资源加载错误',
fireAjaxError: '点击触发ajax错误',
enableMessage: '只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效.',
};
export default {
backLogin: '返回登录',
backHome: '返回首页',
redo: '刷新',
subTitle403: '抱歉,您无权访问此页面。',
subTitle404: '抱歉,您访问的页面不存在。',
subTitle500: '抱歉,服务器报告错误。',
noDataTitle: '当前页无数据',
networkErrorTitle: '网络错误',
networkErrorSubTitle: '抱歉,您的网络连接已断开,请检查您的网络!',
};
export default {
alert: '锁屏密码错误',
backToLogin: '返回登录',
entry: '进入系统',
placeholder: '请输入锁屏密码或者用户密码',
};
export default {
loginButton: '登录',
autoLogin: '自动登录',
forgetPassword: '忘记密码',
// notify
loginSuccessTitle: '登录成功',
loginSuccessDesc: '欢迎回来',
// placeholder
accountPlaceholder: '请输入账号',
passwordPlaceholder: '请输入密码',
};
export type LocaleType = 'zhCN' | 'en' | 'ru' | 'ja'; export type LocaleType = 'zh_CN' | 'en' | 'ru' | 'ja';
...@@ -6,7 +6,7 @@ import { createProgressGuard } from './progressGuard'; ...@@ -6,7 +6,7 @@ import { createProgressGuard } from './progressGuard';
import { createPermissionGuard } from './permissionGuard'; import { createPermissionGuard } from './permissionGuard';
import { createPageLoadingGuard } from './pageLoadingGuard'; import { createPageLoadingGuard } from './pageLoadingGuard';
import { useSetting } from '/@/hooks/core/useSetting'; import { useGlobSetting, useProjectSetting } from '/@/settings/use';
import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper'; import { getIsOpenTab, setCurrentTo } from '/@/utils/helper/routeHelper';
import { setTitle } from '/@/utils/browser'; import { setTitle } from '/@/utils/browser';
...@@ -14,9 +14,9 @@ import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel'; ...@@ -14,9 +14,9 @@ import { AxiosCanceler } from '/@/utils/http/axios/axiosCancel';
import { tabStore } from '/@/store/modules/tab'; import { tabStore } from '/@/store/modules/tab';
const { projectSetting, globSetting } = useSetting(); const globSetting = useGlobSetting();
export function createGuard(router: Router) { export function createGuard(router: Router) {
const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = projectSetting; const { openNProgress, closeMessageOnSwitch, removeAllHttpPending } = useProjectSetting();
let axiosCanceler: AxiosCanceler | null; let axiosCanceler: AxiosCanceler | null;
if (removeAllHttpPending) { if (removeAllHttpPending) {
axiosCanceler = new AxiosCanceler(); axiosCanceler = new AxiosCanceler();
......
import type { Router, RouteRecordRaw } from 'vue-router'; import type { Router, RouteRecordRaw } from 'vue-router';
import { userStore } from '/@/store/modules/user'; import { appStore } from '/@/store/modules/app';
import { permissionStore } from '/@/store/modules/permission'; import { permissionStore } from '/@/store/modules/permission';
import { PageEnum } from '/@/enums/pageEnum'; import { PageEnum } from '/@/enums/pageEnum';
...@@ -72,7 +72,7 @@ export function createPermissionGuard(router: Router) { ...@@ -72,7 +72,7 @@ export function createPermissionGuard(router: Router) {
router.afterEach((to) => { router.afterEach((to) => {
// Just enter the login page and clear the authentication information // Just enter the login page and clear the authentication information
if (to.path === LOGIN_PATH) { if (to.path === LOGIN_PATH) {
userStore.resumeAllState(); appStore.resumeAllState();
} }
}); });
} }
...@@ -35,10 +35,6 @@ const menu: MenuModule = { ...@@ -35,10 +35,6 @@ const menu: MenuModule = {
path: 'img-preview', path: 'img-preview',
name: '图片预览', name: '图片预览',
}, },
{
path: 'i18n',
name: '国际化',
},
{ {
path: 'copy', path: 'copy',
name: '剪切板', name: '剪切板',
......
...@@ -80,14 +80,6 @@ const feat: AppRouteModule = { ...@@ -80,14 +80,6 @@ const feat: AppRouteModule = {
title: '消息提示', title: '消息提示',
}, },
}, },
{
path: '/i18n',
name: 'I18nDemo',
component: () => import('/@/views/demo/feat/i18n/index.vue'),
meta: {
title: '国际化',
},
},
{ {
path: '/watermark', path: '/watermark',
name: 'WatermarkDemo', name: 'WatermarkDemo',
......
...@@ -7,7 +7,16 @@ import { isProdMode } from '/@/utils/env'; ...@@ -7,7 +7,16 @@ 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', // locale setting
locale: {
// Locales
lang: 'zh_CN',
// Default locale
fallback: 'zh_CN',
// available Locales
availableLocales: ['zh_CN', 'en'],
},
// color // color
// TODO 主题色 // TODO 主题色
themeColor: primaryColor, themeColor: primaryColor,
...@@ -87,15 +96,7 @@ const setting: ProjectConfig = { ...@@ -87,15 +96,7 @@ const setting: ProjectConfig = {
// 开启手风琴模式,只显示一个菜单 // 开启手风琴模式,只显示一个菜单
accordion: true, accordion: true,
}, },
// 消息配置
messageSetting: {
// 弹窗title
title: '操作提示',
// 取消按钮的文子,
cancelText: '取消',
// 确认按钮的文字
okText: '确定',
},
// 多标签 // 多标签
multiTabsSetting: { multiTabsSetting: {
// 开启 // 开启
......
import type { ProjectConfig, GlobConfig, SettingWrap, GlobEnvConfig } from '/@/types/config'; import type { ProjectConfig, GlobConfig, GlobEnvConfig } from '/@/types/config';
import getProjectSetting from '/@/settings/projectSetting'; import getProjectSetting from '/@/settings/projectSetting';
import { getGlobEnvConfig, isDevMode } from '/@/utils/env';
import { getShortName } from '../../../build/getShortName'; import { getShortName } from '../../../build/getShortName';
import { warn } from '/@/utils/log'; import { warn } from '/@/utils/log';
import { getGlobEnvConfig, isDevMode } from '/@/utils/env';
const reg = /[a-zA-Z\_]*/; const reg = /[a-zA-Z\_]*/;
...@@ -12,6 +12,7 @@ const ENV_NAME = getShortName(import.meta.env); ...@@ -12,6 +12,7 @@ const ENV_NAME = getShortName(import.meta.env);
const ENV = ((isDevMode() const ENV = ((isDevMode()
? getGlobEnvConfig() ? getGlobEnvConfig()
: window[ENV_NAME as any]) as unknown) as GlobEnvConfig; : window[ENV_NAME as any]) as unknown) as GlobEnvConfig;
const { const {
VITE_GLOB_APP_TITLE, VITE_GLOB_APP_TITLE,
VITE_GLOB_API_URL, VITE_GLOB_API_URL,
...@@ -25,7 +26,7 @@ if (!reg.test(VITE_GLOB_APP_SHORT_NAME)) { ...@@ -25,7 +26,7 @@ if (!reg.test(VITE_GLOB_APP_SHORT_NAME)) {
); );
} }
export const useSetting = (): SettingWrap => { export const useGlobSetting = (): Readonly<GlobConfig> => {
// Take global configuration // Take global configuration
const glob: Readonly<GlobConfig> = { const glob: Readonly<GlobConfig> = {
title: VITE_GLOB_APP_TITLE, title: VITE_GLOB_APP_TITLE,
...@@ -33,9 +34,10 @@ export const useSetting = (): SettingWrap => { ...@@ -33,9 +34,10 @@ export const useSetting = (): SettingWrap => {
shortName: VITE_GLOB_APP_SHORT_NAME, shortName: VITE_GLOB_APP_SHORT_NAME,
urlPrefix: VITE_GLOB_API_URL_PREFIX, urlPrefix: VITE_GLOB_API_URL_PREFIX,
}; };
const projectSetting: Readonly<ProjectConfig> = getProjectSetting; return glob as Readonly<GlobConfig>;
return { };
globSetting: glob as Readonly<GlobConfig>,
projectSetting, export const useProjectSetting = (): ProjectConfig => {
}; // TODO computed
return getProjectSetting;
}; };
import type { LocaleSetting } from '/@/types/config';
import { computed } from 'vue';
import { appStore } from '/@/store/modules/app';
import getProjectSetting from '/@/settings/projectSetting';
import { localeList } from '/@/locales';
export function useLocaleSetting() {
// Get locale configuration
const getLocale = computed(() => {
return appStore.getProjectConfig.locale || getProjectSetting.locale;
});
// get current language
const getLang = computed(() => {
return getLocale.value.lang;
});
// get Available Locales
const getAvailableLocales = computed((): string[] => {
return getLocale.value.availableLocales;
});
// get Fallback Locales
const getFallbackLocale = computed((): string => {
return getLocale.value.fallback;
});
// Set locale configuration
function setLocale(locale: Partial<LocaleSetting>): void {
appStore.commitProjectConfigState({ locale });
}
return { getLocale, getLang, localeList, setLocale, getAvailableLocales, getFallbackLocale };
}
...@@ -14,9 +14,7 @@ function isAuth(el: Element, binding: any) { ...@@ -14,9 +14,7 @@ function isAuth(el: Element, binding: any) {
const value = binding.value; const value = binding.value;
if (!value) return; if (!value) return;
if (!hasPermission(value)) { if (!hasPermission(value)) {
if (el.parentNode) { el.parentNode?.removeChild(el);
el.parentNode.removeChild(el);
}
} }
} }
......
...@@ -9,7 +9,7 @@ const repeatDirective: Directive = { ...@@ -9,7 +9,7 @@ const repeatDirective: Directive = {
beforeMount(el: Element, binding: DirectiveBinding<any>) { beforeMount(el: Element, binding: DirectiveBinding<any>) {
let interval: Nullable<IntervalHandle> = null; let interval: Nullable<IntervalHandle> = null;
let startTime = 0; let startTime = 0;
const handler = (): void => binding.value && binding.value(); const handler = (): void => binding?.value();
const clear = (): void => { const clear = (): void => {
if (Date.now() - startTime < 100) { if (Date.now() - startTime < 100) {
handler(); handler();
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
*/ */
import { errorStore, ErrorInfo } from '/@/store/modules/error'; import { errorStore, ErrorInfo } from '/@/store/modules/error';
import { useSetting } from '/@/hooks/core/useSetting'; import { useProjectSetting } from '/@/settings/use';
import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
import { App } from 'vue'; import { App } from 'vue';
...@@ -89,7 +89,7 @@ export function scriptErrorHandler( ...@@ -89,7 +89,7 @@ export function scriptErrorHandler(
const errorInfo: Partial<ErrorInfo> = {}; const errorInfo: Partial<ErrorInfo> = {};
colno = colno || (window.event && (window.event as any).errorCharacter) || 0; colno = colno || (window.event && (window.event as any).errorCharacter) || 0;
errorInfo.message = event as string; errorInfo.message = event as string;
if (error && error.stack) { if (error?.stack) {
errorInfo.stack = error.stack; errorInfo.stack = error.stack;
} else { } else {
errorInfo.stack = ''; errorInfo.stack = '';
...@@ -160,8 +160,7 @@ function registerResourceErrorHandler() { ...@@ -160,8 +160,7 @@ function registerResourceErrorHandler() {
* @param app * @param app
*/ */
export function setupErrorHandle(app: App) { export function setupErrorHandle(app: App) {
const { projectSetting } = useSetting(); const { useErrorHandle } = useProjectSetting();
const { useErrorHandle } = projectSetting;
if (!useErrorHandle) return; if (!useErrorHandle) return;
// Vue exception monitoring; // Vue exception monitoring;
app.config.errorHandler = vueErrorHandler; app.config.errorHandler = vueErrorHandler;
......
import type { App } from 'vue'; import { App, unref } from 'vue';
import type { I18n, Locale, I18nOptions } from 'vue-i18n'; import type { I18n, I18nOptions } from 'vue-i18n';
import { createI18n } from 'vue-i18n'; import { createI18n } from 'vue-i18n';
import localeMessages from '/@/locales'; import localeMessages from '/@/locales';
import { useLocale } from '/@/hooks/web/useLocale'; import { useLocale } from '/@/hooks/web/useLocale';
import { useLocaleSetting } from '/@/settings/use/useLocaleSetting';
const { getLocale } = useLocale(); const { setupLocale } = useLocale();
const { getLang, getAvailableLocales, getFallbackLocale } = useLocaleSetting();
const localeData: I18nOptions = { const localeData: I18nOptions = {
legacy: false, legacy: false,
locale: getLocale(), locale: unref(getLang),
// TODO: setting fallback inside settings fallbackLocale: unref(getFallbackLocale),
fallbackLocale: 'en',
messages: localeMessages, messages: localeMessages,
// availableLocales: ['ru'], availableLocales: unref(getAvailableLocales),
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false. 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 silentTranslationWarn: false, // true - warning off
silentFallbackWarn: true, silentFallbackWarn: true,
...@@ -24,12 +25,10 @@ let i18n: I18n; ...@@ -24,12 +25,10 @@ let i18n: I18n;
// setup i18n instance with glob // setup i18n instance with glob
export function setupI18n(app: App) { export function setupI18n(app: App) {
i18n = createI18n(localeData) as I18n; i18n = createI18n(localeData) as I18n;
setI18nLanguage(getLocale()); setupLocale();
app.use(i18n); app.use(i18n);
} }
export function setI18nLanguage(locale: Locale): void { export function getI18n(): I18n {
// @ts-ignore return i18n;
i18n.global.locale.value = locale;
// i18n.global.setLocaleMessage(locale, messages);
} }
...@@ -6,9 +6,19 @@ import store from '/@/store'; ...@@ -6,9 +6,19 @@ import store from '/@/store';
import { PROJ_CFG_KEY, LOCK_INFO_KEY } from '/@/enums/cacheEnum'; import { PROJ_CFG_KEY, LOCK_INFO_KEY } from '/@/enums/cacheEnum';
import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper'; import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
import { setLocal, getLocal, removeLocal } from '/@/utils/helper/persistent'; import {
setLocal,
getLocal,
removeLocal,
clearSession,
clearLocal,
} from '/@/utils/helper/persistent';
import { deepMerge } from '/@/utils'; import { deepMerge } from '/@/utils';
import { resetRouter } from '/@/router';
import { permissionStore } from './permission';
import { tabStore } from './tab';
import { userStore } from './user'; import { userStore } from './user';
export interface LockInfo { export interface LockInfo {
...@@ -77,6 +87,17 @@ class App extends VuexModule { ...@@ -77,6 +87,17 @@ class App extends VuexModule {
this.lockInfoState = null; this.lockInfoState = null;
} }
@Action
async resumeAllState() {
resetRouter();
clearSession();
clearLocal();
permissionStore.commitResetState();
tabStore.commitResetState();
userStore.commitResetState();
}
@Action @Action
public async setPageLoadingAction(loading: boolean): Promise<void> { public async setPageLoadingAction(loading: boolean): Promise<void> {
if (loading) { if (loading) {
......
...@@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-dec ...@@ -4,7 +4,7 @@ import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-dec
import { formatToDateTime } from '/@/utils/dateUtil'; import { formatToDateTime } from '/@/utils/dateUtil';
import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
import { useSetting } from '/@/hooks/core/useSetting'; import { useProjectSetting } from '/@/settings/use';
export interface ErrorInfo { export interface ErrorInfo {
type: ErrorTypeEnum; type: ErrorTypeEnum;
...@@ -16,6 +16,7 @@ export interface ErrorInfo { ...@@ -16,6 +16,7 @@ export interface ErrorInfo {
url: string; url: string;
time?: string; time?: string;
} }
export interface ErrorState { export interface ErrorState {
errorInfoState: ErrorInfo[] | null; errorInfoState: ErrorInfo[] | null;
errorListCountState: number; errorListCountState: number;
...@@ -56,8 +57,7 @@ class Error extends VuexModule implements ErrorState { ...@@ -56,8 +57,7 @@ class Error extends VuexModule implements ErrorState {
@Action @Action
setupErrorHandle(error: any) { setupErrorHandle(error: any) {
const { projectSetting } = useSetting(); const { useErrorHandle } = useProjectSetting();
const { useErrorHandle } = projectSetting;
if (!useErrorHandle) return; if (!useErrorHandle) return;
const errInfo: Partial<ErrorInfo> = { const errInfo: Partial<ErrorInfo> = {
......
...@@ -15,13 +15,11 @@ import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum'; ...@@ -15,13 +15,11 @@ import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import router, { resetRouter } from '/@/router'; import router from '/@/router';
import { permissionStore } from './permission';
import { tabStore } from './tab';
import { loginApi, getUserInfoById } from '/@/api/sys/user'; import { loginApi, getUserInfoById } from '/@/api/sys/user';
import { setLocal, getLocal, clearSession, clearLocal } from '/@/utils/helper/persistent'; import { setLocal, getLocal } from '/@/utils/helper/persistent';
// import { FULL_PAGE_NOT_FOUND_ROUTE } from '/@/router/constant'; // import { FULL_PAGE_NOT_FOUND_ROUTE } from '/@/router/constant';
export type UserInfo = Omit<GetUserInfoByUserIdModel, 'roles'>; export type UserInfo = Omit<GetUserInfoByUserIdModel, 'roles'>;
...@@ -52,7 +50,7 @@ class User extends VuexModule { ...@@ -52,7 +50,7 @@ class User extends VuexModule {
} }
@Mutation @Mutation
resetState(): void { commitResetState(): void {
this.userInfoState = null; this.userInfoState = null;
this.tokenState = ''; this.tokenState = '';
this.roleListState = []; this.roleListState = [];
...@@ -128,16 +126,6 @@ class User extends VuexModule { ...@@ -128,16 +126,6 @@ class User extends VuexModule {
goLogin && router.push(PageEnum.BASE_LOGIN); goLogin && router.push(PageEnum.BASE_LOGIN);
} }
@Action
async resumeAllState() {
resetRouter();
clearSession();
clearLocal();
permissionStore.commitResetState();
tabStore.commitResetState();
this.resetState();
}
/** /**
* @description: Confirm before logging out * @description: Confirm before logging out
*/ */
......
...@@ -2,13 +2,7 @@ ...@@ -2,13 +2,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'; import type { LocaleType } from '/@/locales/types';
export interface MessageSetting {
title: string;
// 取消按钮的文字,
cancelText: string;
// 确认按钮的文字
okText: string;
}
export interface MenuSetting { export interface MenuSetting {
collapsed: boolean; collapsed: boolean;
collapsedShowTitle: boolean; collapsedShowTitle: boolean;
...@@ -54,8 +48,18 @@ export interface HeaderSetting { ...@@ -54,8 +48,18 @@ export interface HeaderSetting {
// 显示消息中心按钮 // 显示消息中心按钮
showNotice: boolean; showNotice: boolean;
} }
export interface LocaleSetting {
// Current language
lang: LocaleType;
// default language
fallback: LocaleType;
// available Locales
availableLocales: LocaleType[];
}
export interface ProjectConfig { export interface ProjectConfig {
locale: LocaleType; locale: LocaleSetting;
// header背景色 // header背景色
headerBgColor: string; headerBgColor: string;
// 左侧菜单背景色 // 左侧菜单背景色
...@@ -81,8 +85,6 @@ export interface ProjectConfig { ...@@ -81,8 +85,6 @@ export interface ProjectConfig {
// menuType: MenuTypeEnum; // menuType: MenuTypeEnum;
menuSetting: MenuSetting; menuSetting: MenuSetting;
messageSetting: MessageSetting;
// 多标签页设置 // 多标签页设置
multiTabsSetting: MultiTabsSetting; multiTabsSetting: MultiTabsSetting;
// pageLayout是否开启keep-alive // pageLayout是否开启keep-alive
...@@ -133,12 +135,6 @@ export interface GlobEnvConfig { ...@@ -133,12 +135,6 @@ export interface GlobEnvConfig {
VITE_GLOB_APP_SHORT_NAME: string; VITE_GLOB_APP_SHORT_NAME: string;
} }
// 修改配置
export type SetProjectSettingFn = <T extends keyof ProjectConfig>(
key: T,
value: ProjectConfig[T]
) => void;
interface GlobWrap { interface GlobWrap {
globSetting: Readonly<GlobConfig>; globSetting: Readonly<GlobConfig>;
} }
...@@ -146,5 +142,3 @@ interface GlobWrap { ...@@ -146,5 +142,3 @@ interface GlobWrap {
interface ProjectSettingWrap { interface ProjectSettingWrap {
projectSetting: Readonly<ProjectConfig>; projectSetting: Readonly<ProjectConfig>;
} }
export type SettingWrap = GlobWrap & ProjectSettingWrap;
import { getEnv } from '/@/utils/env'; import { getEnv } from '/@/utils/env';
import { useSetting } from '/@/hooks/core/useSetting'; import { useGlobSetting } from '/@/settings/use';
import pkg from '../../../package.json'; import pkg from '../../../package.json';
const { globSetting } = useSetting(); const globSetting = useGlobSetting();
// Generate cache key according to version // Generate cache key according to version
export const getStorageShortName = () => { export const getStorageShortName = () => {
......
...@@ -10,7 +10,7 @@ import { AxiosTransform } from './axiosTransform'; ...@@ -10,7 +10,7 @@ import { AxiosTransform } from './axiosTransform';
import { checkStatus } from './checkStatus'; import { checkStatus } from './checkStatus';
import { useSetting } from '/@/hooks/core/useSetting'; import { useGlobSetting } from '/@/settings/use';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum'; import { RequestEnum, ResultEnum, ContentTypeEnum } from '/@/enums/httpEnum';
...@@ -21,7 +21,7 @@ import { setObjToUrlParams, deepMerge } from '/@/utils'; ...@@ -21,7 +21,7 @@ import { setObjToUrlParams, deepMerge } from '/@/utils';
import { errorStore } from '/@/store/modules/error'; import { errorStore } from '/@/store/modules/error';
import { errorResult } from './const'; import { errorResult } from './const';
const { globSetting } = useSetting(); const globSetting = useGlobSetting();
const prefix = globSetting.urlPrefix; const prefix = globSetting.urlPrefix;
const { createMessage, createErrorModal } = useMessage(); const { createMessage, createErrorModal } = useMessage();
......
<template>
<div class="p-4">
<Alert message="国际化方式,没有进行全局国际化,有需要可以自行处理。" type="info" />
<Divider />
国际化信息: {{ t('hello') }}
<Divider />
<a-button :type="localeRef === 'zhCN' ? 'primary' : 'default'" @click="localeRef = 'zhCN'">
中文
</a-button>
<a-button :type="localeRef === 'en' ? 'primary' : 'default'" @click="localeRef = 'en'">
英文
</a-button>
<Divider />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Alert, Divider } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
export default defineComponent({
components: { Alert, Divider },
setup() {
const { t, localeRef } = useI18n({
locale: 'zhCN',
messages: {
en: {
hello: 'hello',
},
zhCN: {
hello: '你好',
},
},
});
return { localeRef, t };
},
});
</script>
<template> <template>
<BasicModal :width="800" title="错误详情" v-bind="$attrs"> <BasicModal :width="800" :title="t('sys.errorLog.tableActionDesc')" v-bind="$attrs">
<Description :data="info" @register="register" /> <Description :data="info" @register="register" />
</BasicModal> </BasicModal>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType } from 'vue'; import { defineComponent, PropType } from 'vue';
import { useI18n } from 'vue-i18n';
import { BasicModal } from '/@/components/Modal/index'; import { BasicModal } from '/@/components/Modal/index';
import { ErrorInfo } from '/@/store/modules/error'; import { ErrorInfo } from '/@/store/modules/error';
import { Description, useDescription } from '/@/components/Description/index'; import { Description, useDescription } from '/@/components/Description/index';
import { getDescSchema } from './data'; import { getDescSchema } from './data';
export default defineComponent({ export default defineComponent({
...@@ -20,12 +23,15 @@ ...@@ -20,12 +23,15 @@
}, },
}, },
setup() { setup() {
const { t } = useI18n();
const [register] = useDescription({ const [register] = useDescription({
column: 2, column: 2,
schema: getDescSchema(), schema: getDescSchema(),
}); });
return { return {
register, register,
useI18n,
t,
}; };
}, },
}); });
......
...@@ -2,11 +2,15 @@ import { Tag } from 'ant-design-vue'; ...@@ -2,11 +2,15 @@ import { Tag } from 'ant-design-vue';
import { BasicColumn } from '/@/components/Table/index'; import { BasicColumn } from '/@/components/Table/index';
import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
import { useExternalI18n } from '/@/hooks/web/useLocale';
const { t } = useExternalI18n();
export function getColumns(): BasicColumn[] { export function getColumns(): BasicColumn[] {
return [ return [
{ {
dataIndex: 'type', dataIndex: 'type',
title: '类型', title: t('sys.errorLog.tableColumnType'),
width: 80, width: 80,
customRender: ({ text }) => { customRender: ({ text }) => {
const color = const color =
...@@ -24,17 +28,17 @@ export function getColumns(): BasicColumn[] { ...@@ -24,17 +28,17 @@ export function getColumns(): BasicColumn[] {
}, },
{ {
dataIndex: 'url', dataIndex: 'url',
title: '地址', title: 'URL',
width: 200, width: 200,
}, },
{ {
dataIndex: 'time', dataIndex: 'time',
title: '时间', title: t('sys.errorLog.tableColumnDate'),
width: 160, width: 160,
}, },
{ {
dataIndex: 'file', dataIndex: 'file',
title: '文件', title: t('sys.errorLog.tableColumnFile'),
width: 200, width: 200,
}, },
{ {
...@@ -44,12 +48,12 @@ export function getColumns(): BasicColumn[] { ...@@ -44,12 +48,12 @@ export function getColumns(): BasicColumn[] {
}, },
{ {
dataIndex: 'message', dataIndex: 'message',
title: '错误信息', title: t('sys.errorLog.tableColumnMsg'),
width: 300, width: 300,
}, },
{ {
dataIndex: 'stack', dataIndex: 'stack',
title: 'stack信息', title: t('sys.errorLog.tableColumnStackMsg'),
width: 300, width: 300,
}, },
]; ];
......
...@@ -6,12 +6,22 @@ ...@@ -6,12 +6,22 @@
<DetailModal :info="rowInfoRef" @register="registerModal" /> <DetailModal :info="rowInfoRef" @register="registerModal" />
<BasicTable @register="register" class="error-handle-table"> <BasicTable @register="register" class="error-handle-table">
<template #toolbar> <template #toolbar>
<a-button @click="fireVueError" type="primary"> 点击触发vue错误 </a-button> <a-button @click="fireVueError" type="primary">
<a-button @click="fireResourceError" type="primary"> 点击触发resource错误 </a-button> {{ t('sys.errorLog.fireVueError') }}
<a-button @click="fireAjaxError" type="primary"> 点击触发ajax错误 </a-button> </a-button>
<a-button @click="fireResourceError" type="primary">
{{ t('sys.errorLog.fireResourceError') }}
</a-button>
<a-button @click="fireAjaxError" type="primary">
{{ t('sys.errorLog.fireAjaxError') }}
</a-button>
</template> </template>
<template #action="{ record }"> <template #action="{ record }">
<TableAction :actions="[{ label: '详情', onClick: handleDetail.bind(null, record) }]" /> <TableAction
:actions="[
{ label: t('sys.errorLog.tableActionDesc'), onClick: handleDetail.bind(null, record) },
]"
/>
</template> </template>
</BasicTable> </BasicTable>
</div> </div>
...@@ -21,10 +31,11 @@ ...@@ -21,10 +31,11 @@
import { defineComponent, watch, ref, nextTick } from 'vue'; import { defineComponent, watch, ref, nextTick } from 'vue';
import DetailModal from './DetailModal.vue'; import DetailModal from './DetailModal.vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table/index';
import { useModal } from '/@/components/Modal/index'; import { useModal } from '/@/components/Modal/index';
import { useMessage } from '/@/hooks/web/useMessage'; import { useMessage } from '/@/hooks/web/useMessage';
import { useI18n } from 'vue-i18n';
import { BasicTable, useTable, TableAction } from '/@/components/Table/index';
import { errorStore, ErrorInfo } from '/@/store/modules/error'; import { errorStore, ErrorInfo } from '/@/store/modules/error';
...@@ -42,12 +53,14 @@ ...@@ -42,12 +53,14 @@
const rowInfoRef = ref<ErrorInfo>(); const rowInfoRef = ref<ErrorInfo>();
const imgListRef = ref<string[]>([]); const imgListRef = ref<string[]>([]);
const { t } = useI18n();
const [register, { setTableData }] = useTable({ const [register, { setTableData }] = useTable({
title: '错误日志列表', title: t('sys.errorLog.tableTitle'),
columns: getColumns(), columns: getColumns(),
actionColumn: { actionColumn: {
width: 80, width: 80,
title: '操作', title: 'Action',
dataIndex: 'action', dataIndex: 'action',
slots: { customRender: 'action' }, slots: { customRender: 'action' },
}, },
...@@ -67,7 +80,7 @@ ...@@ -67,7 +80,7 @@
); );
const { createMessage } = useMessage(); const { createMessage } = useMessage();
if (isDevMode()) { if (isDevMode()) {
createMessage.info('只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效!'); createMessage.info(t('sys.errorLog.enableMessage'));
} }
// 查看详情 // 查看详情
function handleDetail(row: ErrorInfo) { function handleDetail(row: ErrorInfo) {
...@@ -96,6 +109,7 @@ ...@@ -96,6 +109,7 @@
fireAjaxError, fireAjaxError,
imgListRef, imgListRef,
rowInfoRef, rowInfoRef,
t,
}; };
}, },
}); });
......
...@@ -12,6 +12,7 @@ import { useRoute } from 'vue-router'; ...@@ -12,6 +12,7 @@ import { useRoute } from 'vue-router';
import { useGo, useRedo } from '/@/hooks/web/usePage'; import { useGo, useRedo } from '/@/hooks/web/usePage';
import { PageEnum } from '/@/enums/pageEnum'; import { PageEnum } from '/@/enums/pageEnum';
import { useI18n } from 'vue-i18n';
import './exception.less'; import './exception.less';
interface MapValue { interface MapValue {
...@@ -47,9 +48,12 @@ export default defineComponent({ ...@@ -47,9 +48,12 @@ export default defineComponent({
}, },
setup(props) { setup(props) {
const statusMapRef = ref(new Map<string | number, MapValue>()); const statusMapRef = ref(new Map<string | number, MapValue>());
const { query } = useRoute(); const { query } = useRoute();
const go = useGo(); const go = useGo();
const redo = useRedo(); const redo = useRedo();
const { t } = useI18n();
const getStatus = computed(() => { const getStatus = computed(() => {
const { status: routeStatus } = query; const { status: routeStatus } = query;
const { status } = props; const { status } = props;
...@@ -62,41 +66,44 @@ export default defineComponent({ ...@@ -62,41 +66,44 @@ export default defineComponent({
} }
); );
const backLoginI18n = t('sys.exception.backLogin');
const backHomeI18n = t('sys.exception.backHome');
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, { unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_ACCESS, {
title: '403', title: '403',
status: `${ExceptionEnum.PAGE_NOT_ACCESS}`, status: `${ExceptionEnum.PAGE_NOT_ACCESS}`,
subTitle: `Sorry, you don't have access to this page.!`, subTitle: t('sys.exception.subTitle403'),
btnText: props.full ? 'Back Login' : 'Back Home', btnText: props.full ? backLoginI18n : backHomeI18n,
handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
}); });
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, { unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_FOUND, {
title: '404', title: '404',
status: `${ExceptionEnum.PAGE_NOT_FOUND}`, status: `${ExceptionEnum.PAGE_NOT_FOUND}`,
subTitle: `Sorry, the page you visited does not exist.`, subTitle: t('sys.exception.subTitle404'),
btnText: props.full ? 'Back Login' : 'Back Home', btnText: props.full ? backLoginI18n : backHomeI18n,
handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()), handler: () => (props.full ? go(PageEnum.BASE_LOGIN) : go()),
}); });
unref(statusMapRef).set(ExceptionEnum.ERROR, { unref(statusMapRef).set(ExceptionEnum.ERROR, {
title: '500', title: '500',
status: `${ExceptionEnum.ERROR}`, status: `${ExceptionEnum.ERROR}`,
subTitle: `Sorry, the server is reporting an error.`, subTitle: t('sys.exception.subTitle500'),
btnText: 'Back Home', btnText: backHomeI18n,
handler: () => go(), handler: () => go(),
}); });
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, { unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, {
title: 'No data on the current page', title: t('sys.exception.noDataTitle'),
subTitle: '', subTitle: '',
btnText: 'Refresh', btnText: t('sys.exception.redo'),
handler: () => redo(), handler: () => redo(),
icon: notDataImg, icon: notDataImg,
}); });
unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, { unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, {
title: 'Network Error', title: t('sys.exception.networkErrorTitle'),
subTitle: 'Sorry,Your network connection has been disconnected, please check your network!', subTitle: t('sys.exception.networkErrorSubTitle'),
btnText: 'Refresh', btnText: 'Refresh',
handler: () => redo(), handler: () => redo(),
icon: netWorkImg, icon: netWorkImg,
......
...@@ -6,36 +6,38 @@ ...@@ -6,36 +6,38 @@
<p class="lock-page__header-name">{{ realName }}</p> <p class="lock-page__header-name">{{ realName }}</p>
</div> </div>
<BasicForm @register="register" v-if="!getIsNotPwd" /> <BasicForm @register="register" v-if="!getIsNotPwd" />
<Alert v-if="errMsgRef" type="error" message="锁屏密码错误" banner /> <Alert v-if="errMsgRef" type="error" :message="t('sys.lock.alert')" banner />
<div class="lock-page__footer"> <div class="lock-page__footer">
<a-button type="default" class="mt-2 mr-2" @click="goLogin" v-if="!getIsNotPwd"> <a-button type="default" class="mt-2 mr-2" @click="goLogin" v-if="!getIsNotPwd">
返回登录 {{ t('sys.lock.backToLogin') }}
</a-button> </a-button>
<a-button type="primary" class="mt-2" @click="unLock(!getIsNotPwd)" :loading="loadingRef"> <a-button type="primary" class="mt-2" @click="unLock(!getIsNotPwd)" :loading="loadingRef">
进入系统 {{ t('sys.lock.entry') }}
</a-button> </a-button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
// 组件相关
import { defineComponent, ref, computed } from 'vue'; import { defineComponent, ref, computed } from 'vue';
import { Alert } from 'ant-design-vue'; import { Alert } from 'ant-design-vue';
// hook
import { BasicForm, useForm } from '/@/components/Form'; import { BasicForm, useForm } from '/@/components/Form';
import { userStore } from '/@/store/modules/user'; import { userStore } from '/@/store/modules/user';
import { appStore } from '/@/store/modules/app'; import { appStore } from '/@/store/modules/app';
import { useI18n } from 'vue-i18n';
export default defineComponent({ export default defineComponent({
name: 'LockPage', name: 'LockPage',
components: { Alert, BasicForm }, components: { Alert, BasicForm },
setup() { setup() {
// 获取配置文件
// 样式前缀
const loadingRef = ref(false); const loadingRef = ref(false);
const errMsgRef = ref(false); const errMsgRef = ref(false);
const { t } = useI18n();
const [register, { validateFields }] = useForm({ const [register, { validateFields }] = useForm({
showActionButtonGroup: false, showActionButtonGroup: false,
schemas: [ schemas: [
...@@ -45,7 +47,7 @@ ...@@ -45,7 +47,7 @@
component: 'InputPassword', component: 'InputPassword',
componentProps: { componentProps: {
style: { width: '100%' }, style: { width: '100%' },
placeholder: '请输入锁屏密码或者用户密码', placeholder: t('sys.lock.placeholder'),
}, },
rules: [{ required: true }], rules: [{ required: true }],
}, },
...@@ -55,6 +57,14 @@ ...@@ -55,6 +57,14 @@
const { realName } = userStore.getUserInfoState || {}; const { realName } = userStore.getUserInfoState || {};
return realName; return realName;
}); });
const getIsNotPwd = computed(() => {
if (!appStore.getLockInfo) {
return true;
}
return appStore.getLockInfo.pwd === undefined;
});
/** /**
* @description: unLock * @description: unLock
*/ */
...@@ -76,17 +86,12 @@ ...@@ -76,17 +86,12 @@
loadingRef.value = false; loadingRef.value = false;
} }
} }
function goLogin() { function goLogin() {
userStore.loginOut(true); userStore.loginOut(true);
appStore.resetLockInfo(); appStore.resetLockInfo();
} }
const getIsNotPwd = computed(() => {
if (!appStore.getLockInfo) {
return true;
}
return appStore.getLockInfo.pwd === undefined;
});
// 账号密码登录
return { return {
register, register,
getIsNotPwd, getIsNotPwd,
...@@ -95,6 +100,7 @@ ...@@ -95,6 +100,7 @@
unLock, unLock,
errMsgRef, errMsgRef,
loadingRef, loadingRef,
t,
}; };
}, },
}); });
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
<div class="login-form-wrap"> <div class="login-form-wrap">
<div class="login-form mx-6"> <div class="login-form mx-6">
<div class="login-form__content px-2 py-10"> <div class="login-form__content px-2 py-10">
<AppLocalPicker class="login-form__locale" />
<header> <header>
<img :src="logo" class="mr-4" /> <img :src="logo" class="mr-4" />
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
...@@ -29,13 +30,15 @@ ...@@ -29,13 +30,15 @@
<a-col :span="12"> <a-col :span="12">
<a-form-item> <a-form-item>
<!-- No logic, you need to deal with it yourself --> <!-- 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">{{
t('sys.login.autoLogin')
}}</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 --> <!-- No logic, you need to deal with it yourself -->
<a-button type="link" size="small">忘记密码</a-button> <a-button type="link" size="small">{{ t('sys.login.forgetPassword') }}</a-button>
</a-form-item> </a-form-item>
</a-col> </a-col>
</a-row> </a-row>
...@@ -47,7 +50,7 @@ ...@@ -47,7 +50,7 @@
:block="true" :block="true"
@click="login" @click="login"
:loading="formState.loading" :loading="formState.loading"
>{{ t('system.login.button') }}</a-button >{{ t('sys.login.loginButton') }}</a-button
> >
</a-form-item> </a-form-item>
</a-form> </a-form>
...@@ -61,6 +64,7 @@ ...@@ -61,6 +64,7 @@
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 { AppLocalPicker } from '/@/components/Application';
// 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';
...@@ -68,7 +72,7 @@ ...@@ -68,7 +72,7 @@
// 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 { useGlobSetting } from '/@/settings/use';
import logo from '/@/assets/images/logo.png'; import logo from '/@/assets/images/logo.png';
export default defineComponent({ export default defineComponent({
...@@ -76,14 +80,16 @@ ...@@ -76,14 +80,16 @@
// BasicDragVerify, // BasicDragVerify,
AButton: Button, AButton: Button,
ACheckbox: Checkbox, ACheckbox: Checkbox,
AppLocalPicker,
}, },
setup() { setup() {
const formRef = ref<any>(null); const formRef = ref<any>(null);
const autoLoginRef = ref(false); const autoLoginRef = ref(false);
// const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null); // const verifyRef = ref<RefInstanceType<DragVerifyActionType>>(null);
const { globSetting } = useSetting(); const globSetting = useGlobSetting();
const { notification } = useMessage(); const { notification } = useMessage();
const { t } = useI18n();
// const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify); // const openLoginVerifyRef = computed(() => appStore.getProjectConfig.openLoginVerify);
...@@ -97,8 +103,10 @@ ...@@ -97,8 +103,10 @@
}); });
const formRules = reactive({ const formRules = reactive({
account: [{ required: true, message: '请输入账号', trigger: 'blur' }], account: [{ required: true, message: t('sys.login.accountPlaceholder'), trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }], password: [
{ required: true, message: t('sys.login.passwordPlaceholder'), trigger: 'blur' },
],
// verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [], // verify: unref(openLoginVerifyRef) ? [{ required: true, message: '请通过验证码校验' }] : [],
}); });
...@@ -123,8 +131,8 @@ ...@@ -123,8 +131,8 @@
); );
if (userInfo) { if (userInfo) {
notification.success({ notification.success({
message: '登录成功', message: t('sys.login.loginSuccessTitle'),
description: `欢迎回来: ${userInfo.realName}`, description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`,
duration: 3, duration: 3,
}); });
} }
...@@ -134,7 +142,6 @@ ...@@ -134,7 +142,6 @@
formState.loading = false; formState.loading = false;
} }
} }
const { t } = useI18n();
return { return {
formRef, formRef,
// verifyRef, // verifyRef,
...@@ -195,7 +202,14 @@ ...@@ -195,7 +202,14 @@
.respond-to(xlarge, { width: 540px; right:0}); .respond-to(xlarge, { width: 540px; right:0});
} }
&__locale {
position: absolute;
top: 10px;
right: 10px;
}
&__content { &__content {
position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
border: 1px solid #999; border: 1px solid #999;
......
...@@ -126,7 +126,12 @@ const viteConfig: UserConfig = { ...@@ -126,7 +126,12 @@ const viteConfig: UserConfig = {
}, },
// The package will be recompiled using rollup, and the new package compiled into the esm module specification will be put into node_modules/.vite_opt_cache // The package will be recompiled using rollup, and the new package compiled into the esm module specification will be put into node_modules/.vite_opt_cache
optimizeDeps: { optimizeDeps: {
include: ['echarts/map/js/china', 'ant-design-vue/es/locale/zh_CN', '@ant-design/icons-vue'], include: [
'echarts/map/js/china',
'ant-design-vue/es/locale/zh_CN',
'ant-design-vue/es/locale/en_US',
'@ant-design/icons-vue',
],
}, },
// Local cross-domain proxy // Local cross-domain proxy
......
...@@ -2926,6 +2926,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: ...@@ -2926,6 +2926,11 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2:
shebang-command "^2.0.0" shebang-command "^2.0.0"
which "^2.0.1" which "^2.0.1"
crypto-es@^1.2.6:
version "1.2.6"
resolved "https://registry.npmjs.org/crypto-es/-/crypto-es-1.2.6.tgz#468f3573a5d7b82e3b63b0004f55f905a6d3b12c"
integrity sha512-PQnrovdr5ibmOxqAh/Vy+A30RokHom7kb9Z61EPwfASfbcJCrCG4+vNNegmebNVHiXvS7WjYpHDePxnE/biEbA==
crypto-random-string@^1.0.0: crypto-random-string@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册