Commit 7101587b9676c91e9079044a096df08848f1f602

Authored by vben
1 parent c0692b0f

feat: add error handle

.env.production
... ... @@ -7,7 +7,7 @@ VITE_PUBLIC_PATH = ./
7 7 # Delete console
8 8 VITE_DROP_CONSOLE = true
9 9  
10   -# Delete console
  10 +# Whether to output gz file for packaging
11 11 VITE_BUILD_GZIP = false
12 12  
13 13 # Basic interface address SPA
... ...
README.en-US.md
... ... @@ -225,17 +225,17 @@ yarn clean:lib # Delete node_modules, supported window
225 225 - [x] First screen loading waiting animation
226 226 - [x] Extract the production environment profile
227 227 - [x] Build Gzip
  228 +- [x] System performance optimization
  229 +- [x] Data import and export
  230 +- [x] Global error handling
228 231  
229 232 ## Developing features
230 233  
231 234 - [ ] Upload component
232 235 - [ ] Rich text component
233   -- [ ] Data import and export
234   -- [ ] Global error handling
235 236 - [ ] Theme configuration
236 237 - [ ] Dark theme
237 238 - [ ] Build CDN
238   -- [ ] System performance optimization
239 239  
240 240 If you have more components/functions/suggestions/bugs/, welcome to submit pr or issue.
241 241  
... ...
README.md
... ... @@ -223,17 +223,17 @@ yarn clean:lib # 删除node_modules,兼容window系统
223 223 - [x] 首屏加载等待动画
224 224 - [x] 抽取生产环境配置文件
225 225 - [x] 打包 Gzip
  226 +- [x] 数据导入导出
  227 +- [x] 系统性能优化
  228 +- [x] 全局错误处理
226 229  
227 230 ## 正在开发的功能
228 231  
229 232 - [ ] 上传组件
230 233 - [ ] 富文本组件
231   -- [ ] 数据导入导出
232   -- [ ] 全局错误处理
233 234 - [ ] 主题配置
234 235 - [ ] 黑暗主题
235 236 - [ ] 打包 CDN
236   -- [ ] 系统性能优化
237 237  
238 238 更多组件/功能/建议/bug/欢迎提交 pr 或者 issue
239 239  
... ...
build/config/vite/proxy.ts
... ... @@ -2,13 +2,17 @@ type ProxyItem = [string, string];
2 2  
3 3 type ProxyList = ProxyItem[];
4 4  
  5 +const reg = /^https:\/\//;
5 6 export function createProxy(list: ProxyList = []) {
6 7 const ret: any = {};
7 8 for (const [prefix, target] of list) {
  9 + const isHttps = reg.test(target);
  10 +
8 11 ret[prefix] = {
9 12 target: target,
10 13 changeOrigin: true,
11 14 rewrite: (path: string) => path.replace(new RegExp(`^${prefix}`), ''),
  15 + ...(isHttps ? { secure: false } : {}),
12 16 };
13 17 }
14 18 return ret;
... ...
build/plugin/vite-plugin-context-plugin/transform.ts renamed to build/plugin/vite-plugin-context/transform.ts
package.json
... ... @@ -41,10 +41,10 @@
41 41 "devDependencies": {
42 42 "@commitlint/cli": "^11.0.0",
43 43 "@commitlint/config-conventional": "^11.0.0",
44   - "@iconify/json": "^1.1.242",
  44 + "@iconify/json": "^1.1.243",
45 45 "@ls-lint/ls-lint": "^1.9.2",
46 46 "@purge-icons/generated": "^0.4.1",
47   - "@types/echarts": "^4.8.1",
  47 + "@types/echarts": "^4.8.3",
48 48 "@types/fs-extra": "^9.0.2",
49 49 "@types/html-minifier": "^4.0.0",
50 50 "@types/inquirer": "^7.3.1",
... ... @@ -55,7 +55,7 @@
55 55 "@types/qrcode": "^1.3.5",
56 56 "@types/rollup-plugin-visualizer": "^2.6.0",
57 57 "@types/shelljs": "^0.8.8",
58   - "@types/yargs": "^15.0.8",
  58 + "@types/yargs": "^15.0.9",
59 59 "@types/zxcvbn": "^4.4.0",
60 60 "@typescript-eslint/eslint-plugin": "^4.4.1",
61 61 "@typescript-eslint/parser": "^4.4.1",
... ... @@ -67,7 +67,7 @@
67 67 "cross-env": "^7.0.2",
68 68 "dotenv": "^8.2.0",
69 69 "eslint": "^7.10.0",
70   - "eslint-config-prettier": "^6.12.0",
  70 + "eslint-config-prettier": "^6.13.0",
71 71 "eslint-plugin-prettier": "^3.1.4",
72 72 "eslint-plugin-vue": "^7.0.1",
73 73 "fs-extra": "^9.0.1",
... ... @@ -76,7 +76,7 @@
76 76 "inquirer": "^7.3.3",
77 77 "koa-static": "^5.0.0",
78 78 "less": "^3.12.2",
79   - "lint-staged": "^10.4.0",
  79 + "lint-staged": "^10.4.2",
80 80 "portfinder": "^1.0.28",
81 81 "postcss-import": "^12.0.1",
82 82 "prettier": "^2.1.2",
... ... @@ -95,7 +95,7 @@
95 95 "vite-plugin-mock": "^1.0.2",
96 96 "vite-plugin-purge-icons": "^0.4.4",
97 97 "vue-eslint-parser": "^7.1.1",
98   - "yargs": "^16.0.3"
  98 + "yargs": "^16.1.0"
99 99 },
100 100 "repository": {
101 101 "type": "git",
... ...
src/App.vue
... ... @@ -33,6 +33,7 @@
33 33 const { on } = useLockPage();
34 34 lockOn = on;
35 35 }
  36 +
36 37 return {
37 38 transformCellText,
38 39 zhCN,
... ...
src/api/demo/error.ts 0 → 100644
  1 +import { defHttp } from '/@/utils/http/axios';
  2 +
  3 +enum Api {
  4 + // 该地址不存在
  5 + Error = '/error',
  6 +}
  7 +
  8 +/**
  9 + * @description: 触发ajax错误
  10 + */
  11 +export function fireErrorApi() {
  12 + return defHttp.request({
  13 + url: Api.Error,
  14 + method: 'GET',
  15 + });
  16 +}
... ...
src/components/Table/src/BasicTable.vue
... ... @@ -146,7 +146,7 @@
146 146 }
147 147 if (showSummary) {
148 148 propsData.footer = renderFooter.bind(null, {
149   - scroll,
  149 + scroll: scroll as any,
150 150 columnsRef: getColumnsRef,
151 151 summaryFunc: unref(getMergeProps).summaryFunc,
152 152 dataSourceRef: getDataSourceRef,
... ...
src/components/Table/src/components/TableAction.tsx
... ... @@ -29,7 +29,6 @@ export default defineComponent({
29 29 const {
30 30 disabled = false,
31 31 label,
32   - props,
33 32 icon,
34 33 color = '',
35 34 type = 'link',
... ... @@ -41,7 +40,7 @@ export default defineComponent({
41 40 size="small"
42 41 disabled={disabled}
43 42 color={color}
44   - {...props}
  43 + {...action}
45 44 key={index}
46 45 >
47 46 {() => (
... ... @@ -101,7 +100,6 @@ export default defineComponent({
101 100 const {
102 101 disabled = false,
103 102 label,
104   - props,
105 103 icon,
106 104 color = '',
107 105 type = 'link',
... ... @@ -112,7 +110,7 @@ export default defineComponent({
112 110 <Button
113 111 type={type}
114 112 size="small"
115   - {...props}
  113 + {...action}
116 114 disabled={disabled}
117 115 color={color}
118 116 >
... ...
src/enums/exceptionEnum.ts
... ... @@ -23,3 +23,11 @@ export enum ExceptionEnum {
23 23 // No data on the page. In fact, it is not an exception page
24 24 PAGE_NOT_DATA = 10400,
25 25 }
  26 +
  27 +export enum ErrorTypeEnum {
  28 + VUE = 'vue',
  29 + SCRIPT = 'script',
  30 + RESOURCE = 'resource',
  31 + AJAX = 'ajax',
  32 + PROMISE = 'promise',
  33 +}
... ...
src/hooks/web/usePage.ts
... ... @@ -19,7 +19,7 @@ function handleError(e: Error) {
19 19 // page switch
20 20 export function useGo() {
21 21 const { push, replace } = useRouter();
22   - function go(opt: PageEnum | RouteLocationRawEx = PageEnum.BASE_HOME, isReplace = false) {
  22 + function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
23 23 if (isString(opt)) {
24 24 isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError);
25 25 } else {
... ...
src/layouts/default/LayoutHeader.tsx
1 1 import { defineComponent, unref, computed } from 'vue';
2   -import { Layout, Tooltip } from 'ant-design-vue';
  2 +import { Layout, Tooltip, Badge } from 'ant-design-vue';
3 3 import Logo from '/@/layouts/Logo.vue';
4 4 import UserDropdown from './UserDropdown';
5 5 import LayoutMenu from './LayoutMenu';
... ... @@ -12,12 +12,15 @@ import {
12 12 FullscreenOutlined,
13 13 GithubFilled,
14 14 LockOutlined,
  15 + BugOutlined,
15 16 } from '@ant-design/icons-vue';
16 17 import { useFullscreen } from '/@/hooks/web/useFullScreen';
17 18 import { useTabs } from '/@/hooks/web/useTabs';
18 19 import { GITHUB_URL } from '/@/settings/siteSetting';
19 20 import LockAction from './actions/LockActionItem';
20 21 import { useModal } from '/@/components/Modal/index';
  22 +import { errorStore } from '/@/store/modules/error';
  23 +import { useGo } from '/@/hooks/web/usePage';
21 24  
22 25 export default defineComponent({
23 26 name: 'DefaultLayoutHeader',
... ... @@ -25,6 +28,7 @@ export default defineComponent({
25 28 const { refreshPage } = useTabs();
26 29 const [register, { openModal }] = useModal();
27 30 const { toggleFullscreen, isFullscreenRef } = useFullscreen();
  31 + const go = useGo();
28 32 const getProjectConfigRef = computed(() => {
29 33 return appStore.getProjectConfig;
30 34 });
... ... @@ -37,6 +41,12 @@ export default defineComponent({
37 41 const theme = unref(getProjectConfigRef).headerSetting.theme;
38 42 return theme ? `layout-header__header--${theme}` : '';
39 43 });
  44 +
  45 + function handleToErrorList() {
  46 + errorStore.commitErrorListCountState(0);
  47 + go('/exception/error-log');
  48 + }
  49 +
40 50 /**
41 51 * @description: 锁定屏幕
42 52 */
... ... @@ -46,9 +56,9 @@ export default defineComponent({
46 56 return () => {
47 57 const getProjectConfig = unref(getProjectConfigRef);
48 58 const {
49   - // useErrorHandle,
  59 + useErrorHandle,
50 60 showLogo,
51   - headerSetting: { theme: headerTheme, showRedo, showGithub, showFullScreen },
  61 + headerSetting: { theme: headerTheme, useLockPage, showRedo, showGithub, showFullScreen },
52 62 menuSetting: { mode, type: menuType, split: splitMenu, topMenuAlign },
53 63 showBreadCrumb,
54 64 } = getProjectConfig;
... ... @@ -77,8 +87,28 @@ export default defineComponent({
77 87 </div>
78 88  
79 89 <div class={`layout-header__action`}>
  90 + {useErrorHandle && (
  91 + <Tooltip>
  92 + {{
  93 + title: () => '错误日志',
  94 + default: () => (
  95 + <Badge
  96 + count={errorStore.getErrorListCountState}
  97 + offset={[0, 10]}
  98 + overflowCount={99}
  99 + >
  100 + {() => (
  101 + <div class={`layout-header__action-item`} onClick={handleToErrorList}>
  102 + <BugOutlined class={`layout-header__action-icon`} />
  103 + </div>
  104 + )}
  105 + </Badge>
  106 + ),
  107 + }}
  108 + </Tooltip>
  109 + )}
  110 +
80 111 {showGithub && (
81   - // @ts-ignore
82 112 <Tooltip>
83 113 {{
84 114 title: () => 'github',
... ... @@ -90,8 +120,7 @@ export default defineComponent({
90 120 }}
91 121 </Tooltip>
92 122 )}
93   - {showGithub && (
94   - // @ts-ignore
  123 + {useLockPage && (
95 124 <Tooltip>
96 125 {{
97 126 title: () => '锁定屏幕',
... ... @@ -104,7 +133,6 @@ export default defineComponent({
104 133 </Tooltip>
105 134 )}
106 135 {showRedo && (
107   - // @ts-ignore
108 136 <Tooltip>
109 137 {{
110 138 title: () => '刷新',
... ... @@ -117,7 +145,6 @@ export default defineComponent({
117 145 </Tooltip>
118 146 )}
119 147 {showFullScreen && (
120   - // @ts-ignore
121 148 <Tooltip>
122 149 {{
123 150 title: () => (unref(isFullscreenRef) ? '退出全屏' : '全屏'),
... ...
src/layouts/default/actions/LockActionItem.tsx
... ... @@ -40,10 +40,11 @@ export default defineComponent({
40 40 let password: string | undefined = '';
41 41  
42 42 try {
43   - const values = (await validateFields()) as any;
44   - password = values.password;
45 43 if (!valid) {
46 44 password = undefined;
  45 + } else {
  46 + const values = (await validateFields()) as any;
  47 + password = values.password;
47 48 }
48 49 setModalProps({
49 50 visible: false,
... ...
src/layouts/default/actions/notice/NoticeActionItem.tsx 0 → 100644
  1 +import { defineComponent } from 'vue';
  2 +import { Popover, Tabs } from 'ant-design-vue';
  3 +
  4 +import NoticeList from './NoticeList';
  5 +import { NoticeTabItem, NoticeListItem, noticeTabListData, noticeListData } from './data';
  6 +import './index.less';
  7 +
  8 +const prefixCls = 'notice-popover';
  9 +export default defineComponent({
  10 + name: 'NoticePopover',
  11 + props: {
  12 + visible: {
  13 + type: Boolean,
  14 + default: false,
  15 + },
  16 + },
  17 + setup(props, { attrs }) {
  18 + // 渲染卡片内容
  19 + function renderContent() {
  20 + return (
  21 + <Tabs class={`${prefixCls}__tabs`}>
  22 + {() => {
  23 + return noticeTabListData.map((item: NoticeTabItem) => {
  24 + const { key, name } = item;
  25 + return (
  26 + <Tabs.TabPane key={key} tab={renderTab(key, name)}>
  27 + {() => <NoticeList list={getListData(key)} />}
  28 + </Tabs.TabPane>
  29 + );
  30 + });
  31 + }}
  32 + </Tabs>
  33 + );
  34 + }
  35 +
  36 + // tab标题渲染
  37 + function renderTab(key: string, name: string) {
  38 + const list = getListData(key);
  39 + const unreadlist = list.filter((item: NoticeListItem) => !item.read);
  40 + return (
  41 + <div>
  42 + {name}
  43 + {unreadlist.length > 0 && <span>({unreadlist.length})</span>}
  44 + </div>
  45 + );
  46 + }
  47 +
  48 + // 获取数据
  49 + function getListData(type: string) {
  50 + return noticeListData.filter((item: NoticeListItem) => item.type === type);
  51 + }
  52 +
  53 + return () => {
  54 + const { visible } = props;
  55 + return (
  56 + <Popover
  57 + title=""
  58 + {...{
  59 + ...attrs,
  60 + visible,
  61 + }}
  62 + content={renderContent}
  63 + class={prefixCls}
  64 + />
  65 + );
  66 + };
  67 + },
  68 +});
... ...
src/layouts/default/actions/notice/NoticeList.tsx 0 → 100644
  1 +import { defineComponent } from 'vue';
  2 +import { List, Avatar, Tag } from 'ant-design-vue';
  3 +
  4 +import { NoticeListItem } from './data';
  5 +import './index.less';
  6 +
  7 +const prefixCls = 'notice-popover';
  8 +export default defineComponent({
  9 + name: 'NoticeList',
  10 + props: {
  11 + list: {
  12 + type: Array,
  13 + default: () => [],
  14 + },
  15 + },
  16 + setup(props) {
  17 + // 头像渲染
  18 + function renderAvatar(avatar: string) {
  19 + return avatar ? <Avatar class="avatar" src={avatar} /> : <span>{avatar}</span>;
  20 + }
  21 +
  22 + // 描述渲染
  23 + function renderDescription(description: string, datetime: string) {
  24 + return (
  25 + <div>
  26 + <div class="description">{description}</div>
  27 + <div class="datetime">{datetime}</div>
  28 + </div>
  29 + );
  30 + }
  31 +
  32 + // 标题渲染
  33 + function renderTitle(title: string, extra?: string, color?: string) {
  34 + return (
  35 + <div class="title">
  36 + {title}
  37 + {extra && (
  38 + <div class="extra">
  39 + <Tag class="tag" color={color}>
  40 + {() => extra}
  41 + </Tag>
  42 + </div>
  43 + )}
  44 + </div>
  45 + );
  46 + }
  47 +
  48 + return () => {
  49 + const { list } = props;
  50 + return (
  51 + <List dataSource={list} class={`${prefixCls}__list`}>
  52 + {() => {
  53 + return list.map((item: NoticeListItem) => {
  54 + const { id, avatar, title, description, datetime, extra, read, color } = item;
  55 + return (
  56 + <List.Item key={id} class={`${prefixCls}__list-item ${read ? 'read' : ''}`}>
  57 + {() => (
  58 + <List.Item.Meta
  59 + class="meta"
  60 + avatar={renderAvatar(avatar)}
  61 + title={renderTitle(title, extra, color)}
  62 + description={renderDescription(description, datetime)}
  63 + />
  64 + )}
  65 + </List.Item>
  66 + );
  67 + });
  68 + }}
  69 + </List>
  70 + );
  71 + };
  72 + },
  73 +});
... ...
src/layouts/default/index.less
... ... @@ -102,12 +102,12 @@
102 102 .setting-button {
103 103 top: 45%;
104 104 right: 0;
105   - padding: 14px;
  105 + padding: 8px;
106 106 border-radius: 6px 0 0 6px;
107 107  
108 108 svg {
109   - width: 1.2em;
110   - height: 1.2em;
  109 + width: 1em;
  110 + height: 1em;
111 111 }
112 112 }
113 113  
... ...
src/layouts/default/setting/index.vue
... ... @@ -30,7 +30,7 @@
30 30 position: absolute;
31 31 z-index: 10;
32 32 display: flex;
33   - padding: 10px;
  33 + // padding: 10px;
34 34 color: @white;
35 35 cursor: pointer;
36 36 background: @primary-color;
... ...
src/layouts/page/index.tsx
... ... @@ -54,6 +54,7 @@ export default defineComponent({
54 54 {...on}
55 55 name={name || route.meta.transitionName || routerTransition}
56 56 mode="out-in"
  57 + appear={true}
57 58 >
58 59 {() => Content}
59 60 </Transition>
... ...
src/main.ts
... ... @@ -3,6 +3,7 @@ import { createApp } from &#39;vue&#39;;
3 3 import router, { setupRouter } from '/@/router';
4 4 import { setupStore } from '/@/store';
5 5 import { setupAntd } from '/@/setup/ant-design-vue';
  6 +import { setupErrorHandle } from '/@/setup/error-handle/index';
6 7 import { setupDirectives } from '/@/setup/directives/index';
7 8  
8 9 import { registerGlobComp } from '/@/components/registerGlobComp';
... ... @@ -21,10 +22,12 @@ setupRouter(app);
21 22 // store
22 23 setupStore(app);
23 24  
24   -registerGlobComp(app);
25   -
26 25 setupDirectives(app);
27 26  
  27 +setupErrorHandle(app);
  28 +
  29 +registerGlobComp(app);
  30 +
28 31 router.isReady().then(() => {
29 32 app.mount('#app');
30 33 });
... ...
src/router/menus/modules/demo/exception.ts
... ... @@ -25,6 +25,10 @@ const menu: MenuModule = {
25 25 path: '/not-data',
26 26 name: '无数据',
27 27 },
  28 + {
  29 + path: '/error-log',
  30 + name: '错误日志',
  31 + },
28 32 ],
29 33 },
30 34 };
... ...
src/router/routes/modules/demo/exception.ts
... ... @@ -78,5 +78,13 @@ export default {
78 78 afterCloseLoading: true,
79 79 },
80 80 },
  81 + {
  82 + path: '/error-log',
  83 + name: 'ErrorLog',
  84 + component: () => import('/@/views/sys/error-log/index.vue'),
  85 + meta: {
  86 + title: '错误日志',
  87 + },
  88 + },
81 89 ],
82 90 } as AppRouteModule;
... ...
src/settings/projectSetting.ts
... ... @@ -30,7 +30,7 @@ const setting: ProjectConfig = {
30 30 // theme
31 31 theme: MenuThemeEnum.LIGHT,
32 32 // 开启锁屏功能
33   - useLockPage: isProdMode(),
  33 + useLockPage: true,
34 34 // 显示刷新按钮
35 35 showRedo: true,
36 36 // 显示全屏按钮
... ... @@ -86,7 +86,7 @@ const setting: ProjectConfig = {
86 86 // 是否开启KeepAlive缓存 开发时候最好关闭,不然每次都需要清除缓存
87 87 openKeepAlive: true,
88 88  
89   - // 自动锁屏时间,为0不锁屏。 单位分钟 默认1个小时
  89 + // 自动锁屏时间,为0不锁屏。 单位分钟 默认0
90 90 lockTime: 0,
91 91 // 显示面包屑
92 92 showBreadCrumb: true,
... ... @@ -96,6 +96,7 @@ const setting: ProjectConfig = {
96 96  
97 97 // 开启页面切换动画
98 98 openRouterTransition: true,
  99 +
99 100 // 路由切换动画
100 101 routerTransition: RouterTransitionEnum.ZOOM_FADE,
101 102  
... ...
src/setup/error-handle/index.ts 0 → 100644
  1 +import { errorStore, ErrorInfo } from '/@/store/modules/error';
  2 +import { useSetting } from '/@/hooks/core/useSetting';
  3 +import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
  4 +import { App } from 'vue';
  5 +function processStackMsg(error: Error) {
  6 + if (!error.stack) {
  7 + return '';
  8 + }
  9 + let stack = error.stack
  10 + .replace(/\n/gi, '') // 去掉换行,节省传输内容大小
  11 + .replace(/\bat\b/gi, '@') // chrome中是at,ff中是@
  12 + .split('@') // 以@分割信息
  13 + .slice(0, 9) // 最大堆栈长度(Error.stackTraceLimit = 10),所以只取前10条
  14 + .map((v) => v.replace(/^\s*|\s*$/g, '')) // 去除多余空格
  15 + .join('~') // 手动添加分隔符,便于后期展示
  16 + .replace(/\?[^:]+/gi, ''); // 去除js文件链接的多余参数(?x=1之类)
  17 + const msg = error.toString();
  18 + if (stack.indexOf(msg) < 0) {
  19 + stack = msg + '@' + stack;
  20 + }
  21 + return stack;
  22 +}
  23 +
  24 +function formatComponentName(vm: any) {
  25 + if (vm.$root === vm) {
  26 + return {
  27 + name: 'root',
  28 + path: 'root',
  29 + };
  30 + }
  31 +
  32 + const options = vm.$options as any;
  33 + if (!options) {
  34 + return {
  35 + name: 'anonymous',
  36 + path: 'anonymous',
  37 + };
  38 + }
  39 + const name = options.name || options._componentTag;
  40 + return {
  41 + name: name,
  42 + path: options.__file,
  43 + };
  44 +}
  45 +
  46 +function vueErrorHandler(err: Error, vm: any, info: string) {
  47 + const { name, path } = formatComponentName(vm);
  48 + errorStore.commitErrorInfoState({
  49 + type: ErrorTypeEnum.VUE,
  50 + name,
  51 + file: path,
  52 + message: err.message,
  53 + stack: processStackMsg(err),
  54 + detail: info,
  55 + url: window.location.href,
  56 + });
  57 +}
  58 +
  59 +export function scriptErrorHandler(
  60 + event: Event | string,
  61 + source?: string,
  62 + lineno?: number,
  63 + colno?: number,
  64 + error?: Error
  65 +) {
  66 + if (event === 'Script error.' && !source) {
  67 + return false;
  68 + }
  69 + setTimeout(function () {
  70 + const errorInfo: Partial<ErrorInfo> = {};
  71 + colno = colno || (window.event && (window.event as any).errorCharacter) || 0;
  72 + errorInfo.message = event as string;
  73 + if (error && error.stack) {
  74 + errorInfo.stack = error.stack;
  75 + } else {
  76 + errorInfo.stack = '';
  77 + }
  78 + const name = source ? source.substr(source.lastIndexOf('/') + 1) : 'script';
  79 + errorStore.commitErrorInfoState({
  80 + type: ErrorTypeEnum.SCRIPT,
  81 + name: name,
  82 + file: source as string,
  83 + detail: 'lineno' + lineno,
  84 + url: window.location.href,
  85 + ...(errorInfo as Pick<ErrorInfo, 'message' | 'stack'>),
  86 + });
  87 + }, 0);
  88 + return true;
  89 +}
  90 +
  91 +function registerPromiseErrorHandler() {
  92 + window.addEventListener(
  93 + 'unhandledrejection',
  94 + function (event: any) {
  95 + errorStore.commitErrorInfoState({
  96 + type: ErrorTypeEnum.PROMISE,
  97 + name: 'Promise Error!',
  98 + file: 'none',
  99 + detail: 'promise error!',
  100 + url: window.location.href,
  101 + stack: 'promise error!',
  102 + message: event.reason,
  103 + });
  104 + },
  105 + true
  106 + );
  107 +}
  108 +
  109 +function registerResourceErrorHandler() {
  110 + // 监控资源加载错误(img,script,css,以及jsonp)
  111 + window.addEventListener(
  112 + 'error',
  113 + function (e: Event) {
  114 + const target = e.target ? e.target : (e.srcElement as any);
  115 +
  116 + errorStore.commitErrorInfoState({
  117 + type: ErrorTypeEnum.RESOURCE,
  118 + name: 'Resouce Error!',
  119 + file: (e.target || ({} as any)).currentSrc,
  120 + detail: JSON.stringify({
  121 + tagName: target.localName,
  122 + html: target.outerHTML,
  123 + type: e.type,
  124 + }),
  125 + url: window.location.href,
  126 + stack: 'resouce is not found',
  127 + message: (e.target || ({} as any)).localName + ' is load error',
  128 + });
  129 + },
  130 + true
  131 + );
  132 +}
  133 +
  134 +export function setupErrorHandle(app: App) {
  135 + const { projectSetting } = useSetting();
  136 + const { useErrorHandle } = projectSetting;
  137 + if (!useErrorHandle) {
  138 + return;
  139 + }
  140 + // Vue异常监控;
  141 + app.config.errorHandler = vueErrorHandler;
  142 + // js错误
  143 + window.onerror = scriptErrorHandler;
  144 + // promise 异常
  145 + registerPromiseErrorHandler();
  146 +
  147 + // 静态资源异常
  148 + registerResourceErrorHandler();
  149 +}
... ...
src/store/modules/error.ts
1 1 import store from '/@/store';
2 2 import { hotModuleUnregisterModule } from '/@/utils/helper/vuexHelper';
3   -import { VuexModule, getModule, Module, Mutation } from 'vuex-module-decorators';
  3 +import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators';
4 4  
5 5 import { formatToDateTime } from '/@/utils/dateUtil';
6   -export enum ErrorTypeEnum {
7   - VUE = 'vue',
8   - SCRIPT = 'script',
9   - RESOURCE = 'resource',
10   - AJAX = 'ajax',
11   - PROMISE = 'promise',
12   -}
  6 +import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
  7 +import { useSetting } from '/@/hooks/core/useSetting';
13 8  
14 9 export interface ErrorInfo {
15 10 type: ErrorTypeEnum;
... ... @@ -43,10 +38,11 @@ class Error extends VuexModule implements ErrorState {
43 38  
44 39 @Mutation
45 40 commitErrorInfoState(info: ErrorInfo): void {
46   - this.errorInfoState.unshift({
  41 + const item = {
47 42 ...info,
48 43 time: formatToDateTime(new Date()),
49   - });
  44 + };
  45 + this.errorInfoState = [item, ...this.errorInfoState];
50 46 this.errorListCountState += 1;
51 47 }
52 48  
... ... @@ -54,6 +50,30 @@ class Error extends VuexModule implements ErrorState {
54 50 commitErrorListCountState(count: number): void {
55 51 this.errorListCountState = count;
56 52 }
  53 +
  54 + @Action
  55 + setupErrorHandle(error: any) {
  56 + const { projectSetting } = useSetting();
  57 + const { useErrorHandle } = projectSetting;
  58 + if (!useErrorHandle) return;
  59 +
  60 + const errInfo: Partial<ErrorInfo> = {
  61 + message: error.message,
  62 + type: ErrorTypeEnum.AJAX,
  63 + };
  64 + if (error.response) {
  65 + const {
  66 + config: { url = '', data: params = '', method = 'get', headers = {} } = {},
  67 + data = {},
  68 + } = error.response;
  69 + errInfo.url = url;
  70 + errInfo.name = 'Ajax Error!';
  71 + errInfo.file = '-';
  72 + errInfo.stack = JSON.stringify(data);
  73 + errInfo.detail = JSON.stringify({ params, method, headers });
  74 + }
  75 + this.commitErrorInfoState(errInfo as ErrorInfo);
  76 + }
57 77 }
58 78 export { Error };
59 79 export const errorStore = getModule<Error>(Error);
... ...
src/utils/http/axios/index.ts
... ... @@ -18,36 +18,13 @@ import { RequestEnum, ResultEnum, ContentTypeEnum } from &#39;/@/enums/httpEnum&#39;;
18 18 import { isString } from '/@/utils/is';
19 19 import { formatRequestDate } from '/@/utils/dateUtil';
20 20 import { setObjToUrlParams, deepMerge } from '/@/utils';
21   -import { errorStore, ErrorTypeEnum, ErrorInfo } from '/@/store/modules/error';
22   -import { appStore } from '/@/store/modules/app';
  21 +import { errorStore } from '/@/store/modules/error';
23 22 import { errorResult } from './const';
24 23  
25 24 const { globSetting } = useSetting();
26 25 const prefix = globSetting.urlPrefix;
27 26 const { createMessage, createErrorModal } = useMessage();
28 27  
29   -function setupErrorHandle(error: any) {
30   - const { useErrorHandle } = appStore.getProjectConfig;
31   - if (!useErrorHandle) return;
32   -
33   - const errInfo: Partial<ErrorInfo> = {
34   - message: error.message,
35   - type: ErrorTypeEnum.AJAX,
36   - };
37   - if (error.response) {
38   - const {
39   - config: { url = '', data: params = '', method = 'get', headers = {} } = {},
40   - data = {},
41   - } = error.response;
42   - errInfo.url = url;
43   - errInfo.name = 'Ajax Error!';
44   - errInfo.file = '-';
45   - errInfo.stack = JSON.stringify(data);
46   - errInfo.detail = JSON.stringify({ params, method, headers });
47   - }
48   - errorStore.commitErrorInfoState(errInfo as ErrorInfo);
49   -}
50   -
51 28 /**
52 29 * @description: 数据处理,方便区分多种处理方式
53 30 */
... ... @@ -175,7 +152,7 @@ const transform: AxiosTransform = {
175 152 * @description: 响应错误处理
176 153 */
177 154 responseInterceptorsCatch: (error: any) => {
178   - setupErrorHandle(error);
  155 + errorStore.setupErrorHandle(error);
179 156 const { response, code, message } = error || {};
180 157 const msg: string =
181 158 response && response.data && response.data.error ? response.data.error.message : '';
... ...
src/views/sys/error-log/DetailModal.vue 0 → 100644
  1 +<template>
  2 + <BasicModal :width="800" title="错误详情" v-bind="$attrs">
  3 + <Description :data="info" @register="register" />
  4 + </BasicModal>
  5 +</template>
  6 +<script lang="ts">
  7 + import { defineComponent, PropType } from 'vue';
  8 + import { BasicModal } from '/@/components/Modal/index';
  9 + import { ErrorInfo } from '/@/store/modules/error';
  10 + import { Description, useDescription } from '/@/components/Description/index';
  11 + import { getDescSchema } from './data';
  12 +
  13 + export default defineComponent({
  14 + name: 'ErrorLogDetailModal',
  15 + components: { BasicModal, Description },
  16 + props: {
  17 + info: {
  18 + type: Object as PropType<ErrorInfo>,
  19 + default: null,
  20 + },
  21 + },
  22 + setup() {
  23 + const [register] = useDescription({
  24 + column: 2,
  25 + schema: getDescSchema(),
  26 + });
  27 + return {
  28 + register,
  29 + };
  30 + },
  31 + });
  32 +</script>
... ...
src/views/sys/error-log/data.tsx 0 → 100644
  1 +import { Tag } from 'ant-design-vue';
  2 +import { BasicColumn } from '/@/components/Table/index';
  3 +import { ErrorTypeEnum } from '/@/enums/exceptionEnum';
  4 +
  5 +export function getColumns(): BasicColumn[] {
  6 + return [
  7 + {
  8 + dataIndex: 'type',
  9 + title: '类型',
  10 + width: 80,
  11 + customRender: ({ text }) => {
  12 + const color =
  13 + text === ErrorTypeEnum.VUE
  14 + ? 'green'
  15 + : text === ErrorTypeEnum.RESOURCE
  16 + ? 'cyan'
  17 + : text === ErrorTypeEnum.PROMISE
  18 + ? 'blue'
  19 + : ErrorTypeEnum.AJAX
  20 + ? 'red'
  21 + : 'purple';
  22 + return <Tag color={color}>{() => text}</Tag>;
  23 + },
  24 + },
  25 + {
  26 + dataIndex: 'url',
  27 + title: '地址',
  28 + width: 200,
  29 + },
  30 + {
  31 + dataIndex: 'time',
  32 + title: '时间',
  33 + width: 160,
  34 + },
  35 + {
  36 + dataIndex: 'file',
  37 + title: '文件',
  38 + width: 200,
  39 + },
  40 + {
  41 + dataIndex: 'name',
  42 + title: 'Name',
  43 + width: 200,
  44 + },
  45 + {
  46 + dataIndex: 'message',
  47 + title: '错误信息',
  48 + width: 300,
  49 + },
  50 + {
  51 + dataIndex: 'stack',
  52 + title: 'stack信息',
  53 + width: 300,
  54 + },
  55 + ];
  56 +}
  57 +
  58 +export function getDescSchema() {
  59 + return getColumns().map((column) => {
  60 + return {
  61 + field: column.dataIndex!,
  62 + label: column.title,
  63 + };
  64 + });
  65 +}
... ...
src/views/sys/error-log/index.vue 0 → 100644
  1 +<template>
  2 + <div class="p-4">
  3 + <template v-for="src in imgListRef" :key="src">
  4 + <img :src="src" v-show="false" />
  5 + </template>
  6 + <DetailModal :info="rowInfoRef" @register="registerModal" />
  7 + <BasicTable @register="register" class="error-handle-table">
  8 + <template #toolbar>
  9 + <a-button @click="fireVueError" type="primary"> 点击触发vue错误 </a-button>
  10 + <a-button @click="fireResourceError" type="primary"> 点击触发resource错误 </a-button>
  11 + <a-button @click="fireAjaxError" type="primary"> 点击触发ajax错误 </a-button>
  12 + </template>
  13 + <template #action="{ record }">
  14 + <TableAction :actions="[{ label: '详情', onClick: handleDetail.bind(null, record) }]" />
  15 + </template>
  16 + </BasicTable>
  17 + </div>
  18 +</template>
  19 +
  20 +<script lang="ts">
  21 + import { defineComponent, watch, ref, nextTick } from 'vue';
  22 +
  23 + import DetailModal from './DetailModal.vue';
  24 + import { useModal } from '/@/components/Modal/index';
  25 +
  26 + import { BasicTable, useTable, TableAction } from '/@/components/Table/index';
  27 +
  28 + import { errorStore, ErrorInfo } from '/@/store/modules/error';
  29 +
  30 + import { fireErrorApi } from '/@/api/demo/error';
  31 +
  32 + import { getColumns } from './data';
  33 +
  34 + import { cloneDeep } from 'lodash-es';
  35 +
  36 + export default defineComponent({
  37 + name: 'ErrorHandler',
  38 + components: { DetailModal, BasicTable, TableAction },
  39 + setup() {
  40 + const rowInfoRef = ref<ErrorInfo>();
  41 + const imgListRef = ref<string[]>([]);
  42 + const [register, { setTableData }] = useTable({
  43 + titleHelpMessage: '只在`/src/settings/projectSetting.ts` 内的useErrorHandle=true时生效!',
  44 + title: '错误日志列表',
  45 + columns: getColumns(),
  46 + actionColumn: {
  47 + width: 80,
  48 + title: '操作',
  49 + dataIndex: 'action',
  50 + slots: { customRender: 'action' },
  51 + },
  52 + });
  53 +
  54 + const [registerModal, { openModal }] = useModal();
  55 + watch(
  56 + () => errorStore.getErrorInfoState,
  57 + (list) => {
  58 + nextTick(() => {
  59 + setTableData(cloneDeep(list));
  60 + });
  61 + },
  62 + {
  63 + immediate: true,
  64 + }
  65 + );
  66 +
  67 + // 查看详情
  68 + function handleDetail(row: ErrorInfo) {
  69 + rowInfoRef.value = row;
  70 + openModal(true);
  71 + }
  72 +
  73 + function fireVueError() {
  74 + throw new Error('fire vue error!');
  75 + }
  76 +
  77 + function fireResourceError() {
  78 + imgListRef.value.push(`${new Date().getTime()}.png`);
  79 + }
  80 +
  81 + async function fireAjaxError() {
  82 + await fireErrorApi();
  83 + }
  84 +
  85 + return {
  86 + register,
  87 + registerModal,
  88 + handleDetail,
  89 + fireVueError,
  90 + fireResourceError,
  91 + fireAjaxError,
  92 + imgListRef,
  93 + rowInfoRef,
  94 + };
  95 + },
  96 + });
  97 +</script>
... ...
src/views/sys/lock/index.vue
... ... @@ -118,8 +118,8 @@
118 118 &__entry {
119 119 position: relative;
120 120 width: 400px;
121   - height: 260px;
122   - padding: 80px 50px 0 50px;
  121 + // height: 260px;
  122 + padding: 80px 50px 50px 50px;
123 123 margin-right: 50px;
124 124 background: #fff;
125 125 border-radius: 6px;
... ...
vite.config.ts
... ... @@ -13,7 +13,7 @@ import { createProxy } from &#39;./build/config/vite/proxy&#39;;
13 13 import { createMockServer } from 'vite-plugin-mock';
14 14 import PurgeIcons from 'vite-plugin-purge-icons';
15 15 import gzipPlugin from './build/plugin/gzip/index';
16   -import globbyTransform from './build/plugin/vite-plugin-context-plugin/transform';
  16 +import globbyTransform from './build/plugin/vite-plugin-context/transform';
17 17  
18 18 import { isDevFn, isReportMode, isProdFn, loadEnv, isBuildGzip, isSiteMode } from './build/utils';
19 19 const pkg = require('./package.json');
... ...
yarn.lock
... ... @@ -386,10 +386,10 @@
386 386 resolved "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-rc.1.tgz#a8bae29d71016d5af98c69f56a73c4a040217b3a"
387 387 integrity sha512-ji5H04VjYtR4seIEgVVLPxg1KRhrFquOiyfPyLVS6vYPkuqV6bcWdssi05YSmf/OAzG4E7Qsg80/bOKyd5tYTw==
388 388  
389   -"@iconify/json@^1.1.242":
390   - version "1.1.242"
391   - resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.242.tgz#f43b83acb7c3fe7906eb46cbe174a946aa17f9ff"
392   - integrity sha512-uxiUvrINyZlITzKxa2J/75AzReenah83XxQLyryVHjGCSqNWQjQICmBLlrCsch5PMr5nF9JeNMbIPDzxlS59Og==
  389 +"@iconify/json@^1.1.243":
  390 + version "1.1.243"
  391 + resolved "https://registry.npmjs.org/@iconify/json/-/json-1.1.243.tgz#8013fc781621b2549e379aab4e3e945974ec8032"
  392 + integrity sha512-vf8N+fUJysvCoJknS4XqorlvWLqjw/+Mid8CuzxQxMX8/RlWRU1yMFIgRpGAkMnCYe38KUtHqFVu0FlVNGP/Lw==
393 393  
394 394 "@ls-lint/ls-lint@^1.9.2":
395 395 version "1.9.2"
... ... @@ -577,10 +577,10 @@
577 577 "@types/keygrip" "*"
578 578 "@types/node" "*"
579 579  
580   -"@types/echarts@^4.8.1":
581   - version "4.8.1"
582   - resolved "https://registry.npmjs.org/@types/echarts/-/echarts-4.8.1.tgz#e03aed60bbf25b7629affab699175df2e980fbb2"
583   - integrity sha512-+kyP8TUkyJgmIBioPBJiTay9G7f/xcW7/8CYgh3iWa8kQ/SbGmAIpXyyCXtiWqPXT+tnsIONLC4hcNfmxVfxAg==
  580 +"@types/echarts@^4.8.3":
  581 + version "4.8.3"
  582 + resolved "https://registry.npmjs.org/@types/echarts/-/echarts-4.8.3.tgz#78ef1ede01c3705b52342da997b3d54571d3604e"
  583 + integrity sha512-5aFZ7/6f+SPonLh4Nuso6pEZWwX8VBMYh2e83x1GVEpGkcN3GC0HzxPoF6ZSZPwoe5Rg4bhNwD9f2TVYxgU/QQ==
584 584 dependencies:
585 585 "@types/zrender" "*"
586 586  
... ... @@ -849,10 +849,10 @@
849 849 resolved "https://registry.npm.taobao.org/@types/yargs-parser/download/@types/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
850 850 integrity sha1-yz+fdBhp4gzOMw/765JxWQSDiC0=
851 851  
852   -"@types/yargs@^15.0.8":
853   - version "15.0.8"
854   - resolved "https://registry.npm.taobao.org/@types/yargs/download/@types/yargs-15.0.8.tgz?cache=0&sync_timestamp=1602182032636&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fyargs%2Fdownload%2F%40types%2Fyargs-15.0.8.tgz#7644904cad7427eb704331ea9bf1ee5499b82e23"
855   - integrity sha1-dkSQTK10J+twQzHqm/HuVJm4LiM=
  852 +"@types/yargs@^15.0.9":
  853 + version "15.0.9"
  854 + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz#524cd7998fe810cdb02f26101b699cccd156ff19"
  855 + integrity sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==
856 856 dependencies:
857 857 "@types/yargs-parser" "*"
858 858  
... ... @@ -1735,10 +1735,10 @@ cliui@^6.0.0:
1735 1735 strip-ansi "^6.0.0"
1736 1736 wrap-ansi "^6.2.0"
1737 1737  
1738   -cliui@^7.0.0:
1739   - version "7.0.1"
1740   - resolved "https://registry.npm.taobao.org/cliui/download/cliui-7.0.1.tgz#a4cb67aad45cd83d8d05128fc9f4d8fbb887e6b3"
1741   - integrity sha1-pMtnqtRc2D2NBRKPyfTY+7iH5rM=
  1738 +cliui@^7.0.2:
  1739 + version "7.0.3"
  1740 + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.3.tgz#ef180f26c8d9bff3927ee52428bfec2090427981"
  1741 + integrity sha512-Gj3QHTkVMPKqwP3f7B4KPkBZRMR9r4rfi5bXFpg1a+Svvj8l7q5CnkBkVQzfxT5DFSsGk2+PascOgL0JYkL2kw==
1742 1742 dependencies:
1743 1743 string-width "^4.2.0"
1744 1744 strip-ansi "^6.0.0"
... ... @@ -2537,11 +2537,16 @@ esbuild@^0.7.1:
2537 2537 resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.7.14.tgz#9de555e75669187c2315317fbf489b229b1a4cbb"
2538 2538 integrity sha512-w2CEVeRcUhCGYMHnNNwb8q+9w42scL7RcNzJm85gZVzNBE3AF0sLq5YP/IdaTBJIFBphIKG3bGbwRH+zsgH/ig==
2539 2539  
2540   -escalade@^3.0.2, escalade@^3.1.0:
  2540 +escalade@^3.1.0:
2541 2541 version "3.1.0"
2542 2542 resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz#e8e2d7c7a8b76f6ee64c2181d6b8151441602d4e"
2543 2543 integrity sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==
2544 2544  
  2545 +escalade@^3.1.1:
  2546 + version "3.1.1"
  2547 + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
  2548 + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
  2549 +
2545 2550 escape-goat@^2.0.0:
2546 2551 version "2.1.1"
2547 2552 resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
... ... @@ -2557,10 +2562,10 @@ escape-string-regexp@^1.0.5:
2557 2562 resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
2558 2563 integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
2559 2564  
2560   -eslint-config-prettier@^6.12.0:
2561   - version "6.12.0"
2562   - resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.12.0.tgz#9eb2bccff727db1c52104f0b49e87ea46605a0d2"
2563   - integrity sha512-9jWPlFlgNwRUYVoujvWTQ1aMO8o6648r+K7qU7K5Jmkbyqav1fuEZC0COYpGBxyiAJb65Ra9hrmFx19xRGwXWw==
  2565 +eslint-config-prettier@^6.13.0:
  2566 + version "6.13.0"
  2567 + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.13.0.tgz#207d88796b5624e5bb815bbbdfc5891ceb9ebffa"
  2568 + integrity sha512-LcT0i0LSmnzqK2t764pyIt7kKH2AuuqKRTtJTdddWxOiUja9HdG5GXBVF2gmCTvVYWVsTu8J2MhJLVGRh+pj8w==
2564 2569 dependencies:
2565 2570 get-stdin "^6.0.0"
2566 2571  
... ... @@ -4207,10 +4212,10 @@ lines-and-columns@^1.1.6:
4207 4212 resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
4208 4213 integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
4209 4214  
4210   -lint-staged@^10.4.0:
4211   - version "10.4.0"
4212   - resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-10.4.0.tgz#d18628f737328e0bbbf87d183f4020930e9a984e"
4213   - integrity sha512-uaiX4U5yERUSiIEQc329vhCTDDwUcSvKdRLsNomkYLRzijk3v8V9GWm2Nz0RMVB87VcuzLvtgy6OsjoH++QHIg==
  4215 +lint-staged@^10.4.2:
  4216 + version "10.4.2"
  4217 + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-10.4.2.tgz#9fee4635c4b5ddb845746f237c6d43494ccd21c1"
  4218 + integrity sha512-OLCA9K1hS+Sl179SO6kX0JtnsaKj/MZalEhUj5yAgXsb63qPI/Gfn6Ua1KuZdbfkZNEu3/n5C/obYCu70IMt9g==
4214 4219 dependencies:
4215 4220 chalk "^4.1.0"
4216 4221 cli-truncate "^2.1.0"
... ... @@ -7268,10 +7273,10 @@ y18n@^4.0.0:
7268 7273 resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
7269 7274 integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
7270 7275  
7271   -y18n@^5.0.1:
7272   - version "5.0.2"
7273   - resolved "https://registry.npm.taobao.org/y18n/download/y18n-5.0.2.tgz?cache=0&sync_timestamp=1601576683926&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fy18n%2Fdownload%2Fy18n-5.0.2.tgz#48218df5da2731b4403115c39a1af709c873f829"
7274   - integrity sha1-SCGN9donMbRAMRXDmhr3Cchz+Ck=
  7276 +y18n@^5.0.2:
  7277 + version "5.0.4"
  7278 + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.4.tgz#0ab2db89dd5873b5ec4682d8e703e833373ea897"
  7279 + integrity sha512-deLOfD+RvFgrpAmSZgfGdWYE+OKyHcVHaRQ7NphG/63scpRvTHHeQMAxGGvaLVGJ+HYVcCXlzcTK0ZehFf+eHQ==
7275 7280  
7276 7281 yallist@^3.0.2:
7277 7282 version "3.1.1"
... ... @@ -7304,10 +7309,10 @@ yargs-parser@^18.1.2, yargs-parser@^18.1.3:
7304 7309 camelcase "^5.0.0"
7305 7310 decamelize "^1.2.0"
7306 7311  
7307   -yargs-parser@^20.0.0:
7308   - version "20.2.1"
7309   - resolved "https://registry.npm.taobao.org/yargs-parser/download/yargs-parser-20.2.1.tgz?cache=0&sync_timestamp=1601576684570&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs-parser%2Fdownload%2Fyargs-parser-20.2.1.tgz#28f3773c546cdd8a69ddae68116b48a5da328e77"
7310   - integrity sha1-KPN3PFRs3Ypp3a5oEWtIpdoyjnc=
  7312 +yargs-parser@^20.2.2:
  7313 + version "20.2.3"
  7314 + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz#92419ba867b858c868acf8bae9bf74af0dd0ce26"
  7315 + integrity sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==
7311 7316  
7312 7317 yargs@^13.2.4:
7313 7318 version "13.3.2"
... ... @@ -7342,18 +7347,18 @@ yargs@^15.0.0, yargs@^15.1.0:
7342 7347 y18n "^4.0.0"
7343 7348 yargs-parser "^18.1.2"
7344 7349  
7345   -yargs@^16.0.3:
7346   - version "16.0.3"
7347   - resolved "https://registry.npm.taobao.org/yargs/download/yargs-16.0.3.tgz?cache=0&sync_timestamp=1600660006050&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fyargs%2Fdownload%2Fyargs-16.0.3.tgz#7a919b9e43c90f80d4a142a89795e85399a7e54c"
7348   - integrity sha1-epGbnkPJD4DUoUKol5XoU5mn5Uw=
  7350 +yargs@^16.1.0:
  7351 + version "16.1.0"
  7352 + resolved "https://registry.npmjs.org/yargs/-/yargs-16.1.0.tgz#fc333fe4791660eace5a894b39d42f851cd48f2a"
  7353 + integrity sha512-upWFJOmDdHN0syLuESuvXDmrRcWd1QafJolHskzaw79uZa7/x53gxQKiR07W59GWY1tFhhU/Th9DrtSfpS782g==
7349 7354 dependencies:
7350   - cliui "^7.0.0"
7351   - escalade "^3.0.2"
  7355 + cliui "^7.0.2"
  7356 + escalade "^3.1.1"
7352 7357 get-caller-file "^2.0.5"
7353 7358 require-directory "^2.1.1"
7354 7359 string-width "^4.2.0"
7355   - y18n "^5.0.1"
7356   - yargs-parser "^20.0.0"
  7360 + y18n "^5.0.2"
  7361 + yargs-parser "^20.2.2"
7357 7362  
7358 7363 ylru@^1.2.0:
7359 7364 version "1.2.1"
... ...