VitePress 系列教程:自定义页面 #6
Tailwind css
安装依赖
shell
npm install -D tailwindcss postcss autoprefixer
初始化配置文件
shell
npx tailwindcss init -p
跳转配置文件内容
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./.vitepress/theme/components/*.vue",
"./**/*.md",
],
theme: {
extend: {},
},
plugins: [],
}
新增 custom.css
@tailwind base;
@tailwind components;
@tailwind utilities;
在 .vitepress/theme/comfig.js
中导入使用
import './css/custom.css'
集成 element-plus
安装依赖
shell
npm install element-plus
shell
npm install @element-plus/icons-vue
在 .vitepress/theme/index.js
中导入使用
js
import 'element-plus/dist/index.css';
import ElementPlus from 'element-plus';
export default {
extends: Theme,
Layout: () => {
return h(Theme.Layout, null, {})
},
enhanceApp({app, router, siteData}) {
// ...
app.use(ElementPlus);
}
}
首页显示登录头像
theme/components/Avatar.vue
vue
<script setup>
import {onMounted, ref} from "vue";
import {ElMessage} from "element-plus";
const is_login = ref(false)
onMounted(() => {
is_login.value = !!localStorage.getItem('jwt')
if (!is_login.value) {
ElMessage({
message: '请先登录',
type: 'warning',
})
} else {
url.value = 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png'
}
})
const url = ref('https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png')
const handleCommand = (command) => {
ElMessage(`click on item ${command}`)
switch (command) {
case 'login':
location.href = '/login'
break
case 'logout':
localStorage.removeItem('jwt')
location.reload()
break
}
}
</script>
<template>
<ClientOnly>
<Teleport to=".VPSocialLink:last-child">
<div class="VPAvatar avatar">
<el-dropdown @command="handleCommand">
<el-avatar :size='20' :src="url"/>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="login">登录</el-dropdown-item>
<el-dropdown-item command="logout">退出</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</Teleport>
</ClientOnly>
</template>
<style scoped>
.VPAvatar {
display: flex;
align-items: center;
}
.avatar::before {
margin-right: 8px;
margin-left: 8px;
width: 1px;
height: 24px;
background-color: var(--vp-c-divider);
content: "";
}
.el-dropdown-link {
cursor: pointer;
color: var(--el-color-primary);
display: flex;
align-items: center;
}
:deep(:focus-visible) {
outline: none;
}
</style>
挂载使用
import Avatar from './components/Avatar.vue'
export default {
extends: Theme,
Layout: () => {
return h(Theme.Layout, null, {
'nav-bar-content-after': () => h(Avatar),
})
},
enhanceApp({app, router, siteData}) {
// ...
}
}
自定义登录页
在 login.md 页面中使用
<template>
<el-row class="login-container">
<el-col :lg="16" :md="12" class="left">
<div>
<div>正心全栈编程-文档站</div>
<div>此站点是正心的全栈编程文档站点</div>
</div>
</el-col>
<el-col :lg="8" :md="12" class="right">
<h2 class="title">欢迎回来</h2>
<div>
<span class="line"></span>
<span>账号密码登录</span>
<span class="line"></span>
</div>
<el-form class="w-[250px]" ref="formRef" v-bind:model="form" :rules="rules">
<el-form-item prop="mobile">
<el-input placeholder="请输入手机号" v-model="form.mobile">
<template #prefix>
<el-icon>
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728="">
<path fill="currentColor"
d="M512 512a192 192 0 1 0 0-384 192 192 0 0 0 0 384zm0 64a256 256 0 1 1 0-512 256 256 0 0 1 0 512zm320 320v-96a96 96 0 0 0-96-96H288a96 96 0 0 0-96 96v96a32 32 0 1 1-64 0v-96a160 160 0 0 1 160-160h448a160 160 0 0 1 160 160v96a32 32 0 1 1-64 0z"></path>
</svg>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" show-password placeholder="请输入密码" v-model="form.password">
<template #prefix>
<el-icon>
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-ea893728="">
<path fill="currentColor"
d="M224 448a32 32 0 0 0-32 32v384a32 32 0 0 0 32 32h576a32 32 0 0 0 32-32V480a32 32 0 0 0-32-32H224zm0-64h576a96 96 0 0 1 96 96v384a96 96 0 0 1-96 96H224a96 96 0 0 1-96-96V480a96 96 0 0 1 96-96z"></path>
<path fill="currentColor"
d="M512 544a32 32 0 0 1 32 32v192a32 32 0 1 1-64 0V576a32 32 0 0 1 32-32zm192-160v-64a192 192 0 1 0-384 0v64h384zM512 64a256 256 0 0 1 256 256v128H256V320A256 256 0 0 1 512 64z"></path>
</svg>
</el-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="记住账号">
<el-switch v-model="remember"/>
</el-form-item>
<el-form-item>
<el-button round color="#626aef" class="w-[250px]" type="primary" @click="onSubmit">登 录</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</template>
<script setup>
import {reactive, ref, watch, onMounted} from "vue"
import {ElNotification} from "element-plus";
// 定义登录表单的数据
const form = reactive({
mobile: "",
password: ""
})
// 表单的引用对象
const formRef = ref(null)
// 校验规则
const rules = {
mobile: [
{
required: true,
message: "用户名不能为空",
trigger: "blur"
}
],
password: [
{
required: true,
message: "密码不能为空",
trigger: "blur"
}
]
}
// 点击登录的事件
// localStorage.setItem('jwt', 'Bearer xasdjssalsdfsadfasdf')
// location.href = '/'
// 点击登录的事件
const onSubmit = () => {
// 实现校验
formRef.value.validate((valid) => {
if (!valid) {
return false
}
fetch(`/login`,
{
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(form)
}
).then(response => response.json()).then(ret => {
console.log(ret)
if (ret.status === 'success') {
ElNotification({
title: '成功',
message: '登录成功,2s 之后进行跳转',
type: 'success',
})
setTimeout(function () {
localStorage.setItem('jwt', 'Bearer xasdjssalsdfsadfasdf')
location.href = ret?.next ? ret.next : '/'
}, 2000)
} else {
ElNotification({
title: '失败',
message: ret.message,
type: 'error',
})
}
}).catch(error => {
ElNotification({
title: '错误',
message: '网络有问题或者是服务器出问题,请联系管理员',
type: 'error',
})
})
})
}
const remember = ref(true)
watch(form, (old) => {
if (remember.value) {
localStorage.setItem('info', JSON.stringify(form))
} else {
localStorage.removeItem('info')
}
})
onMounted(() => {
Object.assign(form, JSON.parse(localStorage.getItem('info') || '{}'))
})
</script>
<style scoped>
.login-container {
min-height: 100vh;
background-color: #667eea;
}
.login-container .left,
.login-container .right {
display: flex;
justify-content: center;
align-items: center;
}
.login-container .right {
background-color: #ffffff;
flex-direction: column;
}
.left > div > div:first-child {
color: #ffffff;
font-size: 3rem;
line-height: 1;
font-weight: 700;
}
.left > div > div:last-child {
color: #E5E7EB;
font-size: 0.875rem;
}
.right .title {
font-size: 1.875rem;
line-height: 2.25rem;
font-weight: 700;
}
.right > div {
display: flex;
margin-top: 1.25rem;
margin-bottom: 1.25rem;
color: #D1D5DB;
justify-content: center;
align-items: center;
}
.right .line {
width: 4rem;
height: 1px;
background-color: #edf2f7;
}
</style>