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