This action will force synchronization from infraboard/go-course, which will overwrite any changes that you have made since you forked the repository, and can not be recovered!!!
Synchronous operation will process in the background and will refresh the page when finishing processing. Please be patient.
这里我们直接使用vue cli 初始化我们的骨架, 然后在此基础上进行修改
npm init vue@latest
Need to install the following packages:
create-vue@3.3.4
Ok to proceed? (y) y
Vue.js - The Progressive JavaScript Framework
✔ Project name: … vblog
✔ Add TypeScript? … No / Yes
✔ Add JSX Support? … No / Yes
✔ Add Vue Router for Single Page Application development? … No / Yes
✔ Add Pinia for state management? … No / Yes
✔ Add Vitest for Unit Testing? … No / Yes
✔ Add Cypress for both Unit and End-to-End testing? … No / Yes
✔ Add ESLint for code quality? … No / Yes
✔ Add Prettier for code formatting? … No / Yes
Scaffolding project in /Users/yumaojun/Workspace/Golang/go-course/extra/vblog...
Done. Now run:
cd vblog
npm install
npm run lint
npm run dev
检查源设置:
yrm ls
npm ---- https://registry.npmjs.org/
cnpm --- http://r.cnpmjs.org/
* taobao - https://registry.npm.taobao.org/
nj ----- https://registry.nodejitsu.com/
rednpm - http://registry.mirror.cqupt.edu.cn/
npmMirror https://skimdb.npmjs.com/registry/
edunpm - http://registry.enpmjs.org/
yarn --- https://registry.yarnpkg.com
如果不是,使用yrm use来进行切换
# 使用淘宝的源
> yrm use taobao
YARN Registry has been set to: https://registry.npm.taobao.org/
NPM Registry has been set to: https://registry.npm.taobao.org/
在做Home页面之前,先清理掉脚手架为我们生成的页面
<script setup>
import { RouterView } from "vue-router";
</script>
<template>
<RouterView />
</template>
<style scoped>
</style>
<script setup></script>
<template>
<main>Home</main>
</template>
base.css设置全局样式
* {
box-sizing: border-box;
}
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-size: 14px;
background-color: var(--color-bg-1);
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
}
#app {
width: 100%;
height: 100%;
}
main.css 只保留基础样式
@import "./base.css";
接下来就需要编写我们的业务页面, 这我们选择的UI组件为: Arco Design
# npm
npm install --save-dev @arco-design/web-vue
# yarn
yarn add --dev @arco-design/web-vue
在vue安装该插件
import { createApp } from 'vue'
import ArcoVue from '@arco-design/web-vue';
import App from './App.vue';
import '@arco-design/web-vue/dist/arco.css';
const app = createApp(App);
app.use(ArcoVue);
app.mount('#app');
修改HomeView, 引入buttom UI组件进行测试
<script setup></script>
<template>
<main>
<a-space>
<a-button type="primary">Primary</a-button>
<a-button>Secondary</a-button>
<a-button type="dashed">Dashed</a-button>
<a-button type="outline">Outline</a-button>
<a-button type="text">Text</a-button>
</a-space>
</main>
</template>
这里我们需要补充2种异常页面:
我们使用HTTP状态码 404组件进行封装
errors/NotFound.vue:
<template>
<div class="content">
<a-result class="result" status="404" :subtitle="'not found'">
<template #extra>
<a-button key="back" type="primary" @click="back"> 返回主页 </a-button>
</template>
</a-result>
</div>
</template>
<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
const back = () => {
// warning: Go to the node that has the permission
router.push({ name: "home" });
};
</script>
<style scoped lang="less">
.content {
position: absolute;
top: 50%;
left: 50%;
margin-left: -95px;
margin-top: -121px;
text-align: center;
}
</style>
这里我们使用到了less, 一种css编译器(css扩展), 因此需要安装less的编译器
npm install --save-dev less
最后我们在路由上补充上404路由
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: HomeView,
},
{
path: "/:pathMatch(.*)*",
name: "notFound",
component: () => import("@/views/errors/NotFound.vue"),
},
],
});
添加页面: errors/PermissionDeny.vue
<template>
<div class="content">
<a-result
class="result"
status="403"
:subtitle="'你无权限访问该页面, 请登陆后重试'"
>
<template #extra>
<a-button key="back" type="primary" @click="back"> 返回主页 </a-button>
</template>
</a-result>
</div>
</template>
<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
const back = () => {
// warning: Go to the node that has the permission
router.push({ name: "home" });
};
</script>
<style scoped lang="less">
.content {
position: absolute;
top: 50%;
left: 50%;
margin-left: -95px;
margin-top: -121px;
text-align: center;
}
</style>
补充路由:
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: HomeView,
},
{
path: "/errors/403",
name: "PermissionDeny",
component: () => import("@/views/errors/PermissionDeny.vue"),
},
{
path: "/:pathMatch(.*)*",
name: "NotFound",
component: () => import("@/views/errors/NotFound.vue"),
},
],
});
界面分为前台和管理后台
博客前台:
博客管理后台:
因为前台与后台布局样式不一样, 因此分别使用独立布局模版.
前台布局模版: FrontendLayout.vue
<script setup>
import { RouterView } from "vue-router";
</script>
<template>
<div>
<!-- 顶部导航 -->
<div class="header">
<div class="logo">我的博客</div>
<div class="right-header">
<div>
<!-- 登录后台进行博客管理 -->
<a-button size="mini" type="text">后台管理</a-button>
</div>
</div>
</div>
<!-- 显示博客列表 -->
<div class="content">
<RouterView />
</div>
</div>
</template>
<style scoped>
.header {
display: flex;
align-content: center;
justify-content: flex-start;
align-items: center;
border-bottom: 1px solid rgb(229, 230, 235);
height: 45px;
}
.logo {
margin-left: 8px;
font-size: 14px;
font-weight: 500;
}
.right-header {
margin-left: auto;
}
.content {
margin: 20px;
display: flex;
align-content: center;
justify-content: center;
align-items: center;
}
</style>
补充一个前台展位页面: frontend/BlogView.vue
<script setup></script>
<template>
<main>
博客页面
</main>
</template>
{
path: "/",
name: "home",
redirect: "/frontend",
},
{
path: "/frontend",
name: "frontend",
component: FrontendLayout,
children: [
{
path: "",
name: "frontend",
component: BlogView,
},
],
},
博客后台使用的布局模版: BackendLayout.vue
这里我们需要使用到侧边栏导航:Arco Design菜单 Menu
<script setup>
import { RouterView, useRouter } from "vue-router";
const router = useRouter();
const clickMenu = (key) => {
router.push(key);
};
</script>
<template>
<div>
<div class="header">
<div class="logo">我的博客</div>
<div class="right-header">
<div>
<a-button size="mini" type="text">前台</a-button>
</div>
</div>
</div>
<div class="main">
<div class="sidebar">
<a-menu
:style="{ width: '200px', height: '100%' }"
:default-open-keys="['0']"
:default-selected-keys="['/backend/blogs']"
show-collapse-button
breakpoint="xl"
@menu-item-click="clickMenu"
>
<a-sub-menu key="0">
<template #icon><icon-apps></icon-apps></template>
<template #title>文章管理</template>
<a-menu-item key="/backend/blogs">文章列表</a-menu-item>
<a-menu-item key="/backend/tags">标签管理</a-menu-item>
</a-sub-menu>
</a-menu>
</div>
<div class="content">
<RouterView />
</div>
</div>
</div>
</template>
<style scoped>
.header {
display: flex;
align-content: center;
justify-content: flex-start;
align-items: center;
border-bottom: 1px solid rgb(229, 230, 235);
height: 45px;
}
.logo {
margin-left: 8px;
font-size: 14px;
font-weight: 500;
}
.right-header {
margin-left: auto;
}
.main {
display: flex;
align-content: center;
justify-content: flex-start;
align-items: flex-start;
height: calc(100vh - 45px);
}
.sidebar {
height: 100%;
border-right: 1px solid rgb(229, 230, 235);
}
.content {
margin: 8px;
width: 100%;
height: 100%;
}
</style>
添加 backend/BlogList.vue
<script setup></script>
<template>
<main>博客列表</main>
</template>
添加 backend/TagList.vue
<script setup></script>
<template>
<main>标签列表</main>
</template>
{
path: "/backend",
name: "backend",
component: BackendLayout,
children: [
{
path: "blogs",
name: "BlogList",
component: BlogList,
},
{
path: "tags",
name: "TagList",
component: TagList,
},
],
},
无效任务 后台可以直接切换到前台:
layout/BackendLayout.vue
const jumpToFrontend = () => {
router.push("/frontend");
};
但是前台切换到后台,是需要认证的, 因此需要先做登录页面
这里我们将要使用到: 表单 Form
<template>
<div class="login-form">
<a-form
ref="loginForm"
:model="form"
:style="{ width: '400px', height: '100%', justifyContent: 'center' }"
@submit="handleSubmit"
>
<a-form-item>
<div class="title">登录博客管理后台</div>
</a-form-item>
<a-form-item
field="username"
label=""
:rules="[{ required: true, message: '请输入用户名' }]"
hide-asterisk
>
<a-input v-model="form.username" placeholder="请输入用户名">
<template #prefix>
<icon-user />
</template>
</a-input>
</a-form-item>
<a-form-item
field="password"
label=""
:rules="[{ required: true, message: '请输入密码' }]"
hide-asterisk
>
<a-input
type="password"
v-model="form.password"
placeholder="请输入密码"
>
<template #prefix>
<icon-lock />
</template>
</a-input>
</a-form-item>
<a-form-item>
<a-button style="width: 100%" type="primary" html-type="submit"
>登录</a-button
>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { reactive } from "vue";
import { Message } from "@arco-design/web-vue";
import { useRouter } from "vue-router";
const router = useRouter();
const form = reactive({
username: "",
password: ""
});
const handleSubmit = (data) => {
if (data.errors === undefined) {
let form = data.values;
if (form.username === "admin" && form.password === "123456") {
// 保存登录状态
localStorage.setItem("username", form.username);
localStorage.setItem("password", form.password);
// 登录成功直接跳转到后台页, 如果URL页面带有重定向参数, 则直接路由到重定向的页面
const { redirect, ...othersQuery } = router.currentRoute.value.query;
router.push({
name: redirect || "BlogList",
query: {
...othersQuery,
},
});
} else {
Message.error("用户名或者密码不正确");
}
}
};
</script>
<style scoped>
.login-form {
height: 100%;
display: flex;
align-content: center;
justify-content: center;
align-items: center;
}
.title {
display: flex;
justify-content: center;
align-items: center;
align-content: center;
width: 100%;
font-weight: 500;
}
</style>
添加路由:
{
path: "/login",
name: "LoginPage",
component: () => import("@/views/login/LoginPage.vue"),
},
前台页面添加登录跳转: fronend/FrontendLayout.vue:
<script setup>
import { RouterView, useRouter } from "vue-router";
const router = useRouter();
const jumpToBackend = () => {
const username = localStorage.getItem("username");
const password = localStorage.getItem("password");
if (
username !== null &&
password !== null &&
username !== "" &&
password !== ""
) {
// 直接跳转后台管理页面
router.push("/backend/blogs");
} else {
// 跳转去登录页面
router.push("/login");
}
};
</script>
有了登录我们也需要支持退出登录, 我们是通过localStorage来保持状态的, 因此删除对于数据就可以退出了
补充注销按钮: layout/BackendLayout.vue
<a-button @click="logout" size="mini" type="text">注销</a-button>
补充注销逻辑: 注销成功后 跳转到登录页面:
const logout = () => {
localStorage.removeItem("username");
localStorage.removeItem("password");
router.push("/login");
};
之前的流程 用户通过前台 跳转到后台管理时, 补充了认证, 但是如果用户直接通过URL访问后台管理页面喃?
这时候我们就需要做一个全局的导航守卫, 保护所有的backend的页面, 凡事访问到backend的页面 都需要检查登录状态, 避免用户直接绕开访问
关于导航守卫
单独起一个模块: router/permession.js 来定义导航守卫钩子函数
// 定义导航守卫
export async function beforeEachHandler(to, from, next) {
if (to.fullPath.indexOf("/backend") === 0) {
// 如果未登陆 重定向到登录页面, 并且把目标页面作为重定向参数传递下去
const username = localStorage.getItem("username");
const password = localStorage.getItem("password");
if (
username === null ||
password === null ||
username === "" ||
password === ""
) {
console.log("not login");
next({
path: "/login",
query: {
redirect: to.name,
...to.query,
},
});
} else {
// 已经登录的用户直接放行
next();
}
} else {
// 不属于/backend的页面 直接放开
next();
}
}
export function afterEachHandler(to, from) {
console.log(to, from);
}
然后在router/index.js中 添加该Hook到router实例上
import { beforeEachHandler, afterEachHandler } from "./permession";
// 补充导航守卫
router.beforeEach(beforeEachHandler);
router.afterEach(afterEachHandler);
export default router;
安装process bar库
npm install --save nprogress
进入页面前 开启进度条, 离开后结束进度条
import NProgress from "nprogress"; // progress bar
import "nprogress/nprogress.css"; // progress bar style
export async function beforeEachHandler(to, from, next) {
NProgress.start();
...
}
export function afterEachHandler(to, from) {
NProgress.done();
console.log(to, from);
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。