提交 5810078b 作者: vben

wip: code adjustment and optimization

上级 07c18d60
......@@ -9,14 +9,13 @@
<script lang="ts">
import { defineComponent } from 'vue';
import { ConfigProvider } from 'ant-design-vue';
import { AppProvider } from '/@/components/Application';
import { initAppConfigStore } from '/@/logics/initAppConfig';
import { useLockPage } from '/@/hooks/web/useLockPage';
import { useLocale } from '/@/locales/useLocale';
import { AppProvider } from '/@/components/Application';
export default defineComponent({
name: 'App',
components: { ConfigProvider, AppProvider },
......
......@@ -7,7 +7,6 @@
import { defineComponent, reactive, computed, watch, onMounted, unref, toRef } from 'vue';
import { countToProps } from './props';
import { isNumber } from '/@/utils/is';
import { requestAnimationFrame, cancelAnimationFrame } from '/@/utils/animation';
export default defineComponent({
name: 'CountTo',
props: countToProps,
......
import { isFunction, isUnDef } from '/@/utils/is';
import { ref, unref } from 'vue';
import { requestAnimationFrame } from '/@/utils/animation';
export interface ScrollToParams {
el: HTMLElement;
......
import type { AppRouteRecordRaw } from '/@/router/types';
import ParentLayout from '/@/layouts/page/ParentView.vue';
const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception');
const EXCEPTION_COMPONENT = () => import('../views/sys/exception/Exception.vue');
/**
* @description: default layout
......
......@@ -2,7 +2,7 @@ import type { Router } from 'vue-router';
import { useGlobSetting } from '/@/hooks/setting';
import { setTitle } from '/@/utils/browser';
import { setTitle } from '/@/utils';
import { useI18n } from '/@/hooks/web/useI18n';
import { REDIRECT_NAME } from '/@/router/constant';
......
......@@ -4,7 +4,7 @@ import { getParentLayout, LAYOUT } from '/@/router/constant';
import { ExceptionEnum } from '/@/enums/exceptionEnum';
import { t } from '/@/hooks/web/useI18n';
const ExceptionPage = () => import('/@/views/sys/exception/Exception');
const ExceptionPage = () => import('/@/views/sys/exception/Exception.vue');
const page: AppRouteModule = {
path: '/page-demo',
......
import { isServer } from '/@/utils/is';
let lastTime = 0;
const prefixes = 'webkit moz ms o'.split(' ');
let requestAnimationFrame: typeof window.requestAnimationFrame;
let cancelAnimationFrame: typeof window.cancelAnimationFrame;
(() => {
const NO_LOOP: any = () => {};
const getWindowFrame = (name: string) => {
return name as any;
};
if (isServer) {
requestAnimationFrame = cancelAnimationFrame = NO_LOOP;
} else {
requestAnimationFrame = window.requestAnimationFrame;
cancelAnimationFrame = window.cancelAnimationFrame;
let prefix;
for (let i = 0; i < prefixes.length; i++) {
if (requestAnimationFrame && cancelAnimationFrame) {
break;
}
prefix = prefixes[i];
requestAnimationFrame =
requestAnimationFrame || window[getWindowFrame(prefix + 'RequestAnimationFrame')];
cancelAnimationFrame =
cancelAnimationFrame ||
window[getWindowFrame(prefix + 'CancelAnimationFrame')] ||
window[getWindowFrame(prefix + 'CancelRequestAnimationFrame')];
}
// If the current browser does not support requestAnimationFrame and cancelAnimationFrame, it will fall back to setTimeout
if (!requestAnimationFrame || !cancelAnimationFrame) {
requestAnimationFrame = function (callback: Fn) {
const currTime = new Date().getTime();
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
const id = window.setTimeout(() => {
/* eslint-disable-next-line */
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
cancelAnimationFrame = function (id: number) {
window.clearTimeout(id);
};
}
}
})();
export { requestAnimationFrame, cancelAnimationFrame };
function getTypeVersion() {
const userAgent = navigator.userAgent.toLowerCase();
const browserTypes = {
IE: /(?:msie|trident.*rv).([\d.]+)/,
Edge: /edge.([\d.]+)/,
Chrome: /chrome.([\d.]+)/,
Firefox: /firefox.([\d.]+)/,
Opera: /opera.([\d.]+)/,
Safari: /(?:safari|version).([\d.]+)/,
};
type BrowserKeys = keyof typeof browserTypes;
/** browser type */
let type!: BrowserKeys | null;
/** browser version */
let version!: string | null;
for (type in browserTypes) {
if ((version = browserTypes[type as BrowserKeys].exec(userAgent) as any)) {
version = version[1];
break;
}
}
if (version) {
if (type === 'IE') {
try {
document.execCommand('BackgroundImageCache', false, true as any);
} catch (error) {
console.log(error);
}
}
} else {
type = version = null;
}
return { type, version };
}
const { type, version } = getTypeVersion();
export function getType() {
return type;
}
export function getVersion() {
return version;
}
export function isIeFn() {
return type === 'IE';
}
export function isChromeFn() {
return type === 'Chrome';
}
export function isEdgeFn() {
return type === 'Edge';
}
export function isSafariFn() {
return type === 'Safari';
}
export function isFirefoxFn() {
return type === 'Firefox';
}
export function isOperaFn() {
return type === 'Opera';
}
/**
* set page Title
* @param {*} title :page Title
*/
function setDocumentTitle(title: string) {
document.title = title;
const ua = navigator.userAgent;
const regex = /\bMicroMessenger\/([\d.]+)/;
// 兼容
if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) {
const i = document.createElement('iframe');
i.src = '/favicon.ico';
i.style.display = 'none';
i.onload = function () {
setTimeout(function () {
i.remove();
}, 9);
};
document.body.appendChild(i);
}
}
export function setTitle(title: string, appTitle?: string) {
if (title) {
const _title = title ? ` ${title}-${appTitle} ` : `${appTitle}`;
setDocumentTitle(_title);
}
}
......@@ -6,7 +6,7 @@
* @return Boolean
*/
export function isHexColor(color: string) {
const reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/;
const reg = /^#([0-9a-fA-F]{3}|[0-9a-fA-f]{6})$/;
return reg.test(color);
}
......@@ -40,7 +40,7 @@ export function hexToRGB(hex: string) {
}
sHex = sColorNew;
}
const sColorChange = [];
const sColorChange: number[] = [];
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt('0x' + sHex.slice(i, i + 2)));
}
......
......@@ -14,27 +14,4 @@ export function formatToDate(date: moment.MomentInput = null, format = DATE_FORM
return moment(date).format(format);
}
export function formatAgo(str: string | number) {
if (!str) return '';
const date = new Date(Number(str));
const time = new Date().getTime() - date.getTime(); // 现在的时间-传入的时间 = 相差的时间(单位 = 毫秒)
if (time < 0) {
return '';
} else if (time / 1000 < 30) {
return '刚刚';
} else if (time / 1000 < 60) {
return parseInt(String(time / 1000)) + '秒前';
} else if (time / 60000 < 60) {
return parseInt(String(time / 60000)) + '分钟前';
} else if (time / 3600000 < 24) {
return parseInt(String(time / 3600000)) + '小时前';
} else if (time / 86400000 < 31) {
return parseInt(String(time / 86400000)) + '天前';
} else if (time / 2592000000 < 12) {
return parseInt(String(time / 2592000000)) + '月前';
} else {
return parseInt(String(time / 31536000000)) + '年前';
}
}
export const dateUtil = moment;
import { createStorage } from '/@/utils/cache';
import { isIeFn } from '/@/utils/browser';
import { BASE_LOCAL_CACHE_KEY, BASE_SESSION_CACHE_KEY } from '/@/enums/cacheEnum';
......@@ -127,9 +126,5 @@ export function persistentCache() {
}
}
if (isIeFn() && (document as any).attachEvent) {
(document as any).attachEvent('onstorage', storageChange);
} else {
window.addEventListener('storage', storageChange);
}
window.addEventListener('storage', storageChange);
})();
......@@ -81,7 +81,9 @@ export class VAxios {
// 请求拦截器配置处理
this.axiosInstance.interceptors.request.use((config: AxiosRequestConfig) => {
// If cancel repeat request is turned on, then cancel repeat request is prohibited
const { headers: { ignoreCancelToken } = { ignoreCancelToken: false } } = config;
const {
headers: { ignoreCancelToken = false },
} = config;
!ignoreCancelToken && axiosCanceler.addPending(config);
if (requestInterceptors && isFunction(requestInterceptors)) {
config = requestInterceptors(config);
......
......@@ -99,3 +99,32 @@ export function getLastItem<T extends any>(list: T) {
return Array.from(list.values()).slice(-1)[0];
}
}
/**
* set page Title
* @param {*} title :page Title
*/
function setDocumentTitle(title: string) {
document.title = title;
const ua = navigator.userAgent;
const regex = /\bMicroMessenger\/([\d.]+)/;
// 兼容
if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) {
const i = document.createElement('iframe');
i.src = '/favicon.ico';
i.style.display = 'none';
i.onload = function () {
setTimeout(function () {
i.remove();
}, 9);
};
document.body.appendChild(i);
}
}
export function setTitle(title: string, appTitle?: string) {
if (title) {
const _title = title ? ` ${title}-${appTitle} ` : `${appTitle}`;
setDocumentTitle(_title);
}
}
......@@ -86,21 +86,7 @@ export function isElement(val: unknown): val is Element {
export const isServer = typeof window === 'undefined';
export const isClient = typeof window !== 'undefined';
export function isImageDom(o: Element) {
return o && ['IMAGE', 'IMG'].includes(o.tagName);
}
export function isTextarea(element: Element | null): element is HTMLTextAreaElement {
return element !== null && element.tagName.toLowerCase() === 'textarea';
}
export function isMobile(): boolean {
return !!navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
);
}
export const isClient = !isServer;
export function isUrl(path: string): boolean {
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
......
......@@ -4,13 +4,16 @@
</BasicModal>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import { BasicModal } from '/@/components/Modal/index';
import { ErrorInfo } from '/@/store/modules/error';
import { Description, useDescription } from '/@/components/Description/index';
import { useI18n } from '/@/hooks/web/useI18n';
import { ErrorInfo } from '/@/store/modules/error';
import { getDescSchema } from './data';
export default defineComponent({
......@@ -24,10 +27,12 @@
},
setup() {
const { t } = useI18n();
const [register] = useDescription({
column: 2,
schema: getDescSchema(),
});
return {
register,
useI18n,
......
......@@ -53,7 +53,6 @@ export function getColumns(): BasicColumn[] {
{
dataIndex: 'stack',
title: t('sys.errorLog.tableColumnStackMsg'),
width: 300,
},
];
}
......
<template>
<div class="p-4">
<template v-for="src in imgListRef" :key="src">
<template v-for="src in imgList" :key="src">
<img :src="src" v-show="false" />
</template>
<DetailModal :info="rowInfoRef" @register="registerModal" />
<DetailModal :info="rowInfo" @register="registerModal" />
<BasicTable @register="register" class="error-handle-table">
<template #toolbar>
<a-button @click="fireVueError" type="primary">
......@@ -50,8 +50,8 @@
name: 'ErrorHandler',
components: { DetailModal, BasicTable, TableAction },
setup() {
const rowInfoRef = ref<ErrorInfo>();
const imgListRef = ref<string[]>([]);
const rowInfo = ref<ErrorInfo>();
const imgList = ref<string[]>([]);
const { t } = useI18n();
......@@ -84,7 +84,7 @@
}
// 查看详情
function handleDetail(row: ErrorInfo) {
rowInfoRef.value = row;
rowInfo.value = row;
openModal(true);
}
......@@ -93,7 +93,7 @@
}
function fireResourceError() {
imgListRef.value.push(`${new Date().getTime()}.png`);
imgList.value.push(`${new Date().getTime()}.png`);
}
async function fireAjaxError() {
......@@ -107,8 +107,8 @@
fireVueError,
fireResourceError,
fireAjaxError,
imgListRef,
rowInfoRef,
imgList,
rowInfo,
t,
};
},
......
import './exception.less';
import type { PropType } from 'vue';
import { Result, Button } from 'ant-design-vue';
import { defineComponent, ref, computed, unref } from 'vue';
import { ExceptionEnum } from '/@/enums/exceptionEnum';
import netWorkImg from '/@/assets/images/exception/net-work.png';
import notDataImg from '/@/assets/images/no-data.png';
import { useRoute } from 'vue-router';
import { useGo, useRedo } from '/@/hooks/web/usePage';
import { PageEnum } from '/@/enums/pageEnum';
import { useI18n } from '/@/hooks/web/useI18n';
interface MapValue {
title: string;
subTitle: string;
btnText?: string;
icon?: string;
handler?: Fn;
status?: string;
}
export default defineComponent({
name: 'ErrorPage',
props: {
// 状态码
status: {
type: Number as PropType<number>,
default: ExceptionEnum.PAGE_NOT_FOUND,
},
title: {
type: String as PropType<string>,
default: '',
},
subTitle: {
type: String as PropType<string>,
default: '',
<script lang="tsx">
import type { PropType } from 'vue';
import { Result, Button } from 'ant-design-vue';
import { defineComponent, ref, computed, unref } from 'vue';
import { ExceptionEnum } from '/@/enums/exceptionEnum';
import notDataSvg from '/@/assets/svg/no-data.svg';
import netWorkSvg from '/@/assets/svg/net-error.svg';
import { useRoute } from 'vue-router';
import { useDesign } from '/@/hooks/web/useDesign';
import { useI18n } from '/@/hooks/web/useI18n';
import { useGo, useRedo } from '/@/hooks/web/usePage';
import { PageEnum } from '/@/enums/pageEnum';
interface MapValue {
title: string;
subTitle: string;
btnText?: string;
icon?: string;
handler?: Fn;
status?: string;
}
export default defineComponent({
name: 'ErrorPage',
props: {
// 状态码
status: {
type: Number as PropType<number>,
default: ExceptionEnum.PAGE_NOT_FOUND,
},
title: {
type: String as PropType<string>,
default: '',
},
subTitle: {
type: String as PropType<string>,
default: '',
},
full: {
type: Boolean as PropType<boolean>,
default: false,
},
},
setup(props) {
const statusMapRef = ref(new Map<string | number, MapValue>());
const { query } = useRoute();
const go = useGo();
const redo = useRedo();
const { t } = useI18n();
const { prefixCls } = useDesign('app-exception-page');
const getStatus = computed(() => {
const { status: routeStatus } = query;
const { status } = props;
return Number(routeStatus) || status;
});
const getMapValue = computed(
(): MapValue => {
return unref(statusMapRef).get(unref(getStatus)) as MapValue;
}
);
full: {
type: Boolean as PropType<boolean>,
default: false,
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: 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: 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: t('sys.exception.subTitle500'),
btnText: backHomeI18n,
handler: () => go(),
});
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, {
title: t('sys.exception.noDataTitle'),
subTitle: '',
btnText: t('common.redo'),
handler: () => redo(),
icon: notDataSvg,
});
unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, {
title: t('sys.exception.networkErrorTitle'),
subTitle: t('sys.exception.networkErrorSubTitle'),
btnText: t('common.redo'),
handler: () => redo(),
icon: netWorkSvg,
});
return () => {
const { title, subTitle, btnText, icon, handler, status } = unref(getMapValue) || {};
return (
<Result
class={prefixCls}
status={status as any}
title={props.title || title}
sub-title={props.subTitle || subTitle}
>
{{
extra: () =>
btnText && (
<Button type="primary" onClick={handler}>
{() => btnText}
</Button>
),
icon: () => (icon ? <img src={icon} /> : null),
}}
</Result>
);
};
},
},
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;
return Number(routeStatus) || status;
});
const getMapValue = computed(
(): MapValue => {
return unref(statusMapRef).get(unref(getStatus)) as MapValue;
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-app-exception-page';
.@{prefix-cls} {
display: flex;
align-items: center;
flex-direction: column;
.ant-result-icon {
img {
max-width: 400px;
max-height: 300px;
}
);
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: 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: 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: t('sys.exception.subTitle500'),
btnText: backHomeI18n,
handler: () => go(),
});
unref(statusMapRef).set(ExceptionEnum.PAGE_NOT_DATA, {
title: t('sys.exception.noDataTitle'),
subTitle: '',
btnText: t('common.redo'),
handler: () => redo(),
icon: notDataImg,
});
unref(statusMapRef).set(ExceptionEnum.NET_WORK_ERROR, {
title: t('sys.exception.networkErrorTitle'),
subTitle: t('sys.exception.networkErrorSubTitle'),
btnText: 'Refresh',
handler: () => redo(),
icon: netWorkImg,
});
return () => {
const { title, subTitle, btnText, icon, handler, status } = unref(getMapValue) || {};
return (
<Result
class="exception "
status={status as any}
title={props.title || title}
sub-title={props.subTitle || subTitle}
>
{{
extra: () =>
btnText && (
<Button type="primary" onClick={handler}>
{() => btnText}
</Button>
),
icon: () => (icon ? <img src={icon} /> : null),
}}
</Result>
);
};
},
});
}
}
</style>
.exception {
display: flex;
align-items: center;
flex-direction: column;
}
export { default as Exception } from './Exception';
export { default as Exception } from './Exception.vue';
<template>
<div class="iframe-page" :style="getWrapStyle">
<div :class="prefixCls" :style="getWrapStyle">
<Spin :spinning="loading" size="large" :style="getWrapStyle">
<iframe :src="frameSrc" class="iframe-page__main" ref="frameRef"></iframe>
<iframe :src="frameSrc" :class="`${prefixCls}__main`" ref="frameRef"></iframe>
</Spin>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, ref, unref, onMounted, nextTick, computed } from 'vue';
import type { CSSProperties } from 'vue';
import { defineComponent, ref, unref, onMounted, nextTick, computed } from 'vue';
import { Spin } from 'ant-design-vue';
import { getViewportOffset } from '/@/utils/domUtils';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { propTypes } from '/@/utils/propTypes';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'IFrame',
components: { Spin },
props: {
frameSrc: {
type: String as PropType<string>,
default: '',
},
frameSrc: propTypes.string.def(''),
},
setup() {
const loadingRef = ref(false);
const loading = ref(false);
const topRef = ref(50);
const heightRef = ref(window.innerHeight);
const frameRef = ref<HTMLElement | null>(null);
const frameRef = ref<HTMLFrameElement | null>(null);
const { prefixCls } = useDesign('iframe-page');
useWindowSizeFn(calcHeight, 150, { immediate: true });
const getWrapStyle = computed(
(): CSSProperties => {
return {
height: `${unref(heightRef)}px`,
};
}
);
function calcHeight() {
const iframe = unref(frameRef);
......@@ -40,21 +53,19 @@
iframe.style.height = `${clientHeight}px`;
}
useWindowSizeFn(calcHeight, 150, { immediate: true });
function hideLoading() {
loadingRef.value = false;
loading.value = false;
calcHeight();
}
function init() {
nextTick(() => {
const iframe = unref(frameRef);
if (!iframe) {
return;
}
if ((iframe as any).attachEvent) {
(iframe as any).attachEvent('onload', () => {
if (!iframe) return;
const _frame = iframe as any;
if (_frame.attachEvent) {
_frame.attachEvent('onload', () => {
hideLoading();
});
} else {
......@@ -65,23 +76,23 @@
});
}
onMounted(() => {
loadingRef.value = true;
loading.value = true;
init();
});
return {
getWrapStyle: computed(() => {
return {
height: `${unref(heightRef)}px`,
};
}),
loading: loadingRef,
getWrapStyle,
loading,
frameRef,
prefixCls,
};
},
});
</script>
<style lang="less" scoped>
.iframe-page {
@prefix-cls: ~'@{namespace}-iframe-page';
.@{prefix-cls} {
.ant-spin-nested-loading {
position: relative;
height: 100%;
......
......@@ -203,15 +203,12 @@
display: flex;
width: 40%;
height: 74%;
// font-size: 50em;
font-weight: 700;
color: #bababa;
background: #141313;
border-radius: 30px;
justify-content: center;
align-items: center;
// .respond-to(large-only, { font-size: 25em;});
// .respond-to(large-only, { font-size: 30em;});
@media (min-width: @screen-xxxl-min) {
font-size: 46em;
}
......
......@@ -12,7 +12,9 @@
const { params, query } = unref(currentRoute);
const { path } = params;
const _path = Array.isArray(path) ? path.join('/') : path;
replace({
path: '/' + _path,
query,
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论