feat: 注释&剔除无用代码&配置调整&TODO

This commit is contained in:
baiqi 2023-06-02 17:03:13 +08:00 committed by rubylliu
parent 50fe6619c8
commit 9798aa0bb1
39 changed files with 285 additions and 166 deletions

View File

@ -73,6 +73,7 @@ module.exports = {
'global-require': 0,
'no-plusplus': 'off',
'no-underscore-dangle': 'off',
'vue/attributes-order': 1,
},
// 对特定文件进行配置
overrides: [

View File

@ -6,11 +6,13 @@ import svgLoader from 'vite-svg-loader';
import configArcoStyleImportPlugin from './plugin/arcoStyleImport';
import configArcoResolverPlugin from './plugin/arcoResolver';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
export default defineConfig({
plugins: [
vue(),
vueJsx(),
vueSetupExtend(),
svgLoader({ svgoConfig: {} }),
configArcoResolverPlugin(),
configArcoStyleImportPlugin(),

View File

@ -36,8 +36,8 @@
"dependencies": {
"@7polo/kity": "2.0.8",
"@7polo/kityminder-core": "1.4.53",
"@arco-design/web-vue": "^2.46.0",
"@arco-themes/vue-ms-theme-default": "^0.0.7",
"@arco-design/web-vue": "^2.46.2",
"@arco-themes/vue-ms-theme-default": "^0.0.12",
"@form-create/arco-design": "^3.1.21",
"@vueuse/core": "^9.13.0",
"ace-builds": "^1.21.1",
@ -54,7 +54,7 @@
"pinia-plugin-persistedstate": "^3.1.0",
"query-string": "^8.1.0",
"sortablejs": "^1.15.0",
"vue": "^3.3.2",
"vue": "^3.3.4",
"vue-echarts": "^6.5.5",
"vue-i18n": "^9.2.2",
"vue-router": "^4.2.0",
@ -101,7 +101,7 @@
"rollup": "^2.79.1",
"rollup-plugin-visualizer": "^5.9.0",
"sass": "^1.62.1",
"stylelint": "^14.6.0",
"stylelint": "^14.16.1",
"stylelint-config-html": "^1.0.0",
"stylelint-config-prettier": "^9.0.3",
"stylelint-config-rational-order": "^0.1.2",
@ -121,6 +121,7 @@
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-imagemin": "^0.6.1",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vite-svg-loader": "^3.6.0",
"vitest": "^0.31.0",
"vue-tsc": "^1.6.5"

View File

@ -5,7 +5,29 @@
<script lang="ts" setup>
import { ref, nextTick } from 'vue';
import VCharts from 'vue-echarts';
// import { useAppStore } from '@/store';
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts';
import {
GridComponent,
TooltipComponent,
LegendComponent,
DataZoomComponent,
GraphicComponent,
} from 'echarts/components';
use([
CanvasRenderer,
BarChart,
LineChart,
PieChart,
RadarChart,
GridComponent,
TooltipComponent,
LegendComponent,
DataZoomComponent,
GraphicComponent,
]);
defineProps({
options: {
@ -27,13 +49,8 @@
default: '100%',
},
});
// const appStore = useAppStore();
// const theme = computed(() => {
// if (appStore.theme === 'dark') return 'dark';
// return '';
// });
const renderChart = ref(false);
// wait container expand
nextTick(() => {
renderChart.value = true;
});

View File

@ -1,35 +0,0 @@
import { App } from 'vue';
import { use } from 'echarts/core';
import { CanvasRenderer } from 'echarts/renderers';
import { BarChart, LineChart, PieChart, RadarChart } from 'echarts/charts';
import {
GridComponent,
TooltipComponent,
LegendComponent,
DataZoomComponent,
GraphicComponent,
} from 'echarts/components';
import Chart from './chart/index.vue';
import Breadcrumb from './breadcrumb/index.vue';
// Manually introduce ECharts modules to reduce packing size
use([
CanvasRenderer,
BarChart,
LineChart,
PieChart,
RadarChart,
GridComponent,
TooltipComponent,
LegendComponent,
DataZoomComponent,
GraphicComponent,
]);
export default {
install(Vue: App) {
Vue.component('Chart', Chart);
Vue.component('Breadcrumb', Breadcrumb);
},
};

View File

@ -30,45 +30,57 @@
const openKeys = ref<string[]>([]);
const selectedKey = ref<string[]>([]);
const goto = (item: RouteRecordRaw) => {
// Open external link
if (regexUrl.test(item.path)) {
openWindow(item.path);
selectedKey.value = [item.name as string];
return;
const goto = (item: RouteRecordRaw | null) => {
if (item) {
//
if (regexUrl.test(item.path)) {
openWindow(item.path);
selectedKey.value = [item.name as string];
return;
}
//
const { hideInMenu, activeMenu } = item.meta as RouteMeta;
if (route.name === item.name && !hideInMenu && !activeMenu) {
selectedKey.value = [item.name as string];
return;
}
router.push({
name: item.name,
});
} else {
router.push({
name: 'notFound',
});
}
// Eliminate external link side effects
const { hideInMenu, activeMenu } = item.meta as RouteMeta;
if (route.name === item.name && !hideInMenu && !activeMenu) {
selectedKey.value = [item.name as string];
return;
}
// Trigger router change
router.push({
name: item.name,
});
};
/**
* 查找激活的菜单项
* @param target 目标菜单名
*/
const findMenuOpenKeys = (target: string) => {
const result: string[] = [];
let isFind = false;
const backtrack = (item: RouteRecordRaw, keys: string[]) => {
if (item.name === target) {
const backtrack = (item: RouteRecordRaw | null, keys: string[]) => {
if (item?.name === target) {
isFind = true;
result.push(...keys);
return;
}
if (item.children?.length) {
if (item?.children?.length) {
item.children.forEach((el) => {
backtrack(el, [...keys, el.name as string]);
});
}
};
menuTree.value.forEach((el: RouteRecordRaw) => {
menuTree.value?.forEach((el: RouteRecordRaw | null) => {
if (isFind) return; // Performance optimization
backtrack(el, [el.name as string]);
backtrack(el, [el?.name as string]);
});
return result;
};
/**
* 监听路由变化存储打开及选中的菜单
*/
listenerRouteChange((newRoute) => {
const { requiresAuth, activeMenu, hideInMenu } = newRoute.meta;
if (requiresAuth !== false && (!hideInMenu || activeMenu)) {
@ -85,7 +97,7 @@
};
const renderSubMenu = () => {
function travel(_route: RouteRecordRaw[], nodes = []) {
function travel(_route: (RouteRecordRaw | null)[] | null, nodes = []) {
if (_route) {
_route.forEach((element) => {
// This is demo, modify nodes as needed

View File

@ -4,6 +4,10 @@ import usePermission from '@/hooks/usePermission';
import appClientMenus from '@/router/app-menus';
import { cloneDeep } from 'lodash-es';
/**
*
* @returns
*/
export default function useMenuTree() {
const permission = usePermission();
const menuTree = computed(() => {
@ -14,7 +18,7 @@ export default function useMenuTree() {
function travel(_routes: RouteRecordRaw[], layer: number) {
if (!_routes) return null;
const collector: any = _routes.map((element) => {
const collector = _routes.map((element) => {
// 权限校验不通过
if (!permission.accessRouter(element)) {
return null;
@ -32,13 +36,13 @@ export default function useMenuTree() {
// 解析子菜单
const subItem = travel(element.children, layer + 1);
if (subItem.length) {
element.children = subItem;
if (subItem && subItem.length) {
element.children = subItem as RouteRecordRaw[];
return element;
}
// the else logic
if (layer > 1) {
element.children = subItem;
element.children = subItem as RouteRecordRaw[];
return element;
}

View File

@ -48,7 +48,7 @@
import { PropType, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useTabBarStore } from '@/store';
import type { TagProps } from '@/store/modules/tab-bar/types';
import type { TabProps } from '@/store/modules/tab-bar/types';
import { DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants';
// eslint-disable-next-line no-shadow
@ -63,7 +63,7 @@
const props = defineProps({
itemData: {
type: Object as PropType<TagProps>,
type: Object as PropType<TabProps>,
default() {
return [];
},
@ -78,7 +78,7 @@
const route = useRoute();
const tabBarStore = useTabBarStore();
const goto = (tag: TagProps) => {
const goto = (tag: TabProps) => {
router.push({ ...tag });
};
const tabList = computed(() => {
@ -101,7 +101,7 @@
return props.index === tabList.value.length - 1;
});
const tagClose = (tag: TagProps, idx: number) => {
const tagClose = (tag: TabProps, idx: number) => {
tabBarStore.deleteTag(idx, tag);
if (props.itemData.fullPath === route.fullPath) {
const latest = tabList.value[idx - 1]; // tab

View File

@ -1,6 +1,11 @@
import { DirectiveBinding } from 'vue';
import { useUserStore } from '@/store';
/**
* ,TODO:权限判定按权限点来
* @param el dom
* @param binding vue
*/
function checkPermission(el: HTMLElement, binding: DirectiveBinding) {
const { value } = binding;
const userStore = useUserStore();

View File

@ -1,5 +1,5 @@
/**
* @description: Request result set
*
*/
export enum ResultEnum {
SUCCESS = 0,
@ -9,7 +9,7 @@ export enum ResultEnum {
}
/**
* @description: request method
*
*/
export enum RequestEnum {
GET = 'GET',
@ -19,7 +19,7 @@ export enum RequestEnum {
}
/**
* @description: contentTyp
*
*/
export enum ContentTypeEnum {
// json

View File

@ -10,6 +10,9 @@ interface optionsFn {
(isDark: boolean): EChartsOption;
}
/**
* echarts
*/
export default function useChartOption(sourceOption: optionsFn) {
const appStore = useAppStore();
const isDark = computed(() => {

View File

@ -11,22 +11,12 @@ type I18nGlobalTranslation = {
type I18nTranslationRestParameters = [string, any];
function getKey(namespace: string | undefined, key: string) {
if (!namespace) {
return key;
}
if (key.startsWith(namespace)) {
return key;
}
return `${namespace}.${key}`;
}
export function useI18n(namespace?: string): {
t: I18nGlobalTranslation;
} {
const normalFn = {
t: (key: string) => {
return getKey(namespace, key);
return key;
},
};
@ -40,7 +30,7 @@ export function useI18n(namespace?: string): {
if (!key) return '';
if (!key.includes('.') && !namespace) return key;
// @ts-ignore
return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters));
return t(key, ...(arg as I18nTranslationRestParameters));
};
return {
...methods,

View File

@ -1,5 +1,10 @@
import { ref } from 'vue';
/**
* loading显示隐藏
* @param initValue
* @returns
*/
export default function useLoading(initValue = false) {
const loading = ref(initValue);
const setLoading = (value: boolean) => {

View File

@ -1,9 +1,18 @@
import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
import { useUserStore } from '@/store';
/**
*
* @returns
*/
export default function usePermission() {
const userStore = useUserStore();
return {
/**
* 访
* @param route
* @returns
*/
accessRouter(route: RouteLocationNormalized | RouteRecordRaw) {
return (
route.meta?.requiresAuth === false ||
@ -12,6 +21,12 @@ export default function usePermission() {
route.meta?.roles?.includes(userStore.role)
);
},
/**
* 访
* @param _routers
* @param role
* @returns or null
*/
findFirstPermissionRoute(_routers: any, role = 'admin') {
const cloneRouters = [..._routers];
while (cloneRouters.length) {

View File

@ -5,13 +5,24 @@ import { addEventListen, removeEventListen } from '@/utils/event';
const WIDTH = 992; // https://arco.design/vue/component/grid#responsivevalue
/**
*
* @returns
*/
function queryDevice() {
const rect = document.body.getBoundingClientRect();
return rect.width - 1 < WIDTH;
}
/**
*
* @param immediate
*/
export default function useResponsive(immediate?: boolean) {
const appStore = useAppStore();
/**
*
*/
function resizeHandler() {
if (!document.hidden) {
const isMobile = queryDevice();

View File

@ -1,6 +1,10 @@
import { computed } from 'vue';
import { useAppStore } from '@/store';
/**
*
* @returns
*/
export default function useThemes() {
const appStore = useAppStore();
const isDark = computed(() => {

View File

@ -3,6 +3,10 @@ import { Message } from '@arco-design/web-vue';
import { useUserStore } from '@/store';
import { useI18n } from '@/hooks/useI18n';
/**
*
* @returns
*/
export default function useUser() {
const router = useRouter();
const userStore = useUserStore();

View File

@ -1,5 +1,10 @@
import { ref } from 'vue';
/**
*
* @param initValue
* @returns
*/
export default function useVisible(initValue = false) {
const visible = ref(initValue);
const setVisible = (value: boolean) => {

View File

@ -1,6 +1,7 @@
<template>
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in" appear>
<!-- transition内必须有且只有一个根元素不然会导致二级路由的组件无法渲染 -->
<div>
<component :is="Component" v-if="route.meta.ignoreCache" :key="route.fullPath" />
<keep-alive v-else :include="cacheList">

View File

@ -1,4 +1,4 @@
import { computed, unref } from 'vue';
import { unref, ref } from 'vue';
import dayjs from 'dayjs';
import { i18n } from '@/locale';
import { setHtmlPageLang, loadLocalePool } from '@/locale/helper';
@ -11,6 +11,10 @@ interface LangModule {
dayjsLocaleName: string;
}
/**
*
* @param locale
*/
function setI18nLanguage(locale: LocaleType) {
if (i18n.mode === 'legacy') {
i18n.global.locale = locale;
@ -21,6 +25,11 @@ function setI18nLanguage(locale: LocaleType) {
setHtmlPageLang(locale);
}
/**
*
* @param locale
* @returns
*/
async function changeLocale(locale: LocaleType) {
const globalI18n = i18n.global;
const currentLocale = unref(globalI18n.locale);
@ -47,9 +56,7 @@ async function changeLocale(locale: LocaleType) {
export default function useLocale() {
const { locale } = i18n.global;
const currentLocale = computed(() => {
return locale;
});
const currentLocale = ref(locale);
return {
currentLocale,

View File

@ -2,6 +2,7 @@ import { createApp } from 'vue';
import FormCreate from '@form-create/arco-design';
import ArcoVueIcon from '@arco-design/web-vue/es/icon';
import SvgIcon from '@/components/svg-icon/index.vue';
import Breadcrumb from '@/components/breadcrumb/index.vue';
import router from './router';
import store from './store';
import { setupI18n } from './locale';
@ -24,6 +25,7 @@ async function bootstrap() {
app.use(ArcoVueIcon);
app.component('SvgIcon', SvgIcon);
app.component('Breadcrumb', Breadcrumb);
app.use(router);
app.use(directive);

View File

@ -1,3 +1,4 @@
// 请求返回结构
export default interface CommonReponse<T> {
code: number;
message: string;

View File

@ -1,5 +0,0 @@
export type HttpResponse = {
data: any;
code: number;
};
export default {};

View File

@ -1,8 +1,10 @@
// 登录信息
export interface LoginData {
username: string;
password: string;
}
// 登录返回
export interface LoginRes {
token: string;
}

View File

@ -2,6 +2,7 @@ import appRoutes from '../routes';
const mixinRoutes = [...appRoutes];
// 菜单信息根据路由配置推导
const appClientMenus = mixinRoutes.map((el) => {
const { name, path, meta, redirect, children } = el;
return {

View File

@ -11,7 +11,10 @@ function setupPageGuard(router: Router) {
}
export default function createRouteGuard(router: Router) {
// 设置路由监听守卫
setupPageGuard(router);
// 设置用户登录校验守卫
setupUserLoginInfoGuard(router);
// 设置菜单权限守卫
setupPermissionGuard(router);
}

View File

@ -16,6 +16,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
await userStore.info();
next();
} catch (error) {
// 获取用户信息错误则先退出,重定向到登录页
await userStore.logout();
next({
name: 'login',
@ -27,6 +28,7 @@ export default function setupUserLoginInfoGuard(router: Router) {
}
}
} else {
// 未登录的都直接跳转至登录页,访问的页面地址缓存到 query 上
if (to.name === 'login') {
next();
return;

View File

@ -1,28 +0,0 @@
import { DEFAULT_LAYOUT } from '../base';
import { AppRouteRecordRaw } from '../types';
const MINDER: AppRouteRecordRaw = {
path: '/minder',
name: 'minder',
component: DEFAULT_LAYOUT,
redirect: '/minder/index',
meta: {
locale: 'menu.minder',
icon: 'icon-dashboard',
order: 0,
hideChildrenInMenu: true,
},
children: [
{
path: 'index',
name: 'minder',
component: () => import('@/views/minder/index.vue'),
meta: {
locale: 'menu.minder',
roles: ['*'],
},
},
],
};
export default MINDER;

View File

@ -25,13 +25,19 @@ const useAppStore = defineStore('app', {
},
actions: {
// Update app settings
/**
*
* @param partial
*/
updateSettings(partial: Partial<AppState>) {
// @ts-ignore-next-line
this.$patch(partial);
},
// Change theme color
/**
*
* @param dark
*/
toggleTheme(dark: boolean) {
if (dark) {
this.theme = 'dark';
@ -41,12 +47,23 @@ const useAppStore = defineStore('app', {
document.body.removeAttribute('MS-theme');
}
},
/**
*
* @param device mobile | desktop
*/
toggleDevice(device: string) {
this.device = device;
},
/**
*
* @param value
*/
toggleMenu(value: boolean) {
this.hideMenu = value;
},
/**
*
*/
async fetchServerMenuConfig() {
let notifyInstance: NotificationReturn | null = null;
try {
@ -71,6 +88,9 @@ const useAppStore = defineStore('app', {
});
}
},
/**
*
*/
clearServerMenu() {
this.serverMenu = [];
},

View File

@ -2,9 +2,9 @@ import type { RouteLocationNormalized } from 'vue-router';
import { defineStore } from 'pinia';
import { DEFAULT_ROUTE, DEFAULT_ROUTE_NAME, REDIRECT_ROUTE_NAME } from '@/router/constants';
import { isString } from '@/utils/is';
import { TabBarState, TagProps } from './types';
import { TabBarState, TabProps } from './types';
const formatTag = (route: RouteLocationNormalized): TagProps => {
const formatTag = (route: RouteLocationNormalized): TabProps => {
const { name, meta, fullPath, query } = route;
return {
title: meta.locale || '',
@ -24,7 +24,7 @@ const useAppStore = defineStore('tabBar', {
}),
getters: {
getTabList(): TagProps[] {
getTabList(): TabProps[] {
return this.tabList;
},
getCacheList(): string[] {
@ -33,6 +33,11 @@ const useAppStore = defineStore('tabBar', {
},
actions: {
/**
* tabs
* @param route
* @returns void
*/
updateTabList(route: RouteLocationNormalized) {
if (BAN_LIST.includes(route.name as string)) return;
this.tabList.push(formatTag(route));
@ -40,18 +45,35 @@ const useAppStore = defineStore('tabBar', {
this.cacheTabList.add(route.name as string);
}
},
deleteTag(idx: number, tag: TagProps) {
/**
* tab
* @param idx
* @param tag
*/
deleteTag(idx: number, tag: TabProps) {
this.tabList.splice(idx, 1);
this.cacheTabList.delete(tag.name);
},
/**
*
* @param name
*/
addCache(name: string) {
if (isString(name) && name !== '') this.cacheTabList.add(name);
},
deleteCache(tag: TagProps) {
/**
*
* @param tag
*/
deleteCache(tag: TabProps) {
this.cacheTabList.delete(tag.name);
},
freshTabList(tags: TagProps[]) {
this.tabList = tags;
/**
* tabs
* @param tabs tabs
*/
freshTabList(tabs: TabProps[]) {
this.tabList = tabs;
this.cacheTabList.clear();
// 要先判断ignoreCache
this.tabList
@ -59,6 +81,9 @@ const useAppStore = defineStore('tabBar', {
.map((el) => el.name)
.forEach((x) => this.cacheTabList.add(x));
},
/**
* tabs
*/
resetTabList() {
this.tabList = [DEFAULT_ROUTE];
this.cacheTabList.clear();

View File

@ -1,4 +1,4 @@
export interface TagProps {
export interface TabProps {
title: string;
name: string;
fullPath: string;
@ -7,6 +7,6 @@ export interface TagProps {
}
export interface TabBarState {
tabList: TagProps[];
tabList: TabProps[];
cacheTabList: Set<string>;
}

View File

@ -40,24 +40,24 @@ const useUserStore = defineStore('user', {
resolve(this.role);
});
},
// Set user's information
// 设置用户信息
setInfo(partial: Partial<UserState>) {
this.$patch(partial);
},
// Reset user's information
// 重置用户信息
resetInfo() {
this.$reset();
},
// Get user's information
// 获取用户信息
async info() {
const res = await getUserInfo();
this.setInfo(res);
},
// Login
// 登录
async login(loginForm: LoginData) {
try {
const res = await userLogin(loginForm);
@ -67,6 +67,7 @@ const useUserStore = defineStore('user', {
throw err;
}
},
// 登出回调
logoutCallBack() {
const appStore = useAppStore();
this.resetInfo();
@ -74,7 +75,7 @@ const useUserStore = defineStore('user', {
removeRouteListener();
appStore.clearServerMenu();
},
// Logout
// 登出
async logout() {
try {
await userLogout();

View File

@ -1,3 +1,10 @@
/**
*
* @param target DOM
* @param event
* @param handler
* @param capture
*/
export function addEventListen(
target: Window | HTMLElement,
event: string,
@ -9,6 +16,13 @@ export function addEventListen(
}
}
/**
*
* @param target DOM
* @param event
* @param handler
* @param capture
*/
export function removeEventListen(
target: Window | HTMLElement,
event: string,

View File

@ -2,6 +2,11 @@ import { isObject } from './is';
type TargetContext = '_self' | '_parent' | '_blank' | '_top';
/**
*
* @param url
* @param opts
*/
export const openWindow = (url: string, opts?: { target?: TargetContext; [key: string]: any }) => {
const { target = '_blank', ...others } = opts || {};
window.open(
@ -16,11 +21,20 @@ export const openWindow = (url: string, opts?: { target?: TargetContext; [key: s
);
};
/**
* url
*/
export const regexUrl = new RegExp(
'^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$',
'i'
);
/**
* url
* @param baseUrl url
* @param obj
* @returns url
*/
export function setObjToUrlParams(baseUrl: string, obj: any): string {
let parameters = '';
Object.keys(obj).forEach((key) => {
@ -30,6 +44,12 @@ export function setObjToUrlParams(baseUrl: string, obj: any): string {
return /\?$/.test(baseUrl) ? baseUrl + parameters : baseUrl.replace(/\/?$/, '?') + parameters;
}
/**
*
* @param src
* @param target
* @returns
*/
export const deepMerge = <T = any>(src: any = {}, target: any = {}): T => {
Object.keys(target).forEach((key) => {
src[key] = isObject(src[key]) ? deepMerge(src[key], target[key]) : (src[key] = target[key]);

View File

@ -1,21 +0,0 @@
/**
*
* @param target
* @param source
* @returns {Object}
*/
function merge(target: any, source: any): any {
target = target || {};
Object.keys(source).forEach((key) => {
if (Object.prototype.hasOwnProperty.call(source, key)) {
const obj = source[key];
if (Object.prototype.toString.call(obj) === '[object Object]') {
target[key] = merge(target[key], obj);
} else {
target[key] = obj;
}
}
});
return target;
}
export default merge;

View File

@ -1,6 +1,12 @@
import { App, ComponentPublicInstance } from 'vue';
import axios from 'axios';
/**
* TODO:错误捕获
* @param Vue
* @param baseUrl
* @returns
*/
export default function handleError(Vue: App, baseUrl: string) {
if (!baseUrl) {
return;

View File

@ -1,5 +1,4 @@
/**
* Listening to routes alone would waste rendering performance. Use the publish-subscribe model for distribution management
* 使
*/
import mitt, { Handler } from 'mitt';
@ -11,11 +10,20 @@ const key = Symbol('ROUTE_CHANGE');
let latestRoute: RouteLocationNormalized;
/**
*
* @param to
*/
export function setRouteEmitter(to: RouteLocationNormalized) {
emitter.emit(key, to);
latestRoute = to;
}
/**
*
* @param handler
* @param immediate
*/
export function listenerRouteChange(handler: (route: RouteLocationNormalized) => void, immediate = true) {
emitter.on(key, handler as Handler);
if (immediate && latestRoute) {
@ -23,6 +31,9 @@ export function listenerRouteChange(handler: (route: RouteLocationNormalized) =>
}
}
/**
*
*/
export function removeRouteListener() {
emitter.off(key);
}

View File

@ -4,6 +4,11 @@ export default ({ mock, setup }: { mock?: boolean; setup: () => void }) => {
if (mock !== false && debug) setup();
};
/**
* mock-
* @param data mock
* @returns
*/
export const successResponseWrap = (data: unknown) => {
return {
data,
@ -13,6 +18,13 @@ export const successResponseWrap = (data: unknown) => {
};
};
/**
* mock-
* @param data mock
* @param message
* @param code
* @returns
*/
export const failResponseWrap = (data: unknown, message: string, code = 50000) => {
return {
data,

View File

@ -11,6 +11,7 @@
"build/**/*.ts",
"build/**/*.d.ts",
"mock/**/*.ts",
"__test__/**/*.ts",
], // TS
"exclude": [
"node_modules"