06 Vue 页面与后台布局:先看见一个后台系统
06 Vue 页面与后台布局:先看见一个后台系统
本章你会做出什么
你会清理 Vite 欢迎页面,接入 Element Plus、Pinia 和 Vue Router,创建登录页面、后台布局、403 与 404 页面。登录逻辑下一章接接口,本章先看得到完整页面结构。
先理解三个概念
1. 单文件组件
Vue 的 .vue 文件通常包含三个区域:
<script setup lang="ts">
页面数据和事件
</script>
<template>页面结构</template>
<style scoped>
只影响当前组件的样式
</style>
2. 路由页面
路由把浏览器地址与组件对应起来:访问 /login 显示登录页,访问 /dashboard 显示工作台。后台公共侧栏和顶部栏放在布局组件里,内部页面放入 <router-view />。
3. UI 组件库
Element Plus 已经提供输入框、按钮、卡片、菜单等后台常用组件。你需要关心业务与布局,而不是从零写每个控件。
本章最终目录变化
client/src/
App.vue
main.ts
router/index.ts
layouts/AppLayout.vue
styles/main.css
views/
LoginView.vue
DashboardPlaceholderView.vue
errors/ForbiddenView.vue
errors/NotFoundView.vue
一步一步操作
第 1 步:清理默认页面并建目录
mkdir client\src\router
mkdir client\src\layouts
mkdir client\src\styles
mkdir client\src\views
mkdir client\src\views\errors
第 2 步:创建应用入口
main.ts 将路由、状态管理和 Element Plus 注入整个应用。App.vue 只保留当前路由出口。
第 3 步:建立临时页面路由
本章为了观察布局允许直接访问 /dashboard。第 8 章会将它改为必须登录并依据权限动态注册。
第 4 步:创建后台布局
布局由侧栏品牌区、菜单区、顶部用户区、内容区组成。先显示假用户“演示账号”;下一章替换成登录用户数据。
关键文件完整代码
client/src/main.ts
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './styles/main.css'
const app = createApp(App)
app.use(createPinia())
app.use(ElementPlus)
app.use(router)
app.mount('#app')
client/src/App.vue
<template>
<router-view />
</template>
client/src/router/index.ts(布局阶段版本)
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/login', component: () => import('../views/LoginView.vue') },
{
path: '/',
component: () => import('../layouts/AppLayout.vue'),
children: [
{ path: '', redirect: '/dashboard' },
{ path: '/dashboard', component: () => import('../views/DashboardPlaceholderView.vue') }
]
},
{ path: '/403', component: () => import('../views/errors/ForbiddenView.vue') },
{ path: '/:pathMatch(.*)*', component: () => import('../views/errors/NotFoundView.vue') }
]
})
export default router
client/src/views/LoginView.vue
<script setup lang="ts">
import { reactive } from 'vue'
const form = reactive({ username: 'supervisor', password: '123456' })
</script>
<template>
<div class="login-page">
<section>
<p class="label">企业内部服务管理</p>
<h1>ServiceDesk</h1>
<p class="lead">统一接收、分配和追踪服务工单,让处理过程清晰可查。</p>
</section>
<el-card class="login-card" shadow="never">
<h2>欢迎登录</h2>
<el-form :model="form" label-position="top">
<el-form-item label="账号"><el-input v-model="form.username" /></el-form-item>
<el-form-item label="密码"><el-input v-model="form.password" type="password" show-password /></el-form-item>
<el-button type="primary" class="submit">登录系统</el-button>
</el-form>
<p class="hint">演示账号密码统一为 123456</p>
</el-card>
</div>
</template>
<style scoped>
.login-page {
min-height: 100%;
display: grid;
grid-template-columns: 1fr 420px;
align-items: center;
gap: 64px;
padding: 64px max(calc((100vw - 1100px) / 2), 40px);
background: #f2f6ff;
}
.label {
color: #2776eb;
font-weight: 600;
}
h1 {
font-size: 56px;
margin: 14px 0;
}
.lead {
color: #53657d;
font-size: 18px;
}
.login-card {
padding: 20px;
border-radius: 14px;
}
.submit {
width: 100%;
}
.hint {
margin-top: 20px;
color: #778398;
font-size: 13px;
}
</style>
client/src/layouts/AppLayout.vue
<template>
<el-container class="layout">
<el-aside class="sidebar" width="220px">
<div class="brand"><strong>ServiceDesk</strong><small>服务工单运营平台</small></div>
<el-menu router default-active="/dashboard">
<el-menu-item index="/dashboard">运营看板</el-menu-item>
</el-menu>
</el-aside>
<el-container>
<el-header class="topbar">
<el-breadcrumb separator="/"><el-breadcrumb-item>ServiceDesk</el-breadcrumb-item></el-breadcrumb>
<span>演示账号</span>
</el-header>
<el-main><router-view /></el-main>
</el-container>
</el-container>
</template>
<style scoped>
.layout {
height: 100%;
}
.sidebar {
background: #101c34;
}
.brand {
height: 68px;
padding: 18px;
color: #fff;
}
.brand strong,
.brand small {
display: block;
}
.brand small {
color: #aab6cc;
margin-top: 6px;
}
.topbar {
background: #fff;
display: flex;
align-items: center;
justify-content: space-between;
}
</style>
页面占位与错误页
client/src/views/DashboardPlaceholderView.vue:
<template>
<el-card
><h2>运营看板</h2>
<p>图表将在第 12 章加入。</p></el-card
>
</template>
client/src/views/errors/ForbiddenView.vue:
<template>
<el-result icon="warning" title="403" sub-title="你没有访问此页面的权限"
><template #extra><el-button @click="$router.push('/login')">返回登录</el-button></template></el-result
>
</template>
client/src/views/errors/NotFoundView.vue:
<template>
<el-result icon="info" title="404" sub-title="页面不存在"
><template #extra><el-button @click="$router.push('/login')">返回登录</el-button></template></el-result
>
</template>
client/src/styles/main.css
:root {
--sd-bg: #f4f7fc;
--sd-border: #e5eaf2;
--sd-brand: #2776eb;
color: #15243c;
font-family: 'Microsoft YaHei', Arial, sans-serif;
}
* {
box-sizing: border-box;
}
html,
body,
#app {
height: 100%;
margin: 0;
}
body {
background: var(--sd-bg);
}
启动并验证
npm run dev
打开 http://localhost:5173/login,应看到蓝白色登录区域。打开 http://localhost:5173/dashboard,应看到左侧栏、顶部栏和“图表将在第 12 章加入”的卡片。
常见报错与原因
| 现象 | 原因 | 处理 |
|---|---|---|
| Element Plus 组件没有样式 | 忘记导入 CSS | 确认 main.ts 导入 element-plus/dist/index.css |
| 页面全白 | 路由导入路径写错 | 查看浏览器控制台报错和实际文件路径 |
| 页面高度只占一小段 | 根元素未设高度 | 复制 html, body, #app 的样式 |
本章完成清单
- 登录页和后台布局均可打开。
- 我能指出
<router-view />的作用。 - 403 和 404 页面可手动访问查看。
- 我知道当前还没有真正登录限制。
面试时这一章能怎么讲
页面使用主布局承载导航与账户区域,业务页面作为子路由渲染;通用视觉与布局样式集中管理,后续权限路由只需要控制子页面注册,不需要重复编写外壳。