区域组件,axios
This commit is contained in:
parent
19c09a116b
commit
ac90e6984a
@ -42,7 +42,7 @@
|
|||||||
"element-resize-detector": "^1.2.4",
|
"element-resize-detector": "^1.2.4",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"naive-ui": "^2.39.0",
|
"naive-ui": "^2.40.0",
|
||||||
"perfect-scrollbar": "^1.5.5",
|
"perfect-scrollbar": "^1.5.5",
|
||||||
"pinia": "^2.2.2",
|
"pinia": "^2.2.2",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
|
@ -6,5 +6,8 @@ export function upload(data) {
|
|||||||
url: '/upload/uploadFile',
|
url: '/upload/uploadFile',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data,
|
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 }">
|
<div class="img-cropper" :class="{ 'img-cropper-auto': $slots.default }">
|
||||||
<slot name="default"></slot>
|
<slot name="default"></slot>
|
||||||
<template v-if="!$slots.default">
|
<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 }" />
|
<img :src="src" :class="{ circled: circled }" />
|
||||||
<div class="mask" :class="{ circled: circled }">
|
<div class="mask" :class="{ circled: circled }">
|
||||||
<n-icon>
|
<n-icon>
|
||||||
<UploadOutlined />
|
<UploadOutlined />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</div>
|
</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>
|
</template>
|
||||||
<CropperModal
|
<CropperModal
|
||||||
ref="cropperRef"
|
ref="cropperRef"
|
||||||
@ -17,6 +45,7 @@
|
|||||||
subBtuText="确认上传"
|
subBtuText="确认上传"
|
||||||
:uploadApi="uploadApi"
|
:uploadApi="uploadApi"
|
||||||
:circled="circled"
|
:circled="circled"
|
||||||
|
:name="name"
|
||||||
@upload-success="uploadSuccess"
|
@upload-success="uploadSuccess"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -25,7 +54,13 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import CropperModal from './CropperModal.vue';
|
import CropperModal from './CropperModal.vue';
|
||||||
import { UploadOutlined } from '@vicons/antd';
|
import {
|
||||||
|
PlusOutlined,
|
||||||
|
ZoomInOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
FormOutlined,
|
||||||
|
UploadOutlined
|
||||||
|
} from '@vicons/antd';
|
||||||
import { cssUnit } from '@/utils';
|
import { cssUnit } from '@/utils';
|
||||||
|
|
||||||
const cropperRef = ref();
|
const cropperRef = ref();
|
||||||
@ -36,7 +71,8 @@
|
|||||||
title: { type: String, default: '图片上传' },
|
title: { type: String, default: '图片上传' },
|
||||||
src: { type: String, required: true },
|
src: { type: String, required: true },
|
||||||
circled: { type: Boolean, default: false },
|
circled: { type: Boolean, default: false },
|
||||||
width: { type: [String, Number], default: 200 },
|
width: { type: [String, Number], default: 120 },
|
||||||
|
name: { type: String, default: 'name' },
|
||||||
uploadApi: {
|
uploadApi: {
|
||||||
type: Function as PropType<(params) => Promise<any>>,
|
type: Function as PropType<(params) => Promise<any>>,
|
||||||
},
|
},
|
||||||
@ -54,6 +90,13 @@
|
|||||||
emit('uploadSuccess', result);
|
emit('uploadSuccess', result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const viewImg = () => {
|
||||||
|
window.open(props.src);
|
||||||
|
};
|
||||||
|
const deleteImg = () => {
|
||||||
|
emit('uploadSuccess', { fileUrl: '' });
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
openCropper,
|
openCropper,
|
||||||
});
|
});
|
||||||
@ -72,7 +115,22 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
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 {
|
.mask {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -86,11 +144,19 @@
|
|||||||
top: 0;
|
top: 0;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.4s;
|
transition: opacity 0.4s;
|
||||||
|
.handle-icon {
|
||||||
.n-icon {
|
display: flex;
|
||||||
color: #fff;
|
flex-direction: column;
|
||||||
font-size: v-bind(iconSize);
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 6%;
|
||||||
|
color: aliceblue;
|
||||||
|
.n-icon {
|
||||||
|
color: #fff;
|
||||||
|
font-size: v-bind(iconSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<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>
|
<template #default>
|
||||||
<div class="cropper-box">
|
<div class="cropper-box">
|
||||||
<div class="cropper-box-left">
|
<div class="cropper-box-left">
|
||||||
@ -184,7 +184,7 @@
|
|||||||
import { useMessage } from 'naive-ui';
|
import { useMessage } from 'naive-ui';
|
||||||
import { basicModal, useModal } from '@/components/Modal';
|
import { basicModal, useModal } from '@/components/Modal';
|
||||||
import CropperImage from './CropperImage.vue';
|
import CropperImage from './CropperImage.vue';
|
||||||
import { dataURLtoBlob } from '@/utils/file/base64Conver';
|
import { dataURLtoBlob,base64ToFile } from '@/utils/file/base64Conver';
|
||||||
import { isFunction } from '@/utils/is';
|
import { isFunction } from '@/utils/is';
|
||||||
import {
|
import {
|
||||||
UploadOutlined,
|
UploadOutlined,
|
||||||
@ -200,6 +200,7 @@
|
|||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
title: { type: String, default: '图片上传' },
|
title: { type: String, default: '图片上传' },
|
||||||
circled: { type: Boolean, default: false },
|
circled: { type: Boolean, default: false },
|
||||||
|
name: { type: String, default: 'name' },
|
||||||
uploadApi: {
|
uploadApi: {
|
||||||
type: Function as PropType<(params) => Promise<any>>,
|
type: Function as PropType<(params) => Promise<any>>,
|
||||||
},
|
},
|
||||||
@ -261,18 +262,28 @@
|
|||||||
return message.error('请先上传图片');
|
return message.error('请先上传图片');
|
||||||
}
|
}
|
||||||
const uploadApi = props.uploadApi;
|
const uploadApi = props.uploadApi;
|
||||||
|
const name = props.name
|
||||||
if (uploadApi && isFunction(uploadApi)) {
|
if (uploadApi && isFunction(uploadApi)) {
|
||||||
const blob = dataURLtoBlob(previewSource.value);
|
const file = base64ToFile(previewSource.value, filename);
|
||||||
|
console.log(file)
|
||||||
try {
|
try {
|
||||||
setSubLoading(true);
|
setSubLoading(true);
|
||||||
const result = await uploadApi({ name: 'file', file: blob, filename });
|
const formData = new window.FormData();
|
||||||
emit('uploadSuccess', { source: previewSource.value, data: result.data });
|
formData.append('file', file);
|
||||||
|
formData.append('name', name);
|
||||||
|
console.log(formData)
|
||||||
|
const result = await uploadApi(formData);
|
||||||
|
emit('uploadSuccess', result);
|
||||||
closeModal();
|
closeModal();
|
||||||
} finally {
|
} finally {
|
||||||
setSubLoading(false);
|
setSubLoading(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function handleClose() {
|
||||||
|
src.value = '';
|
||||||
|
previewSource.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
showModal,
|
showModal,
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
* @description: 请求结果集
|
* @description: 请求结果集
|
||||||
*/
|
*/
|
||||||
export enum ResultEnum {
|
export enum ResultEnum {
|
||||||
SUCCESS = 200,
|
SUCCESS = 0,
|
||||||
ERROR = -1,
|
ERROR = 1,
|
||||||
TIMEOUT = 10042,
|
TIMEOUT = 401,
|
||||||
TYPE = 'success',
|
TYPE = 'success',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,6 @@ export enum PageEnum {
|
|||||||
REDIRECT_NAME = 'Redirect',
|
REDIRECT_NAME = 'Redirect',
|
||||||
// 首页
|
// 首页
|
||||||
BASE_HOME = '/dashboard',
|
BASE_HOME = '/dashboard',
|
||||||
//首页跳转默认路由
|
|
||||||
BASE_HOME_REDIRECT = '/dashboard/console',
|
|
||||||
// 错误
|
// 错误
|
||||||
ERROR_PAGE_NAME = 'ErrorPage',
|
ERROR_PAGE_NAME = 'ErrorPage',
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,12 @@ export function usePermission() {
|
|||||||
* @param accesses
|
* @param accesses
|
||||||
*/
|
*/
|
||||||
function _somePermissions(accesses: string[]) {
|
function _somePermissions(accesses: string[]) {
|
||||||
return userStore.getPermissions.some((item: any) => {
|
const permissionsList = userStore.getPermissions;
|
||||||
const value = item?.value ?? item;
|
if(permissionsList[0]=='*:*:*') {
|
||||||
return accesses.includes(value);
|
return true
|
||||||
|
}
|
||||||
|
return accesses.some((item) => {
|
||||||
|
return permissionsList.includes(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,8 +32,11 @@ export function usePermission() {
|
|||||||
*/
|
*/
|
||||||
function hasEveryPermission(accesses: string[]): boolean {
|
function hasEveryPermission(accesses: string[]): boolean {
|
||||||
const permissionsList = userStore.getPermissions;
|
const permissionsList = userStore.getPermissions;
|
||||||
|
if(permissionsList[0]=='*:*:*') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if (Array.isArray(accesses)) {
|
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 !`);
|
throw new Error(`[hasEveryPermission]: ${accesses} should be a array !`);
|
||||||
}
|
}
|
||||||
@ -42,8 +48,11 @@ export function usePermission() {
|
|||||||
*/
|
*/
|
||||||
function hasSomePermission(accesses: string[]): boolean {
|
function hasSomePermission(accesses: string[]): boolean {
|
||||||
const permissionsList = userStore.getPermissions;
|
const permissionsList = userStore.getPermissions;
|
||||||
|
if(permissionsList[0]=='*:*:*') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
if (Array.isArray(accesses)) {
|
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 !`);
|
throw new Error(`[hasSomePermission]: ${accesses} should be a array !`);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +120,7 @@
|
|||||||
<div class="layout-header-trigger layout-header-trigger-min">
|
<div class="layout-header-trigger layout-header-trigger-min">
|
||||||
<n-dropdown :options="avatarOptions" trigger="hover" @select="avatarSelect">
|
<n-dropdown :options="avatarOptions" trigger="hover" @select="avatarSelect">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<n-avatar round :src="schoolboy" />
|
<n-avatar round :src="userAvatar?userAvatar:schoolboy" />
|
||||||
</div>
|
</div>
|
||||||
</n-dropdown>
|
</n-dropdown>
|
||||||
</div>
|
</div>
|
||||||
@ -155,7 +155,8 @@
|
|||||||
import { computed, ref, unref, watch, inject } from 'vue';
|
import { computed, ref, unref, watch, inject } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useDialog, useMessage } from 'naive-ui';
|
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 { useUserStore } from '@/store/modules/user';
|
||||||
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
import { useLockscreenStore } from '@/store/modules/lockscreen';
|
||||||
import { AsideMenu } from '@/layout/components/Menu';
|
import { AsideMenu } from '@/layout/components/Menu';
|
||||||
@ -210,7 +211,7 @@
|
|||||||
const drawerSetting = ref();
|
const drawerSetting = ref();
|
||||||
|
|
||||||
const amendPwdRef = ref();
|
const amendPwdRef = ref();
|
||||||
|
const userAvatar = userStore.getAvatar
|
||||||
// const username = userStore?.info ? ref(userStore?.info.username) : '';
|
// const username = userStore?.info ? ref(userStore?.info.username) : '';
|
||||||
|
|
||||||
const collapsed = inject('collapsed');
|
const collapsed = inject('collapsed');
|
||||||
@ -238,6 +239,7 @@
|
|||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { isFullscreen, toggle } = useFullscreen();
|
const { isFullscreen, toggle } = useFullscreen();
|
||||||
const asyncRouteStore = useAsyncRouteStore();
|
const asyncRouteStore = useAsyncRouteStore();
|
||||||
|
const tabsViewStore = useTabsViewStore();
|
||||||
|
|
||||||
const generator: any = (routerMap) => {
|
const generator: any = (routerMap) => {
|
||||||
return routerMap
|
return routerMap
|
||||||
@ -295,7 +297,9 @@
|
|||||||
userStore.logout().then(() => {
|
userStore.logout().then(() => {
|
||||||
message.success('成功退出登录');
|
message.success('成功退出登录');
|
||||||
// 移除标签页
|
// 移除标签页
|
||||||
|
tabsViewStore.closeAllTabs();
|
||||||
localStorage.removeItem(TABS_ROUTES);
|
localStorage.removeItem(TABS_ROUTES);
|
||||||
|
localStorage.removeItem(FIRST_ROUTE);
|
||||||
asyncRouteStore.setDynamicAddedRoute(false);
|
asyncRouteStore.setDynamicAddedRoute(false);
|
||||||
router.replace({
|
router.replace({
|
||||||
name: BASE_LOGIN_NAME,
|
name: BASE_LOGIN_NAME,
|
||||||
@ -323,13 +327,13 @@
|
|||||||
click: () => openAppSearch(),
|
click: () => openAppSearch(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
icon: LockOutlined,
|
// icon: LockOutlined,
|
||||||
tips: '锁屏',
|
// tips: '锁屏',
|
||||||
eventObject: {
|
// eventObject: {
|
||||||
click: () => useLockscreen.setLock(true),
|
// click: () => useLockscreen.setLock(true),
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
];
|
];
|
||||||
const avatarOptions = [
|
const avatarOptions = [
|
||||||
{
|
{
|
||||||
@ -353,7 +357,7 @@
|
|||||||
const avatarSelect = (key) => {
|
const avatarSelect = (key) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 1:
|
case 1:
|
||||||
router.push({ name: 'Setting' });
|
router.push({ name: 'setting' });
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
doLogout();
|
doLogout();
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
import { computed, ref, unref, provide, watch, onMounted, nextTick, toRaw, inject } from 'vue';
|
import { computed, ref, unref, provide, watch, onMounted, nextTick, toRaw, inject } from 'vue';
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { storage } from '@/utils/Storage';
|
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 { useTabsViewStore } from '@/store/modules/tabsView';
|
||||||
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
import { useAsyncRouteStore } from '@/store/modules/asyncRoute';
|
||||||
import { RouteItem } from '@/store/modules/tabsView';
|
import { RouteItem } from '@/store/modules/tabsView';
|
||||||
@ -335,7 +335,7 @@
|
|||||||
// 关闭全部
|
// 关闭全部
|
||||||
const closeAll = () => {
|
const closeAll = () => {
|
||||||
tabsViewStore.closeAllTabs();
|
tabsViewStore.closeAllTabs();
|
||||||
router.replace(PageEnum.BASE_HOME_REDIRECT);
|
router.replace(storage.get(FIRST_ROUTE));
|
||||||
updateNavScroll();
|
updateNavScroll();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { adminMenus } from '@/api/system/menu';
|
import { adminMenus } from '@/api/system/menu';
|
||||||
import { constantRouterIcon } from './router-icons';
|
import { constantRouterIcon } from './router-icons';
|
||||||
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
import { Layout, ParentLayout } from '@/router/constant';
|
import { Layout, ParentLayout } from '@/router/constant';
|
||||||
import type { AppRouteRecordRaw } from '@/router/types';
|
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 Iframe = () => import('@/views/iframe/index.vue');
|
||||||
const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();
|
const LayoutMap = new Map<string, () => Promise<typeof import('*.vue')>>();
|
||||||
|
|
||||||
LayoutMap.set('LAYOUT', Layout);
|
LayoutMap.set('LAYOUT', Layout);
|
||||||
LayoutMap.set('IFRAME', Iframe);
|
LayoutMap.set('IFRAME', Iframe);
|
||||||
LayoutMap.set('ParentLayout', ParentLayout);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化 后端 结构信息并递归生成层级路由表
|
* 格式化 后端 结构信息并递归生成层级路由表
|
||||||
@ -16,34 +18,81 @@ LayoutMap.set('ParentLayout', ParentLayout);
|
|||||||
* @param parent
|
* @param parent
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
export const routerGenerator = (routerMap, parent?): AppRouteRecordRaw[] => {
|
export const routerGenerator = (routerMap): any[] => {
|
||||||
return routerMap.map((item) => {
|
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 = {
|
const currentRouter: any = {
|
||||||
// 路由地址 动态拼接生成如 /dashboard/workplace
|
path:item.target==2?'':item.path,
|
||||||
path: `${(parent && parent.path) || ''}/${item.path}`,
|
|
||||||
// 路由名称,建议唯一
|
// 路由名称,建议唯一
|
||||||
name: item.name || '',
|
name: names,
|
||||||
// 该路由对应页面的 组件
|
// 该路由对应页面的 组件
|
||||||
component: item.component,
|
component: components,
|
||||||
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
|
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
|
||||||
meta: {
|
meta: {
|
||||||
...item.meta,
|
...item.meta,
|
||||||
label: item.meta.title,
|
|
||||||
icon: constantRouterIcon[item.meta.icon] || null,
|
|
||||||
permissions: item.meta.permissions || null,
|
permissions: item.meta.permissions || null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
|
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
|
||||||
currentRouter.path = currentRouter.path.replace('//', '/');
|
// currentRouter.path = currentRouter.path.replace('//', '/');
|
||||||
// 重定向
|
// 重定向
|
||||||
item.redirect && (currentRouter.redirect = item.redirect);
|
item.redirect && (currentRouter.redirect = item.redirect);
|
||||||
// 是否有子菜单,并递归处理
|
// 是否有子菜单,并递归处理
|
||||||
if (item.children && item.children.length > 0) {
|
if (item.children && item.children.length > 0) {
|
||||||
//如果未定义 redirect 默认第一个子路由为 redirect
|
//如果未定义 redirect 默认第一个子路由为 redirect
|
||||||
!item.redirect && (currentRouter.redirect = `${item.path}/${item.children[0].path}`);
|
!item.redirect && (currentRouter.redirect = `${item.children[0].path}`);
|
||||||
// Recursion
|
// Recursion
|
||||||
currentRouter.children = routerGenerator(item.children, currentRouter);
|
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;
|
return currentRouter;
|
||||||
});
|
});
|
||||||
@ -53,11 +102,11 @@ export const routerGenerator = (routerMap, parent?): AppRouteRecordRaw[] => {
|
|||||||
* 动态生成菜单
|
* 动态生成菜单
|
||||||
* @returns {Promise<Router>}
|
* @returns {Promise<Router>}
|
||||||
*/
|
*/
|
||||||
export const generatorDynamicRouter = (): Promise<AppRouteRecordRaw[]> => {
|
export const generatorDynamicRouter = (): Promise<RouteRecordRaw[]> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
adminMenus()
|
adminMenus()
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
const routeList = routerGenerator(result);
|
const routeList = routerGenerator(result)
|
||||||
asyncImportRoute(routeList);
|
asyncImportRoute(routeList);
|
||||||
resolve(routeList);
|
resolve(routeList);
|
||||||
})
|
})
|
||||||
@ -119,3 +168,9 @@ export const dynamicImport = (
|
|||||||
return;
|
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 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) => {
|
Object.keys(modules).forEach((key) => {
|
||||||
const mod = modules[key].default || {};
|
const mod = modules[key].default || {};
|
||||||
const modList = Array.isArray(mod) ? [...mod] : [mod];
|
const modList = Array.isArray(mod) ? [...mod] : [mod];
|
||||||
@ -41,8 +46,8 @@ export const RootRoute: RouteRecordRaw = {
|
|||||||
export const LoginRoute: RouteRecordRaw = {
|
export const LoginRoute: RouteRecordRaw = {
|
||||||
path: '/login',
|
path: '/login',
|
||||||
name: 'Login',
|
name: 'Login',
|
||||||
// component: () => import('@/views/login/index.vue'), //v1.x 模板
|
component: () => import('@/views/login/index.vue'), //v1.x 模板
|
||||||
component: () => import('@/views/login/newLogin.vue'), // 2.x新模板
|
// component: () => import('@/views/login/newLogin.vue'), // 2.x新模板
|
||||||
meta: {
|
meta: {
|
||||||
title: '登录',
|
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 { isNavigationFailure, Router } from 'vue-router';
|
||||||
import { useUserStoreWidthOut } from '@/store/modules/user';
|
import { useUserStoreWidthOut } from '@/store/modules/user';
|
||||||
import { useAsyncRouteStoreWidthOut } from '@/store/modules/asyncRoute';
|
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 { storage } from '@/utils/Storage';
|
||||||
import { PageEnum } from '@/enums/pageEnum';
|
import { PageEnum } from '@/enums/pageEnum';
|
||||||
import { ErrorPageRoute } from '@/router/base';
|
import { ErrorPageRoute } from '@/router/base';
|
||||||
|
import { findFirstRoutePath } from '@/router/generator-routers';
|
||||||
|
import {findTreeByPath} from "@/utils/auth";
|
||||||
import { useLoadingBar } from '@/hooks/web/useLoadingBar';
|
import { useLoadingBar } from '@/hooks/web/useLoadingBar';
|
||||||
|
|
||||||
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
const LOGIN_PATH = PageEnum.BASE_LOGIN;
|
||||||
@ -19,10 +21,10 @@ export function createRouterGuards(router: Router) {
|
|||||||
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
const asyncRouteStore = useAsyncRouteStoreWidthOut();
|
||||||
router.beforeEach(async (to, from, next) => {
|
router.beforeEach(async (to, from, next) => {
|
||||||
loadingBar.start();
|
loadingBar.start();
|
||||||
if (from.path === LOGIN_PATH && to.name === 'errorPage') {
|
// if (from.path === LOGIN_PATH && to.name === 'errorPage') {
|
||||||
next(PageEnum.BASE_HOME);
|
// next(PageEnum.BASE_HOME);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Whitelist can be directly entered
|
// Whitelist can be directly entered
|
||||||
if (whitePathList.includes(to.path as PageEnum)) {
|
if (whitePathList.includes(to.path as PageEnum)) {
|
||||||
@ -72,8 +74,14 @@ export function createRouterGuards(router: Router) {
|
|||||||
if (isErrorPage === -1) {
|
if (isErrorPage === -1) {
|
||||||
router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
|
router.addRoute(ErrorPageRoute as unknown as RouteRecordRaw);
|
||||||
}
|
}
|
||||||
|
const firstRoutePath = findFirstRoutePath(routes)
|
||||||
const redirectPath = (from.query.redirect || to.path) as string;
|
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 redirect = decodeURIComponent(redirectPath);
|
||||||
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
const nextData = to.path === redirect ? { ...to, replace: true } : { path: redirect };
|
||||||
asyncRouteStore.setDynamicAddedRoute(true);
|
asyncRouteStore.setDynamicAddedRoute(true);
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import { renderIcon } from '@/utils/index';
|
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 = {
|
const constantRouterIcon = {};
|
||||||
DashboardOutlined: renderIcon(DashboardOutlined),
|
icons.forEach(icon => {
|
||||||
TableOutlined: renderIcon(TableOutlined),
|
constantRouterIcon[icon] = renderIcon(AllIcons[icon]);
|
||||||
};
|
});
|
||||||
|
export { constantRouterIcon };
|
||||||
|
@ -20,6 +20,7 @@ export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
|
|||||||
export interface Meta {
|
export interface Meta {
|
||||||
// 名称
|
// 名称
|
||||||
title: string;
|
title: string;
|
||||||
|
label: string;
|
||||||
// 是否忽略权限
|
// 是否忽略权限
|
||||||
ignoreAuth?: boolean;
|
ignoreAuth?: boolean;
|
||||||
//权限数组集合
|
//权限数组集合
|
||||||
|
@ -2,15 +2,15 @@ export default {
|
|||||||
table: {
|
table: {
|
||||||
apiSetting: {
|
apiSetting: {
|
||||||
// 当前页的字段名
|
// 当前页的字段名
|
||||||
pageField: 'page',
|
pageField: 'pageNo',
|
||||||
// 每页数量字段名
|
// 每页数量字段名
|
||||||
sizeField: 'pageSize',
|
sizeField: 'pageSize',
|
||||||
// 接口返回的数据字段名
|
// 接口返回的数据字段名
|
||||||
listField: 'list',
|
listField: 'records',
|
||||||
// 接口返回总页数字段名
|
// 接口返回总页数字段名
|
||||||
totalField: 'pageCount',
|
totalField: 'pages',
|
||||||
//总数字段名
|
//总数字段名
|
||||||
countField: 'itemCount',
|
countField: 'total',
|
||||||
},
|
},
|
||||||
//默认分页数量
|
//默认分页数量
|
||||||
defaultPageSize: 10,
|
defaultPageSize: 10,
|
||||||
|
@ -49,7 +49,7 @@ const setting = {
|
|||||||
showIcon: false,
|
showIcon: false,
|
||||||
},
|
},
|
||||||
//菜单权限模式 FIXED 前端固定路由 BACK 动态获取
|
//菜单权限模式 FIXED 前端固定路由 BACK 动态获取
|
||||||
permissionMode: 'FIXED',
|
permissionMode: 'BACK',
|
||||||
//是否开启路由动画
|
//是否开启路由动画
|
||||||
isPageAnimate: true,
|
isPageAnimate: true,
|
||||||
//路由动画类型 默认消退
|
//路由动画类型 默认消退
|
||||||
|
@ -5,7 +5,7 @@ import { ACCESS_TOKEN, CURRENT_USER, IS_LOCKSCREEN } from '@/store/mutation-type
|
|||||||
import { ResultEnum } from '@/enums/httpEnum';
|
import { ResultEnum } from '@/enums/httpEnum';
|
||||||
|
|
||||||
const Storage = createStorage({ storage: localStorage });
|
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';
|
import { storage } from '@/utils/Storage';
|
||||||
|
|
||||||
export interface IUserState {
|
export interface IUserState {
|
||||||
@ -61,14 +61,12 @@ export const useUserStore = defineStore({
|
|||||||
async login(userInfo) {
|
async login(userInfo) {
|
||||||
try {
|
try {
|
||||||
const response = await login(userInfo);
|
const response = await login(userInfo);
|
||||||
const { result, code } = response;
|
const {code,data } = response;
|
||||||
if (code === ResultEnum.SUCCESS) {
|
if (code === ResultEnum.SUCCESS) {
|
||||||
const ex = 7 * 24 * 60 * 60 * 1000;
|
const ex = 7 * 24 * 60 * 60 * 1000;
|
||||||
storage.set(ACCESS_TOKEN, result.token, ex);
|
storage.set(ACCESS_TOKEN, data.access_token, ex);
|
||||||
storage.set(CURRENT_USER, result, ex);
|
|
||||||
storage.set(IS_LOCKSCREEN, false);
|
storage.set(IS_LOCKSCREEN, false);
|
||||||
this.setToken(result.token);
|
this.setToken(data.access_token);
|
||||||
this.setUserInfo(result);
|
|
||||||
}
|
}
|
||||||
return Promise.resolve(response);
|
return Promise.resolve(response);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -98,19 +96,11 @@ export const useUserStore = defineStore({
|
|||||||
|
|
||||||
// 登出
|
// 登出
|
||||||
async logout() {
|
async logout() {
|
||||||
return new Promise((resolve, reject) => {
|
this.setPermissions([]);
|
||||||
userLogout()
|
this.setUserInfo('');
|
||||||
.then(() => {
|
storage.remove(ACCESS_TOKEN);
|
||||||
this.setPermissions([]);
|
storage.remove(CURRENT_USER);
|
||||||
this.setUserInfo({} as IUserState);
|
return Promise.resolve('');
|
||||||
storage.remove(ACCESS_TOKEN);
|
|
||||||
storage.remove(CURRENT_USER);
|
|
||||||
return resolve(true);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -2,3 +2,4 @@ export const ACCESS_TOKEN = 'ACCESS-TOKEN'; // 用户token
|
|||||||
export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
|
export const CURRENT_USER = 'CURRENT-USER'; // 当前用户信息
|
||||||
export const IS_LOCKSCREEN = 'IS-LOCKSCREEN'; // 是否锁屏
|
export const IS_LOCKSCREEN = 'IS-LOCKSCREEN'; // 是否锁屏
|
||||||
export const TABS_ROUTES = 'TABS-ROUTES'; // 标签页
|
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;
|
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;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = res;
|
|
||||||
|
|
||||||
const $dialog = useDialog();
|
const $dialog = useDialog();
|
||||||
const $message = useMessage();
|
const $message = useMessage();
|
||||||
|
|
||||||
if (!data) {
|
if (!res.data) {
|
||||||
// return '[HTTP] Request has no return value';
|
// return '[HTTP] Request has no return value';
|
||||||
throw new Error('请求出错,请稍候重试');
|
throw new Error('请求出错,请稍候重试');
|
||||||
}
|
}
|
||||||
// 这里 code,result,message为 后台统一的字段,需要修改为项目自己的接口返回格式
|
// 这里 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 (isShowMessage) {
|
||||||
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
|
if (hasSuccess && (successMessageText || isShowSuccessMessage)) {
|
||||||
// 是否显示自定义信息提示
|
// 是否显示自定义信息提示
|
||||||
$dialog.success({
|
$dialog.success({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
content: successMessageText || message || '操作成功!',
|
content: successMessageText || msg || '操作成功!',
|
||||||
});
|
});
|
||||||
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
|
} else if (!hasSuccess && (errorMessageText || isShowErrorMessage)) {
|
||||||
// 是否显示自定义信息提示
|
// 是否显示自定义信息提示
|
||||||
$message.error(message || errorMessageText || '操作失败!');
|
$message.error(msg || errorMessageText || '操作失败!');
|
||||||
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
} else if (!hasSuccess && options.errorMessageMode === 'modal') {
|
||||||
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
// errorMessageMode=‘custom-modal’的时候会显示modal错误弹窗,而不是消息提示,用于一些比较重要的错误
|
||||||
$dialog.info({
|
$dialog.info({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
content: message,
|
content: msg,
|
||||||
positiveText: '确定',
|
positiveText: '确定',
|
||||||
onPositiveClick: () => {},
|
onPositiveClick: () => {},
|
||||||
});
|
});
|
||||||
@ -90,10 +88,10 @@ const transform: AxiosTransform = {
|
|||||||
|
|
||||||
// 接口请求成功,直接返回结果
|
// 接口请求成功,直接返回结果
|
||||||
if (code === ResultEnum.SUCCESS) {
|
if (code === ResultEnum.SUCCESS) {
|
||||||
return result;
|
return data;
|
||||||
}
|
}
|
||||||
// 接口请求错误,统一提示错误信息 这里逻辑可以根据项目进行修改
|
// 接口请求错误,统一提示错误信息 这里逻辑可以根据项目进行修改
|
||||||
let errorMsg = message;
|
let errorMsg = msg;
|
||||||
switch (code) {
|
switch (code) {
|
||||||
// 请求失败
|
// 请求失败
|
||||||
case ResultEnum.ERROR:
|
case ResultEnum.ERROR:
|
||||||
@ -242,7 +240,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
|
|||||||
deepMerge(
|
deepMerge(
|
||||||
{
|
{
|
||||||
timeout: 10 * 1000,
|
timeout: 10 * 1000,
|
||||||
authenticationScheme: '',
|
authenticationScheme: 'Bearer',
|
||||||
// 接口前缀
|
// 接口前缀
|
||||||
prefixUrl: urlPrefix,
|
prefixUrl: urlPrefix,
|
||||||
headers: { 'Content-Type': ContentTypeEnum.JSON },
|
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"
|
object-assign "^4.0.1"
|
||||||
thenify-all "^1.0.0"
|
thenify-all "^1.0.0"
|
||||||
|
|
||||||
naive-ui@^2.39.0:
|
naive-ui@^2.40.0:
|
||||||
version "2.40.1"
|
version "2.40.1"
|
||||||
resolved "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.40.1.tgz#64d9570dae4286b89979a582713f6f824c25230b"
|
resolved "https://registry.npmmirror.com/naive-ui/-/naive-ui-2.40.1.tgz#64d9570dae4286b89979a582713f6f824c25230b"
|
||||||
integrity sha512-3NkL+vLRQZKQxCHXa+7xiD6oM74OrQELaehDkGYRYpr6kjT+JJB+Z7h+5LC70gn8VkbgCAETv0+uRWF+6MLlgQ==
|
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"
|
resolved "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
|
||||||
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
|
integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
|
||||||
|
|
||||||
"string-width-cjs@npm:string-width@^4.2.0":
|
"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==
|
|
||||||
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:
|
|
||||||
version "4.2.3"
|
version "4.2.3"
|
||||||
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
resolved "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||||
@ -5863,14 +5854,7 @@ stringify-object@3.3.0:
|
|||||||
is-obj "^1.0.1"
|
is-obj "^1.0.1"
|
||||||
is-regexp "^1.0.0"
|
is-regexp "^1.0.0"
|
||||||
|
|
||||||
"strip-ansi-cjs@npm: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==
|
|
||||||
dependencies:
|
|
||||||
ansi-regex "^5.0.1"
|
|
||||||
|
|
||||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
|
||||||
version "6.0.1"
|
version "6.0.1"
|
||||||
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
resolved "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||||
@ -6782,7 +6766,7 @@ word@~0.3.0:
|
|||||||
resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
|
resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
|
||||||
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
|
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"
|
version "7.0.0"
|
||||||
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||||
@ -6800,15 +6784,6 @@ wrap-ansi@^6.2.0:
|
|||||||
string-width "^4.1.0"
|
string-width "^4.1.0"
|
||||||
strip-ansi "^6.0.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:
|
wrap-ansi@^8.1.0:
|
||||||
version "8.1.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
resolved "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||||
|
Loading…
Reference in New Issue
Block a user