区域组件,axios
This commit is contained in:
parent
19c09a116b
commit
ac90e6984a
@ -42,7 +42,7 @@
|
||||
"element-resize-detector": "^1.2.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"naive-ui": "^2.39.0",
|
||||
"naive-ui": "^2.40.0",
|
||||
"perfect-scrollbar": "^1.5.5",
|
||||
"pinia": "^2.2.2",
|
||||
"print-js": "^1.6.0",
|
||||
|
@ -6,5 +6,8 @@ export function upload(data) {
|
||||
url: '/upload/uploadFile',
|
||||
method: 'post',
|
||||
data,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
115
src/components/ChinaArea/index.vue
Normal file
115
src/components/ChinaArea/index.vue
Normal file
@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<n-cascader label-field="name" value-field="areaCode" :options="optionsData" placeholder="请选择所属区域" remote ref="cascaderInstRef"
|
||||
:on-load="loadData" clearable :disabled="disabled" v-model:value="selectedOptions" check-strategy="child" show-path
|
||||
@update:value="handleUpdateValue"/>
|
||||
</template>
|
||||
<script setup lang="ts" name="china-area">
|
||||
import { ref, onMounted, computed, watch } from 'vue'
|
||||
import { getCityByList } from "@/api/system/user";
|
||||
import type { CascaderOption } from 'naive-ui'
|
||||
const emit = defineEmits(['update:modelValue', 'change']);
|
||||
const optionsData = ref([]);
|
||||
const tempData=ref([])
|
||||
const cascaderInstRef = ref()
|
||||
const props = defineProps({
|
||||
// 当前的值
|
||||
modelValue: [String,Array],
|
||||
// 类型 (二级联动,三级联动)
|
||||
type: {
|
||||
type: Number,
|
||||
default: 3,
|
||||
},
|
||||
// 是否禁用
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: () => false,
|
||||
},
|
||||
});
|
||||
|
||||
// watch(
|
||||
// () => props,
|
||||
// () => {
|
||||
// props && setData(props.modelValue);
|
||||
// },
|
||||
// {
|
||||
// deep: true,
|
||||
// },
|
||||
// );
|
||||
const getCityData = async (pid: any) => {
|
||||
const data = await getCityByList(pid)
|
||||
data.map(item => {
|
||||
item.isLeaf = false
|
||||
})
|
||||
return data
|
||||
}
|
||||
const changeFlag = ref(false)
|
||||
const selectedOptions = computed({
|
||||
get: () => {
|
||||
return props.modelValue[3];
|
||||
},
|
||||
set: (val) => {
|
||||
emit('update:modelValue', tempData.value);
|
||||
},
|
||||
});
|
||||
const setData = async (data) => {
|
||||
if (changeFlag.value) return
|
||||
let index1 = findListChild(optionsData.value, data[0])
|
||||
let data1 = await getCityByList(data[0])
|
||||
optionsData.value[index1].children = data1
|
||||
|
||||
|
||||
let index2 = findListChild(data1, data[1])
|
||||
let data2 = await getCityByList(data[1])
|
||||
optionsData.value[index1].children[index2].children = data2
|
||||
|
||||
|
||||
let index3 = findListChild(data2, data[2])
|
||||
let data3 = await getCityByList(data[2])
|
||||
optionsData.value[index1].children[index2].children[index3].children = data3
|
||||
|
||||
}
|
||||
|
||||
const findListChild = (list: any, value: any) => {
|
||||
let child = null
|
||||
let index = 0
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].areaCode == value) {
|
||||
child = list[i]
|
||||
index = i
|
||||
break;
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
const loadData = async (option: CascaderOption) => {
|
||||
tempData.value[option.level]=option.areaCode
|
||||
return new Promise<void>(async (resolve) => {
|
||||
const data = await getCityByList(option.areaCode)
|
||||
data.map(item => {
|
||||
item.isLeaf = item.level==3?true:false
|
||||
})
|
||||
option.children = data
|
||||
resolve()
|
||||
})
|
||||
};
|
||||
const handleUpdateValue =(val)=>{
|
||||
changeFlag.value = true
|
||||
tempData.value[3] = val
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
const data = await getCityByList(0)
|
||||
data.map(item => {
|
||||
item.isLeaf = false
|
||||
})
|
||||
optionsData.value = data
|
||||
if(props.modelValue.length>0) {
|
||||
setData(props.modelValue)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="less">
|
||||
.ant-cascader-menu {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
</style>
|
@ -2,14 +2,42 @@
|
||||
<div class="img-cropper" :class="{ 'img-cropper-auto': $slots.default }">
|
||||
<slot name="default"></slot>
|
||||
<template v-if="!$slots.default">
|
||||
<div class="img-cropper-img" @click="openCropper">
|
||||
<!-- <div class="img-cropper-img" @click="openCropper">
|
||||
<img :src="src" :class="{ circled: circled }" />
|
||||
<div class="mask" :class="{ circled: circled }">
|
||||
<n-icon>
|
||||
<UploadOutlined />
|
||||
</n-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<template v-if="src">
|
||||
<div class="img-cropper-img" @click="openCropper">
|
||||
<div class="img-boxs">
|
||||
<img :src="src" :class="{ circled: circled }" />
|
||||
</div>
|
||||
<div class="mask" :class="{ circled: circled }" @click.stop>
|
||||
<div class="handle-icon" @click="openCropper">
|
||||
<span class="icon">
|
||||
<n-icon size="18"><FormOutlined /></n-icon></span>
|
||||
</div>
|
||||
<div class="handle-icon" @click="viewImg">
|
||||
<span class="icon"><n-icon size="18"><ZoomInOutlined /></n-icon></span>
|
||||
</div>
|
||||
<div class="handle-icon" @click="deleteImg">
|
||||
<span class="icon"><n-icon size="18"><DeleteOutlined /></n-icon></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="img-cropper-img" @click="openCropper">
|
||||
<div class="addImg">
|
||||
<span class="icon">
|
||||
<n-icon><PlusOutlined /></n-icon>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<CropperModal
|
||||
ref="cropperRef"
|
||||
@ -17,6 +45,7 @@
|
||||
subBtuText="确认上传"
|
||||
:uploadApi="uploadApi"
|
||||
:circled="circled"
|
||||
:name="name"
|
||||
@upload-success="uploadSuccess"
|
||||
/>
|
||||
</div>
|
||||
@ -25,7 +54,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import CropperModal from './CropperModal.vue';
|
||||
import { UploadOutlined } from '@vicons/antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
ZoomInOutlined,
|
||||
DeleteOutlined,
|
||||
FormOutlined,
|
||||
UploadOutlined
|
||||
} from '@vicons/antd';
|
||||
import { cssUnit } from '@/utils';
|
||||
|
||||
const cropperRef = ref();
|
||||
@ -36,7 +71,8 @@
|
||||
title: { type: String, default: '图片上传' },
|
||||
src: { type: String, required: true },
|
||||
circled: { type: Boolean, default: false },
|
||||
width: { type: [String, Number], default: 200 },
|
||||
width: { type: [String, Number], default: 120 },
|
||||
name: { type: String, default: 'name' },
|
||||
uploadApi: {
|
||||
type: Function as PropType<(params) => Promise<any>>,
|
||||
},
|
||||
@ -54,6 +90,13 @@
|
||||
emit('uploadSuccess', result);
|
||||
}
|
||||
|
||||
const viewImg = () => {
|
||||
window.open(props.src);
|
||||
};
|
||||
const deleteImg = () => {
|
||||
emit('uploadSuccess', { fileUrl: '' });
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openCropper,
|
||||
});
|
||||
@ -72,7 +115,22 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
|
||||
.img-boxs {
|
||||
border: 1px dashed #999999;
|
||||
padding: 5px;
|
||||
}
|
||||
.addImg {
|
||||
width: v-bind(getWidth);
|
||||
height: v-bind(getWidth);
|
||||
border: 1px dashed #999999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.icon {
|
||||
color: #999999;
|
||||
font-size: v-bind(iconSize);
|
||||
}
|
||||
}
|
||||
.mask {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@ -86,11 +144,19 @@
|
||||
top: 0;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
|
||||
.n-icon {
|
||||
color: #fff;
|
||||
font-size: v-bind(iconSize);
|
||||
.handle-icon {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 6%;
|
||||
color: aliceblue;
|
||||
.n-icon {
|
||||
color: #fff;
|
||||
font-size: v-bind(iconSize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<basicModal ref="modalRef" class="cropperModal" @register="modalRegister" @on-ok="handleOk">
|
||||
<basicModal ref="modalRef" class="cropperModal" @register="modalRegister" @on-ok="handleOk" @on-close="handleClose">
|
||||
<template #default>
|
||||
<div class="cropper-box">
|
||||
<div class="cropper-box-left">
|
||||
@ -184,7 +184,7 @@
|
||||
import { useMessage } from 'naive-ui';
|
||||
import { basicModal, useModal } from '@/components/Modal';
|
||||
import CropperImage from './CropperImage.vue';
|
||||
import { dataURLtoBlob } from '@/utils/file/base64Conver';
|
||||
import { dataURLtoBlob,base64ToFile } from '@/utils/file/base64Conver';
|
||||
import { isFunction } from '@/utils/is';
|
||||
import {
|
||||
UploadOutlined,
|
||||
@ -200,6 +200,7 @@
|
||||
const props = defineProps({
|
||||
title: { type: String, default: '图片上传' },
|
||||
circled: { type: Boolean, default: false },
|
||||
name: { type: String, default: 'name' },
|
||||
uploadApi: {
|
||||
type: Function as PropType<(params) => Promise<any>>,
|
||||
},
|
||||
@ -261,18 +262,28 @@
|
||||
return message.error('请先上传图片');
|
||||
}
|
||||
const uploadApi = props.uploadApi;
|
||||
const name = props.name
|
||||
if (uploadApi && isFunction(uploadApi)) {
|
||||
const blob = dataURLtoBlob(previewSource.value);
|
||||
const file = base64ToFile(previewSource.value, filename);
|
||||
console.log(file)
|
||||
try {
|
||||
setSubLoading(true);
|
||||
const result = await uploadApi({ name: 'file', file: blob, filename });
|
||||
emit('uploadSuccess', { source: previewSource.value, data: result.data });
|
||||
const formData = new window.FormData();
|
||||
formData.append('file', file);
|
||||
formData.append('name', name);
|
||||
console.log(formData)
|
||||
const result = await uploadApi(formData);
|
||||
emit('uploadSuccess', result);
|
||||
closeModal();
|
||||
} finally {
|
||||
setSubLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleClose() {
|
||||
src.value = '';
|
||||
previewSource.value = '';
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
showModal,
|
||||
|
@ -2,9 +2,9 @@
|
||||
* @description: 请求结果集
|
||||
*/
|
||||
export enum ResultEnum {
|
||||
SUCCESS = 200,
|
||||
ERROR = -1,
|
||||
TIMEOUT = 10042,
|
||||
SUCCESS = 0,
|
||||
ERROR = 1,
|
||||
TIMEOUT = 401,
|
||||
TYPE = 'success',
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,6 @@ export enum PageEnum {
|
||||
REDIRECT_NAME = 'Redirect',
|
||||
// 首页
|
||||
BASE_HOME = '/dashboard',
|
||||
//首页跳转默认路由
|
||||
BASE_HOME_REDIRECT = '/dashboard/console',
|
||||
// 错误
|
||||
ERROR_PAGE_NAME = 'ErrorPage',
|
||||
}
|
||||
|
@ -8,9 +8,12 @@ export function usePermission() {
|
||||
* @param accesses
|
||||
*/
|
||||
function _somePermissions(accesses: string[]) {
|
||||
return userStore.getPermissions.some((item: any) => {
|
||||
const value = item?.value ?? item;
|
||||
return accesses.includes(value);
|
||||
const permissionsList = userStore.getPermissions;
|
||||
if(permissionsList[0]=='*:*:*') {
|
||||
return true
|
||||
}
|
||||
return accesses.some((item) => {
|
||||
return permissionsList.includes(item);
|
||||
});
|
||||
}
|
||||
|
||||
@ -29,8 +32,11 @@ export function usePermission() {
|
||||
*/
|
||||
function hasEveryPermission(accesses: string[]): boolean {
|
||||
const permissionsList = userStore.getPermissions;
|
||||
if(permissionsList[0]=='*:*:*') {
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(accesses)) {
|
||||
return permissionsList.every((access: any) => accesses.includes(access.value));
|
||||
return accesses.every((access: any) => permissionsList.includes(access));
|
||||
}
|
||||
throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
|
||||
}
|
||||
@ -42,8 +48,11 @@ export function usePermission() {
|
||||
*/
|
||||
function hasSomePermission(accesses: string[]): boolean {
|
||||
const permissionsList = userStore.getPermissions;
|
||||
if(permissionsList[0]=='*:*:*') {
|
||||
return true
|
||||
}
|
||||
if (Array.isArray(accesses)) {
|
||||
return permissionsList.some((access: any) => accesses.includes(access.value));
|
||||
return accesses.some((access: any) => permissionsList.includes(access));
|
||||
}
|
||||
throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
|
||||
}
|
||||
|
@ -120,7 +120,7 @@
|
||||
<div class="layout-header-trigger layout-header-trigger-min">
|
||||
<n-dropdown :options="avatarOptions" trigger="hover" @select="avatarSelect">
|
||||
<div class="avatar">
|
||||
<n-avatar round :src="schoolboy" />
|
||||
<n-avatar round :src="userAvatar?userAvatar:schoolboy" />
|
||||
</div>
|
||||
</n-dropdown>
|
||||
</div>
|
||||
@ -155,7 +155,8 @@
|
||||
import { computed, ref, unref, watch, inject } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { useDialog, useMessage } from 'naive-ui';
|
||||
import { TABS_ROUTES } from '@/store/mutation-types';
|
||||
import { useTabsViewStore } from '@/store/modules/tabsView';
|
||||
import { TABS_ROUTES,FIRST_ROUTE } from '@/store/mutation-types';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||
import { AsideMenu } from '@/layout/components/Menu';
|
||||
@ -210,7 +211,7 @@
|
||||
const drawerSetting = ref();
|
||||
|
||||
const amendPwdRef = ref();
|
||||
|
||||
const userAvatar = userStore.getAvatar
|
||||
// const username = userStore?.info ? ref(userStore?.info.username) : '';
|
||||
|
||||
const collapsed = inject('collapsed');
|
||||
@ -238,6 +239,7 @@
|
||||
const route = useRoute();
|
||||
const { isFullscreen, toggle } = useFullscreen();
|
||||
const asyncRouteStore = useAsyncRouteStore();
|
||||
const tabsViewStore = useTabsViewStore();
|
||||
|
||||
const generator: any = (routerMap) => {
|
||||
return routerMap
|
||||
@ -295,7 +297,9 @@
|
||||
userStore.logout().then(() => {
|
||||
message.success('成功退出登录');
|
||||
// 移除标签页
|
||||
tabsViewStore.closeAllTabs();
|
||||
localStorage.removeItem(TABS_ROUTES);
|
||||
localStorage.removeItem(FIRST_ROUTE);
|
||||
asyncRouteStore.setDynamicAddedRoute(false);
|
||||
router.replace({
|
||||
name: BASE_LOGIN_NAME,
|
||||
@ -323,13 +327,13 @@
|
||||
click: () => openAppSearch(),
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: LockOutlined,
|
||||
tips: '锁屏',
|
||||
eventObject: {
|
||||
click: () => useLockscreen.setLock(true),
|
||||
},
|
||||
},
|
||||
// {
|
||||
// icon: LockOutlined,
|
||||
// tips: '锁屏',
|
||||
// eventObject: {
|
||||
// click: () => useLockscreen.setLock(true),
|
||||
// },
|
||||
// },
|
||||
];
|
||||
const avatarOptions = [
|
||||
{
|
||||
@ -353,7 +357,7 @@
|
||||
const avatarSelect = (key) => {
|
||||
switch (key) {
|
||||
case 1:
|
||||
router.push({ name: 'Setting' });
|
||||
router.push({ name: 'setting' });
|
||||
break;
|
||||
case 2:
|
||||
doLogout();
|
||||
|
@ -74,7 +74,7 @@
|
||||
import { computed, ref, unref, provide, watch, onMounted, nextTick, toRaw, inject } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { TABS_ROUTES } from '@/store/mutation-types';
|
||||
import { TABS_ROUTES,FIRST_ROUTE } from '@/store/mutation-types';
|
||||
import { useTabsViewStore } from '@/store/modules/tabsView';
|
||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||
import { RouteItem } from '@/store/modules/tabsView';
|
||||
@ -335,7 +335,7 @@
|
||||
// 关闭全部
|
||||
const closeAll = () => {
|
||||
tabsViewStore.closeAllTabs();
|
||||
router.replace(PageEnum.BASE_HOME_REDIRECT);
|
||||
router.replace(storage.get(FIRST_ROUTE));
|
||||
updateNavScroll();
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,16 @@
|
||||
import { adminMenus } from '@/api/system/menu';
|
||||
import { constantRouterIcon } from './router-icons';
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout, ParentLayout } from '@/router/constant';
|
||||
import type { AppRouteRecordRaw } from '@/router/types';
|
||||
import { ProfileOutlined } from '@ant-design/icons-vue';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const Iframe = () => import('@/views/iframe/index.vue');
|
||||
const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();
|
||||
|
||||
LayoutMap.set('LAYOUT', Layout);
|
||||
LayoutMap.set('IFRAME', Iframe);
|
||||
LayoutMap.set('ParentLayout', ParentLayout);
|
||||
|
||||
/**
|
||||
* 格式化 后端 结构信息并递归生成层级路由表
|
||||
@ -16,34 +18,81 @@ LayoutMap.set('ParentLayout', ParentLayout);
|
||||
* @param parent
|
||||
* @returns {*}
|
||||
*/
|
||||
export const routerGenerator = (routerMap, parent?): AppRouteRecordRaw[] => {
|
||||
export const routerGenerator = (routerMap): any[] => {
|
||||
return routerMap.map((item) => {
|
||||
const names = /http(s)?:/.test(item.component)?item.component:item.path.replaceAll('/','')
|
||||
item.meta = {
|
||||
title:item.parentId==0 && item.children.length==0?'':item.name,
|
||||
icon:constantRouterIcon[item.icon2]|| null,
|
||||
sort:item.sort,
|
||||
permissions:item.permission,
|
||||
hidden: item.hide?true:false,
|
||||
isRoot:item.parentId==0 && item.children.length==0?true:false,
|
||||
alwaysShow: item.parentId==0 && item.children.length==0?true:false,
|
||||
frameSrc: item.target==1?item.component:'',
|
||||
target:item.target==2?true:false
|
||||
}
|
||||
let components = ''
|
||||
if(item.parentId==0 && (item.children.length==0 || item.children.length>0) ) {
|
||||
components ='LAYOUT'
|
||||
} else if(item.target==0) {
|
||||
components = item.component
|
||||
} else if(item.target==1) {
|
||||
components ='IFRAME'
|
||||
}
|
||||
const currentRouter: any = {
|
||||
// 路由地址 动态拼接生成如 /dashboard/workplace
|
||||
path: `${(parent && parent.path) || ''}/${item.path}`,
|
||||
path:item.target==2?'':item.path,
|
||||
// 路由名称,建议唯一
|
||||
name: item.name || '',
|
||||
name: names,
|
||||
// 该路由对应页面的 组件
|
||||
component: item.component,
|
||||
component: components,
|
||||
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
|
||||
meta: {
|
||||
...item.meta,
|
||||
label: item.meta.title,
|
||||
icon: constantRouterIcon[item.meta.icon] || null,
|
||||
permissions: item.meta.permissions || null,
|
||||
},
|
||||
};
|
||||
|
||||
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
|
||||
currentRouter.path = currentRouter.path.replace('//', '/');
|
||||
// currentRouter.path = currentRouter.path.replace('//', '/');
|
||||
// 重定向
|
||||
item.redirect && (currentRouter.redirect = item.redirect);
|
||||
// 是否有子菜单,并递归处理
|
||||
if (item.children && item.children.length > 0) {
|
||||
//如果未定义 redirect 默认第一个子路由为 redirect
|
||||
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
|
||||
!item.redirect && (currentRouter.redirect = `${item.children[0].path}`);
|
||||
// Recursion
|
||||
currentRouter.children = routerGenerator(item.children, currentRouter);
|
||||
} else {
|
||||
if(item.parentId==0 && item.children.length==0) {
|
||||
currentRouter.children =[]
|
||||
if(item.target==1 && (/http(s)?:/.test(item.component))){
|
||||
currentRouter.children.push({
|
||||
path: item.path,
|
||||
name: names,
|
||||
meta: {
|
||||
title: item.name,
|
||||
frameSrc: item.component,
|
||||
icon:constantRouterIcon[item.icon2],
|
||||
hidden: item.hide?true:false,
|
||||
},
|
||||
component: 'IFRAME',
|
||||
})
|
||||
} else {
|
||||
currentRouter.children.push({
|
||||
path: item.path,
|
||||
name: names,
|
||||
meta: {
|
||||
title: item.name,
|
||||
icon:constantRouterIcon[item.icon2],
|
||||
activeMenu: names,
|
||||
target:item.target==2?true:false,
|
||||
hidden: item.hide?true:false,
|
||||
},
|
||||
component: item.component,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentRouter;
|
||||
});
|
||||
@ -53,11 +102,11 @@ export const routerGenerator = (routerMap, parent?): AppRouteRecordRaw[] => {
|
||||
* 动态生成菜单
|
||||
* @returns {Promise<Router>}
|
||||
*/
|
||||
export const generatorDynamicRouter = (): Promise<AppRouteRecordRaw[]> => {
|
||||
export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
adminMenus()
|
||||
.then((result) => {
|
||||
const routeList = routerGenerator(result);
|
||||
const routeList = routerGenerator(result)
|
||||
asyncImportRoute(routeList);
|
||||
resolve(routeList);
|
||||
})
|
||||
@ -119,3 +168,9 @@ export const dynamicImport = (
|
||||
return;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 查找第一个路由
|
||||
* */
|
||||
export const findFirstRoutePath = (routes)=>{
|
||||
return routes.length > 0?(routes[0].redirect?routes[0].redirect:routes[0].path):''
|
||||
}
|
||||
|
@ -9,6 +9,11 @@ const modules: any = import.meta.glob('./modules/**/*.ts', { eager: true });
|
||||
|
||||
const routeModuleList: RouteRecordRaw[] = [];
|
||||
|
||||
const modulesVue = import.meta.glob('/src/views/**/*.vue')
|
||||
export function getModulesKey() {
|
||||
return Object.keys(modulesVue).map((item) => item.replace('/src/views', '').replace('.vue', ''));
|
||||
}
|
||||
|
||||
Object.keys(modules).forEach((key) => {
|
||||
const mod = modules[key].default || {};
|
||||
const modList = Array.isArray(mod) ? [...mod] : [mod];
|
||||
@ -41,8 +46,8 @@ export const RootRoute: RouteRecordRaw = {
|
||||
export const LoginRoute: RouteRecordRaw = {
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
// component: () => import('@/views/login/index.vue'), //v1.x 模板
|
||||
component: () => import('@/views/login/newLogin.vue'), // 2.x新模板
|
||||
component: () => import('@/views/login/index.vue'), //v1.x 模板
|
||||
// component: () => import('@/views/login/newLogin.vue'), // 2.x新模板
|
||||
meta: {
|
||||
title: '登录',
|
||||
},
|
||||
|
@ -1,32 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { ProjectOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/about',
|
||||
name: 'about',
|
||||
component: Layout,
|
||||
meta: {
|
||||
sort: 11,
|
||||
isRoot: true,
|
||||
activeMenu: 'about_index',
|
||||
alwaysShow: true,
|
||||
icon: renderIcon(ProjectOutlined),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
name: `about_index`,
|
||||
meta: {
|
||||
title: '关于项目',
|
||||
activeMenu: 'about_index',
|
||||
},
|
||||
component: () => import('@/views/about/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,177 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout, ParentLayout } from '@/router/constant';
|
||||
import { WalletOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils';
|
||||
|
||||
const routeName = 'comp';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
*
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/comp',
|
||||
name: routeName,
|
||||
component: Layout,
|
||||
redirect: '/comp/table',
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
icon: renderIcon(WalletOutlined),
|
||||
sort: 8,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'table',
|
||||
name: `${routeName}_table`,
|
||||
redirect: '/comp/table/basic',
|
||||
component: ParentLayout,
|
||||
meta: {
|
||||
title: '表格',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic',
|
||||
name: `${routeName}_table_basic`,
|
||||
meta: {
|
||||
title: '基础表格',
|
||||
},
|
||||
component: () => import('@/views/comp/table/basic.vue'),
|
||||
},
|
||||
{
|
||||
path: 'editCell',
|
||||
name: `${routeName}_table_editCell`,
|
||||
meta: {
|
||||
title: '单元格编辑',
|
||||
},
|
||||
component: () => import('@/views/comp/table/editCell.vue'),
|
||||
},
|
||||
{
|
||||
path: 'editRow',
|
||||
name: `${routeName}_table_editRow`,
|
||||
meta: {
|
||||
title: '整行编辑',
|
||||
},
|
||||
component: () => import('@/views/comp/table/editRow.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'form',
|
||||
name: `${routeName}_form`,
|
||||
redirect: '/comp/form/basic',
|
||||
component: ParentLayout,
|
||||
meta: {
|
||||
title: '表单',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic',
|
||||
name: `${routeName}_form_basic`,
|
||||
meta: {
|
||||
title: '基础使用',
|
||||
},
|
||||
component: () => import('@/views/comp/form/basic.vue'),
|
||||
},
|
||||
{
|
||||
path: 'useForm',
|
||||
name: `useForm`,
|
||||
meta: {
|
||||
title: 'useForm',
|
||||
},
|
||||
component: () => import('@/views/comp/form/useForm.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'upload',
|
||||
name: `${routeName}_upload`,
|
||||
meta: {
|
||||
title: '上传图片',
|
||||
},
|
||||
component: () => import('@/views/comp/upload/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'modal',
|
||||
name: `${routeName}_modal`,
|
||||
meta: {
|
||||
title: '弹窗扩展',
|
||||
},
|
||||
component: () => import('@/views/comp/modal/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'richtext',
|
||||
name: `richtext`,
|
||||
meta: {
|
||||
title: '富文本',
|
||||
},
|
||||
component: () => import('@/views/comp/richtext/vue-quill.vue'),
|
||||
},
|
||||
{
|
||||
path: 'drag',
|
||||
name: `Drag`,
|
||||
meta: {
|
||||
title: '拖拽',
|
||||
},
|
||||
component: () => import('@/views/comp/drag/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'region',
|
||||
name: `Region`,
|
||||
meta: {
|
||||
title: '地区',
|
||||
},
|
||||
component: () => import('@/views/comp/region/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'cropper',
|
||||
name: `Cropper`,
|
||||
meta: {
|
||||
title: '图片裁剪',
|
||||
},
|
||||
component: () => import('@/views/comp/cropper/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'qrcode',
|
||||
name: `Qrcode`,
|
||||
meta: {
|
||||
title: '二维码',
|
||||
},
|
||||
component: () => import('@/views/comp/qrcode/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'password',
|
||||
name: `Password`,
|
||||
meta: {
|
||||
title: '密码强度',
|
||||
},
|
||||
component: () => import('@/views/comp/password/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'select',
|
||||
name: `Select`,
|
||||
meta: {
|
||||
title: '选择器',
|
||||
},
|
||||
component: () => import('@/views/comp/select/BasicSelect.vue'),
|
||||
},
|
||||
{
|
||||
path: 'tableselect',
|
||||
name: `TableSelect`,
|
||||
meta: {
|
||||
title: '表格选择器',
|
||||
},
|
||||
component: () => import('@/views/comp/tableSelect/tableSelect.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,67 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { DashboardOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const routeName = 'dashboard';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
* @param meta.tagView 是否显示在多页签中 设置成 false 则不会显示
|
||||
* @param meta.breadcrumbView 是否显示在面包屑 设置成 false 则不会显示
|
||||
* @param meta.authEvery 是否验证每一个权限都包含
|
||||
* @param meta.documentTitle 网页标题 优先 title 字段
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: routeName,
|
||||
redirect: '/dashboard/console',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: 'Dashboard',
|
||||
icon: renderIcon(DashboardOutlined),
|
||||
permissions: ['dashboard_console', 'dashboard_console', 'dashboard_workplace'],
|
||||
sort: 0,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'console',
|
||||
name: `${routeName}_console`,
|
||||
meta: {
|
||||
title: '主控台',
|
||||
permissions: ['dashboard_console'],
|
||||
affix: true,
|
||||
},
|
||||
component: () => import('@/views/dashboard/console/console.vue'),
|
||||
},
|
||||
{
|
||||
path: 'monitor',
|
||||
name: `${routeName}_monitor`,
|
||||
meta: {
|
||||
title: '监控页',
|
||||
},
|
||||
component: () => import('@/views/dashboard/monitor/monitor.vue'),
|
||||
},
|
||||
{
|
||||
path: 'workplace',
|
||||
name: `${routeName}_workplace`,
|
||||
meta: {
|
||||
title: '工作台',
|
||||
keepAlive: true,
|
||||
permissions: ['dashboard_workplace'],
|
||||
},
|
||||
component: () => import('@/views/dashboard/workplace/workplace.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,19 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { DocumentTextOutline } from '@vicons/ionicons5';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/external',
|
||||
name: 'https://www.naiveadmin.com',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
icon: renderIcon(DocumentTextOutline),
|
||||
sort: 11,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,57 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { ExclamationCircleOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
*
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/exception',
|
||||
name: 'Exception',
|
||||
redirect: '/exception/403',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '异常页面',
|
||||
icon: renderIcon(ExclamationCircleOutlined),
|
||||
sort: 3,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '403',
|
||||
name: 'exception-403',
|
||||
meta: {
|
||||
title: '403',
|
||||
},
|
||||
component: () => import('@/views/exception/403.vue'),
|
||||
},
|
||||
{
|
||||
path: '404',
|
||||
name: 'exception-404',
|
||||
meta: {
|
||||
title: '404',
|
||||
},
|
||||
component: () => import('@/views/exception/404.vue'),
|
||||
},
|
||||
{
|
||||
path: '500',
|
||||
name: 'exception-500',
|
||||
meta: {
|
||||
title: '500',
|
||||
},
|
||||
component: () => import('@/views/exception/500.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,103 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout, ParentLayout } from '@/router/constant';
|
||||
import { ControlOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/feature',
|
||||
name: 'Feature',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '功能示例',
|
||||
icon: renderIcon(ControlOutlined),
|
||||
sort: 7,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'authority',
|
||||
name: 'Authority',
|
||||
component: () => import('@/views/feature/authority/authority.vue'),
|
||||
meta: {
|
||||
title: '权限判断',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'download',
|
||||
name: 'Download',
|
||||
component: () => import('@/views/feature/download/download.vue'),
|
||||
meta: {
|
||||
title: '文件下载',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'context-menus',
|
||||
name: 'ContextMenus',
|
||||
component: () => import('@/views/feature/context-menus/context-menus.vue'),
|
||||
meta: {
|
||||
title: '右键菜单',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'copy',
|
||||
name: 'copy',
|
||||
component: () => import('@/views/feature/copy/copy.vue'),
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'print',
|
||||
name: 'print',
|
||||
component: () => import('@/views/feature/print/print.vue'),
|
||||
meta: {
|
||||
title: '打印',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'scrollbar',
|
||||
name: 'scrollbar',
|
||||
component: () => import('@/views/feature/scrollbar/scrollbar.vue'),
|
||||
meta: {
|
||||
title: '滚动条',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'excel',
|
||||
name: 'Excel',
|
||||
meta: {
|
||||
title: 'Excel',
|
||||
},
|
||||
component: ParentLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'choiceExport',
|
||||
name: 'choiceExport',
|
||||
component: () => import('@/views/feature/excel/choiceExport.vue'),
|
||||
meta: {
|
||||
title: '选择导出格式',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'jsonExport',
|
||||
name: 'jsonExport',
|
||||
component: () => import('@/views/feature/excel/jsonExport.vue'),
|
||||
meta: {
|
||||
title: 'JSON数据导出',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'tagsAction',
|
||||
name: 'TagsAction',
|
||||
meta: {
|
||||
title: '多页签操作',
|
||||
},
|
||||
component: () => import('@/views/feature/tags/tagsAction.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,66 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { ProfileOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
*
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/form',
|
||||
name: 'Form',
|
||||
redirect: '/form/basic-form',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '表单页面',
|
||||
icon: renderIcon(ProfileOutlined),
|
||||
sort: 3,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic-form',
|
||||
name: 'BasicForm',
|
||||
meta: {
|
||||
title: '基础表单',
|
||||
keepAlive: true,
|
||||
},
|
||||
component: () => import('@/views/form/basicForm/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'advanced-form',
|
||||
name: 'form-advanced-form',
|
||||
meta: {
|
||||
title: '高级表单',
|
||||
},
|
||||
component: () => import('@/views/form/advancedForm/advancedForm.vue'),
|
||||
},
|
||||
{
|
||||
path: 'step-form',
|
||||
name: 'form-step-form',
|
||||
meta: {
|
||||
title: '分步表单',
|
||||
},
|
||||
component: () => import('@/views/form/stepForm/stepForm.vue'),
|
||||
},
|
||||
{
|
||||
path: 'detail',
|
||||
name: 'form-detail',
|
||||
meta: {
|
||||
title: '表单详情',
|
||||
},
|
||||
component: () => import('@/views/form/detail/index.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,42 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { DesktopOutline } from '@vicons/ionicons5';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const IFrame = () => import('@/views/iframe/index.vue');
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/frame',
|
||||
name: 'Frame',
|
||||
redirect: '/frame/docs',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '外部页面',
|
||||
sort: 9,
|
||||
icon: renderIcon(DesktopOutline),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'docs',
|
||||
name: 'frame-docs',
|
||||
meta: {
|
||||
title: '项目文档(内嵌)',
|
||||
frameSrc: 'https://www.naiveadmin.com',
|
||||
},
|
||||
component: IFrame,
|
||||
},
|
||||
{
|
||||
path: 'naive',
|
||||
name: 'frame-naive',
|
||||
meta: {
|
||||
title: 'NaiveUi(内嵌)',
|
||||
frameSrc: 'https://www.naiveui.com',
|
||||
},
|
||||
component: IFrame,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,53 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { TableOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
*
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/list',
|
||||
name: 'List',
|
||||
redirect: '/list/basic-list',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '列表页面',
|
||||
icon: renderIcon(TableOutlined),
|
||||
sort: 2,
|
||||
permissions: ['basic_list'],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic-list/:id?',
|
||||
name: 'basic-list',
|
||||
meta: {
|
||||
title: '基础列表',
|
||||
},
|
||||
component: () => import('@/views/list/basicList/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'basic-info/:id?',
|
||||
name: 'BasicInfo',
|
||||
meta: {
|
||||
title: '基础详情',
|
||||
hidden: true,
|
||||
activeMenu: 'basic-list',
|
||||
keepAlive: true,
|
||||
},
|
||||
component: () => import('@/views/list/basicList/info.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,19 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { DiamondOutline } from '@vicons/ionicons5';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/mandate',
|
||||
name: 'https://www.naiveadmin.com/authorize/index',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '获取授权',
|
||||
icon: renderIcon(DiamondOutline),
|
||||
sort: 12,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,57 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { CheckCircleOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
*
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/result',
|
||||
name: 'Result',
|
||||
redirect: '/result/success',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '结果页面',
|
||||
icon: renderIcon(CheckCircleOutlined),
|
||||
sort: 4,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'success',
|
||||
name: 'result-success',
|
||||
meta: {
|
||||
title: '成功页',
|
||||
},
|
||||
component: () => import('@/views/result/success.vue'),
|
||||
},
|
||||
{
|
||||
path: 'fail',
|
||||
name: 'result-fail',
|
||||
meta: {
|
||||
title: '失败页',
|
||||
},
|
||||
component: () => import('@/views/result/fail.vue'),
|
||||
},
|
||||
{
|
||||
path: 'info',
|
||||
name: 'result-info',
|
||||
meta: {
|
||||
title: '信息页',
|
||||
},
|
||||
component: () => import('@/views/result/info.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,49 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { SettingOutlined } from '@vicons/antd';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
*
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/setting',
|
||||
name: 'Setting',
|
||||
redirect: '/setting/account',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '设置页面',
|
||||
icon: renderIcon(SettingOutlined),
|
||||
sort: 5,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'account',
|
||||
name: 'setting-account',
|
||||
meta: {
|
||||
title: '个人设置',
|
||||
},
|
||||
component: () => import('@/views/setting/account/account.vue'),
|
||||
},
|
||||
{
|
||||
path: 'system',
|
||||
name: 'setting-system',
|
||||
meta: {
|
||||
title: '系统设置',
|
||||
},
|
||||
component: () => import('@/views/setting/system/system.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -1,99 +0,0 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import { Layout } from '@/router/constant';
|
||||
import { OptionsSharp } from '@vicons/ionicons5';
|
||||
import { renderIcon } from '@/utils/index';
|
||||
import { NBadge } from 'naive-ui';
|
||||
import { h } from 'vue';
|
||||
|
||||
/**
|
||||
* @param name 路由名称, 必须设置,且不能重名
|
||||
* @param meta 路由元信息(路由附带扩展信息)
|
||||
* @param redirect 重定向地址, 访问这个路由时,自定进行重定向
|
||||
* @param meta.disabled 禁用整个菜单
|
||||
* @param meta.title 菜单名称
|
||||
* @param meta.icon 菜单图标
|
||||
* @param meta.keepAlive 缓存该路由
|
||||
* @param meta.sort 排序越小越排前
|
||||
*
|
||||
* */
|
||||
const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/system',
|
||||
name: 'System',
|
||||
redirect: '/system/menu',
|
||||
component: Layout,
|
||||
meta: {
|
||||
documentTitle: '系统管理',
|
||||
title: () => [
|
||||
h('span', {}, '系统管理'),
|
||||
h(NBadge, {
|
||||
dot: true,
|
||||
type: 'error',
|
||||
value: 1,
|
||||
style: {
|
||||
marginLeft: '5px',
|
||||
},
|
||||
color: '#ff3b30',
|
||||
}),
|
||||
],
|
||||
icon: renderIcon(OptionsSharp),
|
||||
sort: 1,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
name: 'system_user',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
},
|
||||
component: () => import('@/views/system/user/user.vue'),
|
||||
},
|
||||
{
|
||||
path: 'menu',
|
||||
name: 'system_menu',
|
||||
meta: {
|
||||
title: '菜单管理',
|
||||
},
|
||||
component: () => import('@/views/system/menu/index.vue'),
|
||||
},
|
||||
{
|
||||
path: 'menu/table',
|
||||
name: 'system_menu_table',
|
||||
meta: {
|
||||
documentTitle: '菜单管理2',
|
||||
title: () => [
|
||||
h('span', {}, '菜单管理2'),
|
||||
h(NBadge, {
|
||||
dot: true,
|
||||
type: 'error',
|
||||
value: 1,
|
||||
style: {
|
||||
marginLeft: '5px',
|
||||
},
|
||||
color: '#ff3b30',
|
||||
}),
|
||||
],
|
||||
},
|
||||
component: () => import('@/views/system/menu/table.vue'),
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
name: 'system_role',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
},
|
||||
component: () => import('@/views/system/role/role.vue'),
|
||||
},
|
||||
{
|
||||
path: 'dictionary',
|
||||
name: 'system_dictionary',
|
||||
meta: {
|
||||
title: '字典管理',
|
||||
},
|
||||
component: () => import('@/views/system/dictionary/dictionary.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
@ -2,10 +2,12 @@ import type { RouteRecordRaw } from 'vue-router';
|
||||
import { isNavigationFailure, Router } from 'vue-router';
|
||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types';
|
||||
import { ACCESS_TOKEN,FIRST_ROUTE } from '@/store/mutation-types';
|
||||
import { storage } from '@/utils/Storage';
|
||||
import { PageEnum } from '@/enums/pageEnum';
|
||||
import { ErrorPageRoute } from '@/router/base';
|
||||
import { findFirstRoutePath } from '@/router/generator-routers';
|
||||
import {findTreeByPath} from "@/utils/auth";
|
||||
import { useLoadingBar } from '@/hooks/web/useLoadingBar';
|
||||
|
||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
||||
@ -19,10 +21,10 @@ export function createRouterGuards(router: Router) {
|
||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
loadingBar.start();
|
||||
if (from.path === LOGIN_PATH && to.name === 'errorPage') {
|
||||
next(PageEnum.BASE_HOME);
|
||||
return;
|
||||
}
|
||||
// if (from.path === LOGIN_PATH && to.name === 'errorPage') {
|
||||
// next(PageEnum.BASE_HOME);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// Whitelist can be directly entered
|
||||
if (whitePathList.includes(to.path as PageEnum)) {
|
||||
@ -72,8 +74,14 @@ export function createRouterGuards(router: Router) {
|
||||
if (isErrorPage === -1) {
|
||||
router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
|
||||
}
|
||||
|
||||
const redirectPath = (from.query.redirect || to.path) as string;
|
||||
const firstRoutePath = findFirstRoutePath(routes)
|
||||
storage.set(FIRST_ROUTE,firstRoutePath)
|
||||
if (from.path === LOGIN_PATH && (to.name === 'errorPage' || !to.name)) {
|
||||
next(firstRoutePath);
|
||||
return;
|
||||
}
|
||||
let formPath = findTreeByPath(routes,from.query.redirect).length > 0 ? from.query.redirect:to.path
|
||||
const redirectPath = (formPath) as string;
|
||||
const redirect = decodeURIComponent(redirectPath);
|
||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
||||
asyncRouteStore.setDynamicAddedRoute(true);
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { renderIcon } from '@/utils/index';
|
||||
import { DashboardOutlined, TableOutlined } from '@vicons/antd';
|
||||
import * as AllIcons from '@vicons/antd';
|
||||
|
||||
const icons = Object.keys(AllIcons);
|
||||
//前端路由图标映射表
|
||||
export const constantRouterIcon = {
|
||||
DashboardOutlined: renderIcon(DashboardOutlined),
|
||||
TableOutlined: renderIcon(TableOutlined),
|
||||
};
|
||||
const constantRouterIcon = {};
|
||||
icons.forEach(icon => {
|
||||
constantRouterIcon[icon] = renderIcon(AllIcons[icon]);
|
||||
});
|
||||
export { constantRouterIcon };
|
||||
|
@ -20,6 +20,7 @@ export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
|
||||
export interface Meta {
|
||||
// 名称
|
||||
title: string;
|
||||
label: string;
|
||||
// 是否忽略权限
|
||||
ignoreAuth?: boolean;
|
||||
//权限数组集合
|
||||
|
@ -2,15 +2,15 @@ export default {
|
||||
table: {
|
||||
apiSetting: {
|
||||
// 当前页的字段名
|
||||
pageField: 'page',
|
||||
pageField: 'pageNo',
|
||||
// 每页数量字段名
|
||||
sizeField: 'pageSize',
|
||||
// 接口返回的数据字段名
|
||||
listField: 'list',
|
||||
listField: 'records',
|
||||
// 接口返回总页数字段名
|
||||
totalField: 'pageCount',
|
||||
totalField: 'pages',
|
||||
//总数字段名
|
||||
countField: 'itemCount',
|
||||
countField: 'total',
|
||||
},
|
||||
//默认分页数量
|
||||
defaultPageSize: 10,
|
||||
|
@ -49,7 +49,7 @@ const setting = {
|
||||
showIcon: false,
|
||||
},
|
||||
//菜单权限模式 FIXED 前端固定路由 BACK 动态获取
|
||||
permissionMode: 'FIXED',
|
||||
permissionMode: 'BACK',
|
||||
//是否开启路由动画
|
||||
isPageAnimate: true,
|
||||
//路由动画类型 默认消退
|
||||
|
@ -5,7 +5,7 @@ import { ACCESS_TOKEN, CURRENT_USER, IS_LOCKSCREEN } from '@/store/mutation-type
|
||||
import { ResultEnum } from '@/enums/httpEnum';
|
||||
|
||||
const Storage = createStorage({ storage: localStorage });
|
||||
import { getUserInfo, login, userLogout } from '@/api/system/user';
|
||||
import { getUserInfo, login } from '@/api/system/user';
|
||||
import { storage } from '@/utils/Storage';
|
||||
|
||||
export interface IUserState {
|
||||
@ -61,14 +61,12 @@ export const useUserStore = defineStore({
|
||||
async login(userInfo) {
|
||||
try {
|
||||
const response = await login(userInfo);
|
||||
const { result, code } = response;
|
||||
const {code,data } = response;
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
const ex = 7 * 24 * 60 * 60 * 1000;
|
||||
storage.set(ACCESS_TOKEN, result.token, ex);
|
||||
storage.set(CURRENT_USER, result, ex);
|
||||
storage.set(ACCESS_TOKEN, data.access_token, ex);
|
||||
storage.set(IS_LOCKSCREEN, false);
|
||||
this.setToken(result.token);
|
||||
this.setUserInfo(result);
|
||||
this.setToken(data.access_token);
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
} catch (e) {
|
||||
@ -98,19 +96,11 @@ export const useUserStore = defineStore({
|
||||
|
||||
// 登出
|
||||
async logout() {
|
||||
return new Promise((resolve, reject) => {
|
||||
userLogout()
|
||||
.then(() => {
|
||||
this.setPermissions([]);
|
||||
this.setUserInfo({} as IUserState);
|
||||
storage.remove(ACCESS_TOKEN);
|
||||
storage.remove(CURRENT_USER);
|
||||
return resolve(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
this.setPermissions([]);
|
||||
this.setUserInfo('');
|
||||
storage.remove(ACCESS_TOKEN);
|
||||
storage.remove(CURRENT_USER);
|
||||
return Promise.resolve('');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -2,3 +2,4 @@ export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token
|
||||
export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
|
||||
export const IS_LOCKSCREEN = 'IS-LOCKSCREEN'; // 是否锁屏
|
||||
export const TABS_ROUTES = 'TABS-ROUTES'; // 标签页
|
||||
export const FIRST_ROUTE = 'FIRST-ROUTE'; // 菜单第一个路由
|
||||
|
@ -39,3 +39,18 @@ export function urlToBase64(url: string, mineType?: string): Promise<string> {
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
/**
|
||||
* base64 to file
|
||||
* @param dataURL
|
||||
*/
|
||||
export function base64ToFile(dataurl:any, filename:any) {
|
||||
let arr = dataurl.split(",");
|
||||
let mime = arr[0].match(/:(.*?);/)[1];
|
||||
let bstr = atob(arr[1]);
|
||||
let len = bstr.length;
|
||||
let u8arr = new Uint8Array(len);
|
||||
while (len--) {
|
||||
u8arr[len] = bstr.charCodeAt(len);
|
||||
}
|
||||
return new File([u8arr], filename, {type: mime});
|
||||
}
|
||||
|
@ -53,35 +53,33 @@ const transform: AxiosTransform = {
|
||||
return res.data;
|
||||
}
|
||||
|
||||
const { data } = res;
|
||||
|
||||
const $dialog = useDialog();
|
||||
const $message = useMessage();
|
||||
|
||||
if (!data) {
|
||||
if (!res.data) {
|
||||
// return '[HTTP] Request has no return value';
|
||||
throw new Error('请求出错,请稍候重试');
|
||||
}
|
||||
// 这里 code,result,message为 后台统一的字段,需要修改为项目自己的接口返回格式
|
||||
const { code, result, message } = data;
|
||||
const { code, data, msg } = res.data;
|
||||
// 请求成功
|
||||
const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
|
||||
const hasSuccess = data && code === ResultEnum.SUCCESS;
|
||||
// 是否显示提示信息
|
||||
if (isShowMessage) {
|
||||
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
|
||||
// 是否显示自定义信息提示
|
||||
$dialog.success({
|
||||
type: 'success',
|
||||
content: successMessageText || message || '操作成功!',
|
||||
content: successMessageText || msg || '操作成功!',
|
||||
});
|
||||
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
|
||||
// 是否显示自定义信息提示
|
||||
$message.error(message || errorMessageText || '操作失败!');
|
||||
$message.error(msg || errorMessageText || '操作失败!');
|
||||
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
||||
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||
$dialog.info({
|
||||
title: '提示',
|
||||
content: message,
|
||||
content: msg,
|
||||
positiveText: '确定',
|
||||
onPositiveClick: () => {},
|
||||
});
|
||||
@ -90,10 +88,10 @@ const transform: AxiosTransform = {
|
||||
|
||||
// 接口请求成功,直接返回结果
|
||||
if (code === ResultEnum.SUCCESS) {
|
||||
return result;
|
||||
return data;
|
||||
}
|
||||
// 接口请求错误,统一提示错误信息 这里逻辑可以根据项目进行修改
|
||||
let errorMsg = message;
|
||||
let errorMsg = msg;
|
||||
switch (code) {
|
||||
// 请求失败
|
||||
case ResultEnum.ERROR:
|
||||
@ -242,7 +240,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
||||
deepMerge(
|
||||
{
|
||||
timeout: 10 * 1000,
|
||||
authenticationScheme: '',
|
||||
authenticationScheme: 'Bearer',
|
||||
// 接口前缀
|
||||
prefixUrl: urlPrefix,
|
||||
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
||||
|
18
src/utils/useLockFn.ts
Normal file
18
src/utils/useLockFn.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
export function useLockFn(fn: (...args: any[]) => Promise<any>) {
|
||||
const isLock = ref(false);
|
||||
const lockFn = async (...args: any[]) => {
|
||||
if (isLock.value) return;
|
||||
isLock.value = true;
|
||||
try {
|
||||
await fn(...args);
|
||||
} finally {
|
||||
isLock.value = false;
|
||||
}
|
||||
};
|
||||
return {
|
||||
isLock,
|
||||
lockFn,
|
||||
};
|
||||
}
|
33
yarn.lock
33
yarn.lock
@ -4531,7 +4531,7 @@ mz@^2.7.0:
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
naive-ui@^2.39.0:
|
||||
naive-ui@^2.40.0:
|
||||
version "2.40.1"
|
||||
resolved "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.40.1.tgz#64d9570dae4286b89979a582713f6f824c25230b"
|
||||
integrity sha512-3NkL+vLRQZKQxCHXa+7xiD6oM74OrQELaehDkGYRYpr6kjT+JJB+Z7h+5LC70gn8VkbgCAETv0+uRWF+6MLlgQ==
|
||||
@ -5820,16 +5820,7 @@ string-argv@0.3.1:
|
||||
resolved "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
|
||||
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
@ -5863,14 +5854,7 @@ stringify-object@3.3.0:
|
||||
is-obj "^1.0.1"
|
||||
is-regexp "^1.0.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
@ -6782,7 +6766,7 @@ word@~0.3.0:
|
||||
resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
|
||||
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
@ -6800,15 +6784,6 @@ wrap-ansi@^6.2.0:
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
|
Loading…
Reference in New Issue
Block a user