mirror of
https://github.com/m-xlsea/ruoyi-plus-soybean.git
synced 2025-09-24 07:49:47 +08:00
refactor(projects): merge branch unocss to main
This commit is contained in:
@ -13,7 +13,6 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { RouterView } from 'vue-router';
|
||||
import { zhCN, dateZhCN } from 'naive-ui';
|
||||
import { useThemeStore, subscribeStore } from '@/store';
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="dark:(bg-[#18181c] text-white text-opacity-82) transition-all duration-300 ease-in-out"
|
||||
class="dark:bg-[#18181c] dark:text-white dark:text-opacity-82 transition-all duration-300 ease-in-out"
|
||||
:class="inverted ? 'bg-[#001428] text-white' : 'bg-white text-[#333639]'"
|
||||
>
|
||||
<slot></slot>
|
||||
|
@ -1,4 +1,6 @@
|
||||
import UAParser from 'ua-parser-js';
|
||||
import { useAuthStore } from '@/store';
|
||||
import { isArray, isString } from '@/utils';
|
||||
|
||||
interface AppInfo {
|
||||
/** 项目名称 */
|
||||
@ -26,3 +28,27 @@ export function useDeviceInfo() {
|
||||
const result = parser.getResult();
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 权限判断 */
|
||||
export function usePermission() {
|
||||
const auth = useAuthStore();
|
||||
|
||||
function hasPermission(permission: Auth.RoleType | Auth.RoleType[]) {
|
||||
const { userRole } = auth.userInfo;
|
||||
|
||||
let has = userRole === 'super';
|
||||
if (!has) {
|
||||
if (isArray(permission)) {
|
||||
has = (permission as Auth.RoleType[]).includes(userRole);
|
||||
}
|
||||
if (isString(permission)) {
|
||||
has = (permission as Auth.RoleType) === userRole;
|
||||
}
|
||||
}
|
||||
return has;
|
||||
}
|
||||
|
||||
return {
|
||||
hasPermission
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/** 百度地图sdk地址 */
|
||||
export const BAIDU_MAP_SDK_URL =
|
||||
'https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1';
|
||||
export const BAIDU_MAP_SDK_URL = `https://api.map.baidu.com/getscript?v=3.0&ak=KSezYymXPth1DIGILRX3oYN9PxbOQQmU&services=&t=20210201100830&s=1`;
|
||||
|
||||
/** 高德地图sdk地址 */
|
||||
export const GAODE_MAP_SDK_URL = 'https://webapi.amap.com/maps?v=2.0&key=e7bd02bd504062087e6563daf4d6721d';
|
||||
|
@ -1,9 +1,11 @@
|
||||
import type { App } from 'vue';
|
||||
import setupNetworkDirective from './network';
|
||||
import setupLoginDirective from './login';
|
||||
import setupPermissionDirective from './permission';
|
||||
|
||||
/** setup custom vue directives. - [安装自定义的vue指令] */
|
||||
export function setupDirectives(app: App) {
|
||||
setupNetworkDirective(app);
|
||||
setupLoginDirective(app);
|
||||
setupPermissionDirective(app);
|
||||
}
|
||||
|
@ -1,16 +1,26 @@
|
||||
import type { App, Directive } from 'vue';
|
||||
import { useAuthStore } from '@/store';
|
||||
import { usePermission } from '@/composables';
|
||||
|
||||
export default function setupLoginDirective(app: App) {
|
||||
const auth = useAuthStore();
|
||||
export default function setupPermissionDirective(app: App) {
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const loginDirective: Directive<HTMLElement, Auth.RoleType | undefined> = {
|
||||
mounted(el: HTMLElement, binding) {
|
||||
if (binding.value !== auth.userInfo.userRole) {
|
||||
el.remove();
|
||||
}
|
||||
function updateElVisible(el: HTMLElement, permission: Auth.RoleType | Auth.RoleType[]) {
|
||||
if (!permission) {
|
||||
throw new Error(`need roles: like v-permission="'admin'", v-permission="['admin', 'test]"`);
|
||||
}
|
||||
if (!hasPermission(permission)) {
|
||||
el.parentElement?.removeChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
const permissionDirective: Directive<HTMLElement, Auth.RoleType | Auth.RoleType[]> = {
|
||||
mounted(el, binding) {
|
||||
updateElVisible(el, binding.value);
|
||||
},
|
||||
beforeUpdate(el, binding) {
|
||||
updateElVisible(el, binding.value);
|
||||
}
|
||||
};
|
||||
|
||||
app.directive('login', loginDirective);
|
||||
app.directive('permission', permissionDirective);
|
||||
}
|
||||
|
@ -1,3 +1,11 @@
|
||||
/** 用户角色 */
|
||||
export enum EnumUserRole {
|
||||
super = '超级管理员',
|
||||
admin = '管理员',
|
||||
user = '普通用户'
|
||||
// custom = '自定义角色'
|
||||
}
|
||||
|
||||
/** 登录模块 */
|
||||
export enum EnumLoginModule {
|
||||
'pwd-login' = '账密登录',
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<soybean-admin-layout
|
||||
<admin-layout
|
||||
:mode="mode"
|
||||
:min-width="theme.layout.minWidth"
|
||||
:fixed-header-and-tab="theme.fixedHeaderAndTab"
|
||||
@ -25,12 +25,12 @@
|
||||
<template #footer>
|
||||
<global-footer />
|
||||
</template>
|
||||
</soybean-admin-layout>
|
||||
</admin-layout>
|
||||
<setting-drawer />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import SoybeanAdminLayout from 'soybean-admin-layout';
|
||||
import AdminLayout from '@soybeanjs/vue-admin-layout';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
import { useBasicLayout } from '@/composables';
|
||||
import { SettingDrawer, GlobalHeader, GlobalTab, GlobalSider, GlobalContent, GlobalFooter } from '../common';
|
||||
|
@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<hover-container tooltip-content="github" class="w-40px h-full" content-class="hover:text-primary">
|
||||
<a href="https://github.com/honghuangdc/soybean-admin" target="_blank" class="flex-center">
|
||||
<icon-mdi-github class="text-20px" />
|
||||
</a>
|
||||
<hover-container
|
||||
tooltip-content="github"
|
||||
class="w-40px h-full"
|
||||
content-class="hover:text-primary"
|
||||
@click="handleClickLink"
|
||||
>
|
||||
<icon-mdi-github class="text-20px" />
|
||||
</hover-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
function handleClickLink() {
|
||||
window.open('https://github.com/honghuangdc/soybean-admin', '_blank');
|
||||
}
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<hover-container class="w-40px h-full" @click="app.toggleSiderCollapse">
|
||||
<hover-container class="w-40px h-full" content-class="hover:text-primary" @click="app.toggleSiderCollapse">
|
||||
<icon-line-md-menu-unfold-left v-if="app.siderCollapse" class="text-16px" />
|
||||
<icon-line-md-menu-fold-left v-else class="text-16px" />
|
||||
</hover-container>
|
||||
|
@ -2,12 +2,12 @@
|
||||
<div class="mb-6px px-4px cursor-pointer" @mouseenter="setTrue" @mouseleave="setFalse">
|
||||
<div
|
||||
class="flex-center flex-col py-12px rounded-2px bg-transparent transition-colors duration-300 ease-in-out"
|
||||
:class="{ 'text-primary !bg-primary-active': isActive, 'text-primary': isHover }"
|
||||
:class="{ 'text-primary !bg-primary_active': isActive, 'text-primary': isHover }"
|
||||
>
|
||||
<component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" />
|
||||
<p
|
||||
class="pt-8px text-12px overflow-hidden transition-height duration-300 ease-in-out"
|
||||
:class="[isMini ? 'h-0 pt-0' : 'h-20px pt-8px']"
|
||||
class="text-12px overflow-hidden transition-height duration-300 ease-in-out"
|
||||
:class="[isMini ? 'h-0 pt-0' : 'h-24px pt-4px']"
|
||||
>
|
||||
{{ label }}
|
||||
</p>
|
||||
|
@ -28,7 +28,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, computed, nextTick, watch } from 'vue';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { ChromeTab, ButtonTab } from 'soybean-admin-tab';
|
||||
import { ChromeTab, ButtonTab } from '@soybeanjs/vue-admin-tab';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { useThemeStore, useTabStore } from '@/store';
|
||||
import { setTabRoutes } from '@/utils';
|
||||
|
@ -1,21 +1,26 @@
|
||||
import { createApp } from 'vue';
|
||||
import { setupImportAssets } from './plugins';
|
||||
import { setupAssets } from './plugins';
|
||||
import { setupStore } from './store';
|
||||
import { setupDirectives } from './directives';
|
||||
import { setupRouter } from './router';
|
||||
import App from './App.vue';
|
||||
|
||||
async function setupApp() {
|
||||
setupImportAssets();
|
||||
// import assets: js、css
|
||||
setupAssets();
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// store plugin: pinia
|
||||
setupStore(app);
|
||||
|
||||
// vue custom directives
|
||||
setupDirectives(app);
|
||||
|
||||
// vue router
|
||||
await setupRouter(app);
|
||||
|
||||
// mount app
|
||||
app.mount('#app');
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
import 'virtual:windi.css';
|
||||
import 'uno.css';
|
||||
import 'swiper/css';
|
||||
import 'swiper/css/navigation';
|
||||
import 'swiper/css/pagination';
|
||||
import '../styles/css/global.css';
|
||||
|
||||
/** import static assets: css, js , font and so on. - [引入静态资源,css、js和字体文件等] */
|
||||
export default function setupImportAssets() {
|
||||
export default function setupAssets() {
|
||||
//
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
import setupImportAssets from './assets';
|
||||
import setupAssets from './assets';
|
||||
|
||||
export { setupImportAssets };
|
||||
export { setupAssets };
|
||||
|
@ -16,7 +16,7 @@ export async function createDynamicRouteGuard(
|
||||
const isLogin = Boolean(getToken());
|
||||
|
||||
// 初始化权限路由
|
||||
if (!route.isInitedAuthRoute) {
|
||||
if (!route.isInitAuthRoute) {
|
||||
// 未登录情况下直接回到登录页,登录成功后再加载权限路由
|
||||
if (!isLogin) {
|
||||
if (to.name === routeName('login')) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import type { App } from 'vue';
|
||||
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router';
|
||||
import { transformAuthRoutesToVueRoutes } from '@/utils';
|
||||
import { transformAuthRoutesToVueRoutes, transformRouteNameToRoutePath } from '@/utils';
|
||||
import { constantRoutes } from './routes';
|
||||
import { scrollBehavior } from './helpers';
|
||||
import { createRouterGuard } from './guard';
|
||||
@ -20,5 +20,10 @@ export async function setupRouter(app: App) {
|
||||
await router.isReady();
|
||||
}
|
||||
|
||||
/** 路由名称 */
|
||||
export const routeName = (key: AuthRoute.RouteKey) => key;
|
||||
/** 路由路径 */
|
||||
export const routePath = (key: Exclude<AuthRoute.RouteKey, 'not-found-page'>) => transformRouteNameToRoutePath(key);
|
||||
|
||||
export * from './routes';
|
||||
export * from './modules';
|
||||
|
@ -6,9 +6,9 @@ const about: AuthRoute.Route = {
|
||||
title: '关于',
|
||||
requiresAuth: true,
|
||||
singleLayout: 'basic',
|
||||
permissions: ['super', 'admin', 'test'],
|
||||
permissions: ['super', 'admin', 'user'],
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 7
|
||||
order: 8
|
||||
}
|
||||
};
|
||||
|
||||
|
35
src/router/modules/auth-demo.ts
Normal file
35
src/router/modules/auth-demo.ts
Normal file
@ -0,0 +1,35 @@
|
||||
const authDemo: AuthRoute.Route = {
|
||||
name: 'auth-demo',
|
||||
path: '/auth-demo',
|
||||
component: 'basic',
|
||||
children: [
|
||||
{
|
||||
name: 'auth-demo_permission',
|
||||
path: '/auth-demo/permission',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo_super',
|
||||
path: '/auth-demo/super',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
requiresAuth: true,
|
||||
permissions: ['super'],
|
||||
icon: 'ic:round-supervisor-account'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
};
|
||||
|
||||
export default authDemo;
|
@ -37,7 +37,7 @@ const exception: AuthRoute.Route = {
|
||||
meta: {
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 5
|
||||
order: 6
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5,7 +5,7 @@ export const constantRoutes: AuthRoute.Route[] = [
|
||||
{
|
||||
name: 'root',
|
||||
path: '/',
|
||||
redirect: '/dashboard/analysis',
|
||||
redirect: import.meta.env.VITE_ROUTE_HOME_PATH,
|
||||
meta: {
|
||||
title: 'Root'
|
||||
}
|
||||
@ -64,16 +64,3 @@ export const constantRoutes: AuthRoute.Route[] = [
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
/** 路由名称 */
|
||||
export const routeName = (key: AuthRoute.RouteKey) => key;
|
||||
|
||||
/** 路由路径 */
|
||||
export function routePath(key: Exclude<AuthRoute.RouteKey, 'not-found-page'>): AuthRoute.RoutePath {
|
||||
const rootPath: AuthRoute.RoutePath = '/';
|
||||
if (key === 'root') return rootPath;
|
||||
const splitMark: AuthRoute.RouteSplitMark = '_';
|
||||
const pathSplitMark = '/';
|
||||
const path = key.split(splitMark).join(pathSplitMark);
|
||||
return (pathSplitMark + path) as AuthRoute.RoutePath;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { createRequest } from './request';
|
||||
import { getEnvConfig } from '~/.env-config';
|
||||
|
||||
const { http } = getEnvConfig(import.meta.env);
|
||||
const envConfig = getEnvConfig(import.meta.env);
|
||||
const isHttpProxy = import.meta.env.VITE_HTTP_PROXY === 'true';
|
||||
|
||||
export const request = createRequest({ baseURL: isHttpProxy ? http.proxy : http.url });
|
||||
export const request = createRequest({ baseURL: isHttpProxy ? envConfig.proxy : envConfig.url });
|
||||
|
||||
export const mockRequest = createRequest({ baseURL: '/mock' });
|
||||
|
@ -87,6 +87,9 @@ export const useAuthStore = defineStore('auth-store', {
|
||||
await this.loginByToken(data);
|
||||
}
|
||||
this.loginLoading = false;
|
||||
},
|
||||
updateUserRole(userRole: Auth.RoleType) {
|
||||
this.userInfo.userRole = userRole;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -7,8 +7,11 @@ import {
|
||||
transformAuthRouteToMenu,
|
||||
transformAuthRoutesToVueRoutes,
|
||||
transformAuthRoutesToSearchMenus,
|
||||
getCacheRoutes
|
||||
getCacheRoutes,
|
||||
filterAuthRoutesByUserPermission,
|
||||
transformRoutePathToRouteName
|
||||
} from '@/utils';
|
||||
import { useAuthStore } from '../auth';
|
||||
import { useTabStore } from '../tab';
|
||||
|
||||
interface RouteState {
|
||||
@ -19,7 +22,7 @@ interface RouteState {
|
||||
*/
|
||||
authRouteMode: ImportMetaEnv['VITE_AUTH_ROUTE_MODE'];
|
||||
/** 是否初始化了权限路由 */
|
||||
isInitedAuthRoute: boolean;
|
||||
isInitAuthRoute: boolean;
|
||||
/** 路由首页name(前端静态路由时生效,后端动态路由该值会被后端返回的值覆盖) */
|
||||
routeHomeName: AuthRoute.RouteKey;
|
||||
/** 菜单 */
|
||||
@ -33,8 +36,8 @@ interface RouteState {
|
||||
export const useRouteStore = defineStore('route-store', {
|
||||
state: (): RouteState => ({
|
||||
authRouteMode: import.meta.env.VITE_AUTH_ROUTE_MODE,
|
||||
isInitedAuthRoute: false,
|
||||
routeHomeName: 'dashboard_analysis',
|
||||
isInitAuthRoute: false,
|
||||
routeHomeName: transformRoutePathToRouteName(import.meta.env.VITE_ROUTE_HOME_PATH),
|
||||
menus: [],
|
||||
searchMenus: [],
|
||||
cacheRoutes: []
|
||||
@ -73,9 +76,9 @@ export const useRouteStore = defineStore('route-store', {
|
||||
* @param router - 路由实例
|
||||
*/
|
||||
async initStaticRoute(router: Router) {
|
||||
// 先根据用户权限过滤一下staticRoutes
|
||||
|
||||
this.handleAuthRoutes(staticRoutes, router);
|
||||
const auth = useAuthStore();
|
||||
const routes = filterAuthRoutesByUserPermission(staticRoutes, auth.userInfo.userRole);
|
||||
this.handleAuthRoutes(routes, router);
|
||||
},
|
||||
/**
|
||||
* 初始化权限路由
|
||||
@ -84,6 +87,7 @@ export const useRouteStore = defineStore('route-store', {
|
||||
async initAuthRoute(router: Router) {
|
||||
const { initHomeTab } = useTabStore();
|
||||
const { userId } = getUserInfo();
|
||||
|
||||
if (!userId) return;
|
||||
|
||||
const isDynamicRoute = this.authRouteMode === 'dynamic';
|
||||
@ -94,7 +98,8 @@ export const useRouteStore = defineStore('route-store', {
|
||||
}
|
||||
|
||||
initHomeTab(this.routeHomeName, router);
|
||||
this.isInitedAuthRoute = true;
|
||||
|
||||
this.isInitAuthRoute = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -21,7 +21,7 @@ export const useTabStore = defineStore('tab-store', {
|
||||
name: 'root',
|
||||
path: '/',
|
||||
meta: {
|
||||
title: 'root'
|
||||
title: 'Root'
|
||||
},
|
||||
scrollPosition: {
|
||||
left: 0,
|
||||
@ -53,7 +53,8 @@ export const useTabStore = defineStore('tab-store', {
|
||||
initHomeTab(routeHomeName: string, router: Router) {
|
||||
const routes = router.getRoutes();
|
||||
const findHome = routes.find(item => item.name === routeHomeName);
|
||||
if (findHome) {
|
||||
if (findHome && !findHome.children.length) {
|
||||
// 有子路由的不能作为Tab
|
||||
this.homeTab = getTabRouteByVueRoute(findHome);
|
||||
}
|
||||
},
|
||||
@ -165,16 +166,20 @@ export const useTabStore = defineStore('tab-store', {
|
||||
iniTabStore(currentRoute: RouteLocationNormalizedLoaded) {
|
||||
const theme = useThemeStore();
|
||||
|
||||
const isHome = currentRoute.path === this.homeTab.path;
|
||||
const tabs: GlobalTabRoute[] = theme.tab.isCache ? getTabRoutes() : [];
|
||||
|
||||
const hasHome = isInTabRoutes(tabs, this.homeTab.path);
|
||||
const hasCurrent = isInTabRoutes(tabs, currentRoute.path);
|
||||
if (!hasHome) {
|
||||
if (!hasHome && this.homeTab.name !== 'root') {
|
||||
tabs.unshift(this.homeTab);
|
||||
}
|
||||
|
||||
const isHome = currentRoute.path === this.homeTab.path;
|
||||
const hasCurrent = isInTabRoutes(tabs, currentRoute.path);
|
||||
if (!isHome && !hasCurrent) {
|
||||
tabs.push(getTabRouteByVueRoute(currentRoute));
|
||||
const currentTab = getTabRouteByVueRoute(currentRoute);
|
||||
tabs.push(currentTab);
|
||||
}
|
||||
|
||||
this.tabs = tabs;
|
||||
this.setActiveTab(currentRoute.path);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
@import './transition.css';
|
||||
@import './reset.css';
|
||||
|
||||
html, body, #app {
|
||||
height: 100%;
|
||||
|
366
src/styles/css/reset.css
Normal file
366
src/styles/css/reset.css
Normal file
@ -0,0 +1,366 @@
|
||||
/*
|
||||
1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4)
|
||||
2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116)
|
||||
*/
|
||||
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box; /* 1 */
|
||||
border-width: 0; /* 2 */
|
||||
border-style: solid; /* 2 */
|
||||
border-color: currentColor; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use a consistent sensible line-height in all browsers.
|
||||
2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
3. Use a more readable tab size.
|
||||
4. Use the user's configured `sans` font-family by default.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.5; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
-moz-tab-size: 4; /* 3 */
|
||||
tab-size: 4; /* 3 */
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; /* 4 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove the margin in all browsers.
|
||||
2. Inherit line-height from `html` so users can set them as a class directly on the `html` element.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0; /* 1 */
|
||||
line-height: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Add the correct height in Firefox.
|
||||
2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655)
|
||||
3. Ensure horizontal rules are visible by default.
|
||||
*/
|
||||
|
||||
hr {
|
||||
height: 0; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
border-top-width: 1px; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct text decoration in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
abbr:where([title]) {
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the default font size and weight for headings.
|
||||
*/
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Reset links to optimize for opt-in styling instead of opt-out.
|
||||
*/
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font weight in Edge and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Use the user's configured `mono` font family by default.
|
||||
2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp,
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent `sub` and `sup` elements from affecting the line height in all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297)
|
||||
2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016)
|
||||
3. Remove gaps between table borders by default.
|
||||
*/
|
||||
|
||||
table {
|
||||
text-indent: 0; /* 1 */
|
||||
border-color: inherit; /* 2 */
|
||||
border-collapse: collapse; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
1. Change the font styles in all browsers.
|
||||
2. Remove the margin in Firefox and Safari.
|
||||
3. Remove default padding in all browsers.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: inherit; /* 1 */
|
||||
color: inherit; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
padding: 0; /* 3 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inheritance of text transform in Edge and Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Remove default button styles.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'] {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
/* background-color: transparent; 2 */
|
||||
background-image: none; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Use the modern Firefox focus style for all focusable elements.
|
||||
*/
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737)
|
||||
*/
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct vertical alignment in Chrome and Firefox.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/*
|
||||
Correct the cursor style of increment and decrement buttons in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the odd appearance in Chrome and Safari.
|
||||
2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Correct the inability to style clickable types in iOS and Safari.
|
||||
2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Add the correct display in Chrome and Safari.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/*
|
||||
Removes the default spacing and border for appropriate elements.
|
||||
*/
|
||||
|
||||
blockquote,
|
||||
dl,
|
||||
dd,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
figure,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
menu {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Prevent resizing textareas horizontally by default.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300)
|
||||
2. Set the default placeholder color to the user's configured gray 400 color.
|
||||
*/
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1; /* 1 */
|
||||
color: #9ca3af; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Set the default cursor for buttons.
|
||||
*/
|
||||
|
||||
button,
|
||||
[role="button"] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*
|
||||
Make sure disabled buttons don't get the pointer cursor.
|
||||
*/
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/*
|
||||
1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210)
|
||||
This can trigger a poorly considered lint error in some tools but is included by design.
|
||||
*/
|
||||
|
||||
img,
|
||||
svg,
|
||||
video,
|
||||
canvas,
|
||||
audio,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
display: block; /* 1 */
|
||||
vertical-align: middle; /* 2 */
|
||||
}
|
||||
|
||||
/*
|
||||
Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14)
|
||||
*/
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
Ensure the default browser behavior of the `hidden` attribute.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dark {
|
||||
color-scheme: dark;
|
||||
}
|
6
src/typings/business.d.ts
vendored
6
src/typings/business.d.ts
vendored
@ -4,10 +4,10 @@ declare namespace Auth {
|
||||
* 用户角色类型(前端静态路由用角色类型进行路由权限的控制)
|
||||
* - super: 超级管理员(该权限具有所有路由数据)
|
||||
* - admin: 管理员
|
||||
* - test: 测试
|
||||
* - normal: 普通用户
|
||||
* - user: 用户
|
||||
* - custom: 自定义角色
|
||||
*/
|
||||
type RoleType = 'super' | 'admin' | 'test' | 'normal';
|
||||
type RoleType = keyof typeof import('@/enum').EnumUserRole;
|
||||
|
||||
/** 用户信息 */
|
||||
interface UserInfo {
|
||||
|
103
src/typings/components.d.ts
vendored
Normal file
103
src/typings/components.d.ts
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
// generated by unplugin-vue-components
|
||||
// We suggest you to commit this file into source control
|
||||
// Read more: https://github.com/vuejs/vue-next/pull/3399
|
||||
import '@vue/runtime-core'
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
export interface GlobalComponents {
|
||||
BetterScroll: typeof import('./../components/custom/BetterScroll.vue')['default']
|
||||
CountTo: typeof import('./../components/custom/CountTo.vue')['default']
|
||||
DarkModeContainer: typeof import('./../components/common/DarkModeContainer.vue')['default']
|
||||
DarkModeSwitch: typeof import('./../components/common/DarkModeSwitch.vue')['default']
|
||||
GithubLink: typeof import('./../components/custom/GithubLink.vue')['default']
|
||||
HoverContainer: typeof import('./../components/common/HoverContainer.vue')['default']
|
||||
IconAntDesignCloseOutlined: typeof import('~icons/ant-design/close-outlined')['default']
|
||||
IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
|
||||
IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
|
||||
IconCustomActivity: typeof import('~icons/custom/activity')['default']
|
||||
IconCustomAvatar: typeof import('~icons/custom/avatar')['default']
|
||||
IconCustomBanner: typeof import('~icons/custom/banner')['default']
|
||||
IconCustomCast: typeof import('~icons/custom/cast')['default']
|
||||
IconCustomEmptyData: typeof import('~icons/custom/empty-data')['default']
|
||||
IconCustomLogo: typeof import('~icons/custom/logo')['default']
|
||||
IconCustomLogoFill: typeof import('~icons/custom/logo-fill')['default']
|
||||
IconCustomNetworkError: typeof import('~icons/custom/network-error')['default']
|
||||
IconCustomNoPermission: typeof import('~icons/custom/no-permission')['default']
|
||||
IconCustomNotFound: typeof import('~icons/custom/not-found')['default']
|
||||
IconCustomServiceError: typeof import('~icons/custom/service-error')['default']
|
||||
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
|
||||
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
|
||||
IconIcOutlineCheck: typeof import('~icons/ic/outline-check')['default']
|
||||
IconLineMdMenuFoldLeft: typeof import('~icons/line-md/menu-fold-left')['default']
|
||||
IconLineMdMenuUnfoldLeft: typeof import('~icons/line-md/menu-unfold-left')['default']
|
||||
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
|
||||
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
|
||||
IconMdiClose: typeof import('~icons/mdi/close')['default']
|
||||
IconMdiGithub: typeof import('~icons/mdi/github')['default']
|
||||
IconMdiMoonWaningCrescent: typeof import('~icons/mdi/moon-waning-crescent')['default']
|
||||
IconMdiPin: typeof import('~icons/mdi/pin')['default']
|
||||
IconMdiPinOff: typeof import('~icons/mdi/pin-off')['default']
|
||||
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
|
||||
IconMdiWechat: typeof import('~icons/mdi/wechat')['default']
|
||||
IconMdiWhiteBalanceSunny: typeof import('~icons/mdi/white-balance-sunny')['default']
|
||||
IconPhCaretDoubleLeftBold: typeof import('~icons/ph/caret-double-left-bold')['default']
|
||||
IconPhCaretDoubleRightBold: typeof import('~icons/ph/caret-double-right-bold')['default']
|
||||
IconSelect: typeof import('./../components/custom/IconSelect.vue')['default']
|
||||
IconUilSearch: typeof import('~icons/uil/search')['default']
|
||||
ImageVerify: typeof import('./../components/custom/ImageVerify.vue')['default']
|
||||
LoadingEmptyWrapper: typeof import('./../components/business/LoadingEmptyWrapper.vue')['default']
|
||||
LoginAgreement: typeof import('./../components/business/LoginAgreement.vue')['default']
|
||||
NaiveProvider: typeof import('./../components/common/NaiveProvider.vue')['default']
|
||||
NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
|
||||
NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCard: typeof import('naive-ui')['NCard']
|
||||
NCheckbox: typeof import('naive-ui')['NCheckbox']
|
||||
NColorPicker: typeof import('naive-ui')['NColorPicker']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDataTable: typeof import('naive-ui')['NDataTable']
|
||||
NDescriptions: typeof import('naive-ui')['NDescriptions']
|
||||
NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
|
||||
NDialogProvider: typeof import('naive-ui')['NDialogProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NDrawer: typeof import('naive-ui')['NDrawer']
|
||||
NDrawerContent: typeof import('naive-ui')['NDrawerContent']
|
||||
NDropdown: typeof import('naive-ui')['NDropdown']
|
||||
NEmpty: typeof import('naive-ui')['NEmpty']
|
||||
NForm: typeof import('naive-ui')['NForm']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NGradientText: typeof import('naive-ui')['NGradientText']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NGridItem: typeof import('naive-ui')['NGridItem']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
NList: typeof import('naive-ui')['NList']
|
||||
NListItem: typeof import('naive-ui')['NListItem']
|
||||
NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
|
||||
NMenu: typeof import('naive-ui')['NMenu']
|
||||
NMessageProvider: typeof import('naive-ui')['NMessageProvider']
|
||||
NModal: typeof import('naive-ui')['NModal']
|
||||
NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
|
||||
NPopover: typeof import('naive-ui')['NPopover']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSelect: typeof import('naive-ui')['NSelect']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
NSpin: typeof import('naive-ui')['NSpin']
|
||||
NStatistic: typeof import('naive-ui')['NStatistic']
|
||||
NSwitch: typeof import('naive-ui')['NSwitch']
|
||||
NTabPane: typeof import('naive-ui')['NTabPane']
|
||||
NTabs: typeof import('naive-ui')['NTabs']
|
||||
NTag: typeof import('naive-ui')['NTag']
|
||||
NThing: typeof import('naive-ui')['NThing']
|
||||
NTimeline: typeof import('naive-ui')['NTimeline']
|
||||
NTimelineItem: typeof import('naive-ui')['NTimelineItem']
|
||||
NTooltip: typeof import('naive-ui')['NTooltip']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
SystemLogo: typeof import('./../components/common/SystemLogo.vue')['default']
|
||||
WebSiteLink: typeof import('./../components/custom/WebSiteLink.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
export {}
|
28
src/typings/env.d.ts
vendored
28
src/typings/env.d.ts
vendored
@ -1,12 +1,3 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue';
|
||||
|
||||
const component: DefineComponent<object, object, any>;
|
||||
export default component;
|
||||
}
|
||||
|
||||
/**
|
||||
* env环境类型
|
||||
* - dev: 后台开发环境
|
||||
@ -15,6 +6,16 @@ declare module '*.vue' {
|
||||
*/
|
||||
type EnvType = 'dev' | 'test' | 'prod';
|
||||
|
||||
/**
|
||||
* env环境配置
|
||||
*/
|
||||
interface EnvConfig {
|
||||
/** 请求地址 */
|
||||
url: string;
|
||||
/** 代理地址 */
|
||||
proxy: string;
|
||||
}
|
||||
|
||||
interface ImportMetaEnv {
|
||||
/** 项目基本地址 */
|
||||
readonly VITE_BASE_URL: string;
|
||||
@ -27,14 +28,21 @@ interface ImportMetaEnv {
|
||||
/**
|
||||
* 权限路由模式:
|
||||
* - static - 前端声明的静态
|
||||
* - dynamic - 后端返回的动态 */
|
||||
* - dynamic - 后端返回的动态
|
||||
*/
|
||||
readonly VITE_AUTH_ROUTE_MODE: 'static' | 'dynamic';
|
||||
/** 路由首页的路径 */
|
||||
readonly VITE_ROUTE_HOME_PATH: Exclude<AuthRoute.RoutePath, '/not-found-page' | '/:pathMatch(.*)*'>;
|
||||
/** vite环境类型 */
|
||||
readonly VITE_ENV_TYPE?: EnvType;
|
||||
/** 开启请求代理 */
|
||||
readonly VITE_HTTP_PROXY?: 'true' | 'false';
|
||||
/** 是否开启打包文件大小结果分析 */
|
||||
readonly VITE_VISUALIZER?: 'true' | 'false';
|
||||
/** 是否开启打包压缩 */
|
||||
readonly VITE_COMPRESS?: 'true' | 'false';
|
||||
/** 压缩算法类型 */
|
||||
readonly VITE_COMPRESS_TYPE?: 'gzip' | 'brotliCompress' | 'deflate' | 'deflateRaw';
|
||||
/** hash路由模式 */
|
||||
readonly VITE_HASH_ROUTE?: 'true' | 'false';
|
||||
}
|
||||
|
2
src/typings/expose.d.ts
vendored
2
src/typings/expose.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
/** vue 的defineExpose导出的类型 */
|
||||
declare namespace Expose {
|
||||
interface BetterScroll {
|
||||
instance: import('@better-scroll/core');
|
||||
instance: import('@better-scroll/core').BScrollInstance;
|
||||
}
|
||||
}
|
||||
|
3
src/typings/route.d.ts
vendored
3
src/typings/route.d.ts
vendored
@ -36,6 +36,9 @@ declare namespace AuthRoute {
|
||||
| 'plugin_icon'
|
||||
| 'plugin_print'
|
||||
| 'plugin_swiper'
|
||||
| 'auth-demo'
|
||||
| 'auth-demo_permission'
|
||||
| 'auth-demo_super'
|
||||
| 'exception'
|
||||
| 'exception_403'
|
||||
| 'exception_404'
|
||||
|
8
src/typings/system.d.ts
vendored
8
src/typings/system.d.ts
vendored
@ -84,6 +84,14 @@ declare namespace Service {
|
||||
/** 接口消息 */
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** mock的响应option */
|
||||
interface MockOption {
|
||||
url: Record<string, any>;
|
||||
body: Record<string, any>;
|
||||
query: Record<string, any>;
|
||||
headers: Record<string, any>;
|
||||
}
|
||||
}
|
||||
|
||||
/** 主题相关类型 */
|
||||
|
@ -37,7 +37,7 @@ export function getUserInfo() {
|
||||
userId: '',
|
||||
userName: '',
|
||||
userPhone: '',
|
||||
userRole: 'test'
|
||||
userRole: 'user'
|
||||
};
|
||||
const userInfo: Auth.UserInfo = getLocal<Auth.UserInfo>(EnumStorageKey['user-info']) || emptyInfo;
|
||||
return userInfo;
|
||||
|
@ -1,16 +1,10 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
/**
|
||||
* 根据用户权限过滤路由
|
||||
* @param routes - 权限路由
|
||||
* @param permission - 权限
|
||||
*/
|
||||
export function filterAuthRoutesByUserPermission(routes: AuthRoute.Route[], permission: Auth.RoleType) {
|
||||
const filters: AuthRoute.Route[] = [];
|
||||
|
||||
routes.forEach(route => {
|
||||
filterAuthRouteByUserPermission(route, permission);
|
||||
});
|
||||
return filters;
|
||||
return routes.map(route => filterAuthRouteByUserPermission(route, permission)).flat(1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -19,5 +13,12 @@ export function filterAuthRoutesByUserPermission(routes: AuthRoute.Route[], perm
|
||||
* @param permission - 权限
|
||||
*/
|
||||
function filterAuthRouteByUserPermission(route: AuthRoute.Route, permission: Auth.RoleType): AuthRoute.Route[] {
|
||||
return [];
|
||||
const hasPermission =
|
||||
!route.meta.permissions || permission === 'super' || route.meta.permissions.includes(permission);
|
||||
|
||||
if (route.children) {
|
||||
const filterChildren = route.children.map(item => filterAuthRouteByUserPermission(item, permission)).flat(1);
|
||||
Object.assign(route, { children: filterChildren });
|
||||
}
|
||||
return hasPermission ? [route] : [];
|
||||
}
|
||||
|
@ -31,6 +31,34 @@ export function transformAuthRoutesToSearchMenus(routes: AuthRoute.Route[], tree
|
||||
}, treeMap);
|
||||
}
|
||||
|
||||
/** 将路由名字转换成路由路径 */
|
||||
export function transformRouteNameToRoutePath(
|
||||
name: Exclude<AuthRoute.RouteKey, 'not-found-page'>
|
||||
): AuthRoute.RoutePath {
|
||||
const rootPath: AuthRoute.RoutePath = '/';
|
||||
if (name === 'root') return rootPath;
|
||||
|
||||
const splitMark: AuthRoute.RouteSplitMark = '_';
|
||||
const pathSplitMark = '/';
|
||||
const path = name.split(splitMark).join(pathSplitMark);
|
||||
|
||||
return (pathSplitMark + path) as AuthRoute.RoutePath;
|
||||
}
|
||||
|
||||
/** 将路由路径转换成路由名字 */
|
||||
export function transformRoutePathToRouteName(
|
||||
path: Exclude<AuthRoute.RoutePath, '/not-found-page' | '/:pathMatch(.*)*'>
|
||||
): AuthRoute.RouteKey {
|
||||
if (path === '/') return 'root';
|
||||
|
||||
const pathSplitMark = '/';
|
||||
const routeSplitMark: AuthRoute.RouteSplitMark = '_';
|
||||
|
||||
const name = path.split(pathSplitMark).slice(1).join(routeSplitMark) as AuthRoute.RouteKey;
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将单个权限路由转换成vue路由
|
||||
* @param item - 单个权限路由
|
||||
|
@ -1,6 +1,7 @@
|
||||
export * from './module';
|
||||
export * from './helpers';
|
||||
export * from './cache';
|
||||
export * from './auth';
|
||||
export * from './menu';
|
||||
export * from './breadcrumb';
|
||||
export * from './tab';
|
||||
|
65
src/views/auth-demo/permission/index.vue
Normal file
65
src/views/auth-demo/permission/index.vue
Normal file
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="权限切换" class="h-full shadow-sm rounded-16px">
|
||||
<div class="pb-12px">
|
||||
<n-gradient-text type="primary" :size="20">当前用户的权限:{{ auth.userInfo.userRole }}</n-gradient-text>
|
||||
</div>
|
||||
<n-select
|
||||
:value="auth.userInfo.userRole"
|
||||
class="w-120px"
|
||||
size="small"
|
||||
:options="options"
|
||||
@update:value="auth.updateUserRole"
|
||||
/>
|
||||
<div class="py-12px">
|
||||
<n-gradient-text type="primary" :size="20">权限指令 v-permission</n-gradient-text>
|
||||
</div>
|
||||
<div>
|
||||
<n-button v-permission="'super'" class="mr-12px">super可见</n-button>
|
||||
<n-button v-permission="'admin'" class="mr-12px">admin可见</n-button>
|
||||
<n-button v-permission="['admin', 'user']">admin和test可见</n-button>
|
||||
</div>
|
||||
<div class="py-12px">
|
||||
<n-gradient-text type="primary" :size="20">权限函数 hasPermission</n-gradient-text>
|
||||
</div>
|
||||
<n-space>
|
||||
<n-button v-if="hasPermission('super')">super可见</n-button>
|
||||
<n-button v-if="hasPermission('admin')">admin可见</n-button>
|
||||
<n-button v-if="hasPermission(['admin', 'user'])">admin和user可见</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import type { SelectOption } from 'naive-ui';
|
||||
import { EnumUserRole } from '@/enum';
|
||||
import { useAppStore, useAuthStore } from '@/store';
|
||||
import { usePermission } from '@/composables';
|
||||
|
||||
const app = useAppStore();
|
||||
const auth = useAuthStore();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
interface RoleList {
|
||||
label: string;
|
||||
value: keyof typeof EnumUserRole;
|
||||
}
|
||||
|
||||
const roleList: RoleList[] = [
|
||||
{ label: EnumUserRole.super, value: 'super' },
|
||||
{ label: EnumUserRole.admin, value: 'admin' },
|
||||
{ label: EnumUserRole.user, value: 'user' }
|
||||
];
|
||||
|
||||
const options: SelectOption[] = roleList as unknown as SelectOption[];
|
||||
|
||||
watch(
|
||||
() => auth.userInfo.userRole,
|
||||
async () => {
|
||||
app.reloadPage();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style scoped></style>
|
8
src/views/auth-demo/super/index.vue
Normal file
8
src/views/auth-demo/super/index.vue
Normal file
@ -0,0 +1,8 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="当前页面只有super才能看到" class="h-full shadow-sm rounded-16px"> </n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
<style scoped></style>
|
@ -14,12 +14,13 @@ const domRef = ref<HTMLDivElement>();
|
||||
async function renderBaiduMap() {
|
||||
if (!domRef.value) return;
|
||||
await load(true);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const map = new AMap.Map(domRef.value, {
|
||||
zoom: 11,
|
||||
center: [114.05834626586915, 22.546789983033168],
|
||||
viewMode: '3D'
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('map: ', map);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -14,12 +14,13 @@ const domRef = ref<HTMLDivElement | null>(null);
|
||||
async function renderBaiduMap() {
|
||||
if (!domRef.value) return;
|
||||
await load(true);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const map = new TMap.Map(domRef.value, {
|
||||
center: new TMap.LatLng(39.98412, 116.307484),
|
||||
zoom: 11,
|
||||
viewMode: '3D'
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('map: ', map);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -26,8 +26,8 @@ function printTable() {
|
||||
function printImage() {
|
||||
printJS({
|
||||
printable: [
|
||||
'https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/qq_qrcode.JPG',
|
||||
'https://raw.githubusercontent.com/honghuangdc/project-assets/main/img/qq_qrcode.JPG'
|
||||
'https://i.loli.net/2021/11/24/1J6REWXiHomU2kM.jpg',
|
||||
'https://i.loli.net/2021/11/24/1J6REWXiHomU2kM.jpg'
|
||||
],
|
||||
type: 'image',
|
||||
header: 'Multiple Images',
|
||||
|
@ -6,7 +6,7 @@
|
||||
@update:dark="theme.setDarkMode"
|
||||
/>
|
||||
<n-card :bordered="false" size="large" class="z-4 !w-auto rounded-20px shadow-sm">
|
||||
<div class="w-360px <sm:w-300px">
|
||||
<div class="w-300px sm:w-360px">
|
||||
<header class="flex-y-center justify-between">
|
||||
<div class="w-70px h-70px rounded-35px overflow-hidden">
|
||||
<system-logo class="text-70px text-primary" :fill="true" />
|
||||
|
Reference in New Issue
Block a user