界面调整

This commit is contained in:
joylink_fanyuhong 2024-09-14 15:14:48 +08:00
parent 6f6fd11d58
commit c402939b3a
5 changed files with 283 additions and 66 deletions

@ -1 +1 @@
Subproject commit 9218ddcf90a543ca84c7240480a12dd08ceb2b1d Subproject commit 5cb82e750f4f4e6d9523ab9a8a24a4148d12950a

View File

@ -0,0 +1,93 @@
<template>
<q-list bordered separator>
<div v-for="(menu, ii) in list" :key="ii">
<div v-if="menu.children">
<q-expansion-item :icon="menu.icon" :label="menu.label">
<q-card>
<q-list bordered separator>
<div v-for="(item, index) in menu.children" :key="index">
<q-item
clickable
:to="menu.path + '/' + item.path"
exact
:inset-level="1"
>
<q-item-section avatar>
<q-icon :name="item.icon"></q-icon>
</q-item-section>
<q-item-section>
<q-item-label>{{ item.label }}</q-item-label>
</q-item-section>
</q-item>
</div>
</q-list>
</q-card>
</q-expansion-item>
</div>
<div v-else>
<q-item clickable :to="menu.path" exact>
<q-item-section avatar>
<q-icon :name="menu.icon"></q-icon>
</q-item-section>
<q-item-section>
<q-item-label>{{ menu.label }}</q-item-label>
</q-item-section>
</q-item>
</div>
</div>
</q-list>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { RouteRecordRaw } from 'vue-router';
import routes from 'src/router/routes';
interface IMenu {
path: string;
label: string;
icon: string;
children?: IMenu[];
}
const list = ref<IMenu[]>([]);
onMounted(() => {
getList();
});
function getList() {
const arr: IMenu[] = [];
function getPathItem(item: RouteRecordRaw): IMenu | boolean {
if (item.meta?.hidden) {
return false;
}
const obj: IMenu = {
path: item.path,
label: (item.meta?.label as string) || '',
icon: (item.meta?.icon as string) || '',
};
if (item.children) {
const child: IMenu[] = [];
item.children.forEach((ii) => {
const childItem = getPathItem(ii);
if (childItem) {
child.push(childItem as IMenu);
}
});
if (child.length) {
Object.assign(obj, { children: child });
}
}
return obj;
}
routes.forEach((item) => {
const pItem = getPathItem(item);
if (pItem) {
arr.push(pItem as IMenu);
}
});
list.value = arr;
}
</script>

View File

@ -0,0 +1,21 @@
const JwtTokenKey = 'jwttoken';
export function saveJwtToken(token: string) {
sessionStorage.setItem(JwtTokenKey, `Bearer ${token}`);
}
export function getJwtToken(): string | null {
return sessionStorage.getItem(JwtTokenKey);
}
export function clearJwtToken(): void {
sessionStorage.removeItem(JwtTokenKey);
}
export function getOnlyToken(): string | null {
// 去除token前的Bearer
let t = sessionStorage.getItem(JwtTokenKey);
const Rxg = /^Bearer /;
t = t ? t.replace(Rxg, '') : '';
return t;
}

View File

@ -1,6 +1,7 @@
<template> <template>
<q-layout view="lHh Lpr lFf"> <q-layout view="hHh Lpr lFf">
<q-header elevated> <q-header elevated>
<q-resize-observer @resize="onHeaderResize" />
<q-toolbar> <q-toolbar>
<q-btn <q-btn
flat flat
@ -11,87 +12,173 @@
@click="toggleLeftDrawer" @click="toggleLeftDrawer"
/> />
<q-toolbar-title> Quasar App </q-toolbar-title> <q-toolbar-title> 北京玖琏 </q-toolbar-title>
<div>Quasar v{{ $q.version }}</div> <div class="q-gutter-sm no-wrap">
<q-btn
round
dense
flat
color="white"
:icon="$q.fullscreen.isActive ? 'fullscreen_exit' : 'fullscreen'"
@click="$q.fullscreen.toggle()"
v-if="$q.screen.gt.sm"
>
</q-btn>
<q-btn-dropdown flat stretch icon="person">
<q-list>
<q-item clickable v-close-popup @click="showUserInfo">
<q-item-section>
<q-item-label>用户信息</q-item-label>
</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="logOut">
<q-item-section>
<q-item-label>登出</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
</q-toolbar> </q-toolbar>
</q-header> </q-header>
<q-drawer v-model="leftDrawerOpen" show-if-above bordered> <q-drawer v-model="leftDrawerOpen" show-if-above bordered>
<q-list> <q-resize-observer @resize="onLeftResize" />
<q-item-label header> Essential Links </q-item-label> <sys-menu></sys-menu>
<EssentialLink
v-for="link in linksList"
:key="link.title"
v-bind="link"
/>
</q-list>
</q-drawer> </q-drawer>
<q-page-container> <q-page-container>
<router-view /> <q-resize-observer @resize="onResize" />
<q-scroll-area :style="{ height: scrollHeight + 'px' }">
<router-view :sizeHeight="scrollHeight" :sizeWidth="scrollWidth" />
</q-scroll-area>
</q-page-container> </q-page-container>
<q-dialog v-model="showInfo">
<q-card style="width: 400px; max-width: 80vw">
<q-card-section>
<div class="text-h6">用户信息</div>
</q-card-section>
<q-card-section class="q-pt-none">
<q-list>
<q-item>
<q-item-section>
<q-item-label caption>ID</q-item-label>
</q-item-section>
<q-item-section avatar>
<q-item-label>{{ userInfo?.id }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>用户名</q-item-label>
</q-item-section>
<q-item-section avatar>
<q-item-label>{{ userInfo?.name }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>角色</q-item-label>
</q-item-section>
<q-item-section>
<q-item-label avatar style="text-align: right">
{{ getRoleName(userInfo?.roles) }}
</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>电话</q-item-label>
</q-item-section>
<q-item-section avatar>
<q-item-label>{{ userInfo?.mobile }}</q-item-label>
</q-item-section>
</q-item>
<q-item>
<q-item-section>
<q-item-label caption>注册时间</q-item-label>
</q-item-section>
<q-item-section avatar>
<q-item-label>{{ userInfo?.register_time }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-card-section>
<q-card-actions align="right">
<q-btn flat label="确定" color="primary" @click="closeShowInfo" />
</q-card-actions>
</q-card>
</q-dialog>
</q-layout> </q-layout>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref, reactive } from 'vue';
import EssentialLink, { import SysMenu from 'src/components/SysMenu.vue';
EssentialLinkProps, // import { useRouter } from 'vue-router';
} from 'components/EssentialLink.vue'; import { clearJwtToken } from 'src/configs/TokenManage';
import { Dialog } from 'quasar';
defineOptions({ // const router = useRouter();
name: 'MainLayout',
});
const linksList: EssentialLinkProps[] = [
{
title: 'Docs',
caption: 'quasar.dev',
icon: 'school',
link: 'https://quasar.dev',
},
{
title: 'Github',
caption: 'github.com/quasarframework',
icon: 'code',
link: 'https://github.com/quasarframework',
},
{
title: 'Discord Chat Channel',
caption: 'chat.quasar.dev',
icon: 'chat',
link: 'https://chat.quasar.dev',
},
{
title: 'Forum',
caption: 'forum.quasar.dev',
icon: 'record_voice_over',
link: 'https://forum.quasar.dev',
},
{
title: 'Twitter',
caption: '@quasarframework',
icon: 'rss_feed',
link: 'https://twitter.quasar.dev',
},
{
title: 'Facebook',
caption: '@QuasarFramework',
icon: 'public',
link: 'https://facebook.quasar.dev',
},
{
title: 'Quasar Awesome',
caption: 'Community Quasar projects',
icon: 'favorite',
link: 'https://awesome.quasar.dev',
},
];
const leftDrawerOpen = ref(false); const leftDrawerOpen = ref(false);
function toggleLeftDrawer() { function toggleLeftDrawer() {
leftDrawerOpen.value = !leftDrawerOpen.value; leftDrawerOpen.value = !leftDrawerOpen.value;
onResize();
}
const scrollHeight = ref(100);
const scrollWidth = ref(100);
function onResize() {
scrollHeight.value = window.innerHeight - headerSize.height;
scrollWidth.value =
window.innerWidth - (leftDrawerOpen.value ? leftDrawerSize.width : 0);
}
const headerSize = reactive({} as { width: number; height: number });
function onHeaderResize(size: { width: number; height: number }) {
headerSize.width = size.width;
headerSize.height = size.height;
}
const leftDrawerSize = reactive({} as { width: number; height: number });
function onLeftResize(size: { width: number; height: number }) {
leftDrawerSize.width = size.width;
leftDrawerSize.height = size.height;
}
const showInfo = ref(false);
const userInfo = null;
function showUserInfo() {
showInfo.value = true;
}
function closeShowInfo() {
showInfo.value = false;
}
function logOut() {
Dialog.create({
title: '登出确认',
message: '确认是否登出?',
cancel: true,
persistent: true,
}).onOk(() => {
clearJwtToken();
// router.push({ name: 'login' });
});
}
function getRoleName(list: { id: number; name: string }[] | undefined) {
let name = '';
if (list && list.length > 0) {
const arr = list.map((item) => {
return item.name;
});
name = arr.join('');
}
return name;
} }
</script> </script>

View File

@ -3,7 +3,17 @@ import { RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
{ {
path: '/', path: '/',
component: () => import('layouts/DrawLayout.vue'), redirect: '/home',
meta: {
hidden: true,
},
},
{
path: '/home',
component: () => import('layouts/MainLayout.vue'),
meta: {
hidden: true,
},
// children: [{ path: '', component: () => import('pages/IndexPage.vue') }], // children: [{ path: '', component: () => import('pages/IndexPage.vue') }],
}, },
@ -11,11 +21,17 @@ const routes: RouteRecordRaw[] = [
// but you can also remove it // but you can also remove it
{ {
path: '/:catchAll(.*)*', path: '/:catchAll(.*)*',
meta: {
hidden: true,
},
component: () => import('pages/ErrorNotFound.vue'), component: () => import('pages/ErrorNotFound.vue'),
}, },
{ {
path: '/cctvPainting', path: '/cctvPainting',
name: 'cctvPainting', name: 'cctvPainting',
meta: {
label: 'CCTV绘制',
},
component: () => import('layouts/CCTVDrawLayout.vue'), component: () => import('layouts/CCTVDrawLayout.vue'),
}, },
]; ];