Skip to content

自定义页面

Tailwind css

安装依赖

shell
pnpm install -D tailwindcss postcss autoprefixer

初始化配置文件

shell
npx tailwindcss init -p

设置配置文件内容 tailwind.config.js

js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./.vitepress/theme/components/*.vue",	
    "./**/*.md",	
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

.vitepress/theme/css/ 中新增 custom.css

css
@tailwind base;
@tailwind components;
@tailwind utilities;

.vitepress/theme/comfig.js 中导入使用

js
import './css/custom.css'

docs/index.md 中添加代码并预览效果

markdown
<div class="bg-red-900">测试Tailwind css插件</div>

集成 element-plus

安装依赖

shell
pnpm install element-plus
pnpm install @element-plus/icons-vue

.vitepress/theme/index.js 中导入使用

js
import 'element-plus/dist/index.css';	
import ElementPlus from 'element-plus';	


export default {
    extends: DefaultTheme,
    Layout: () => {
        return h(DefaultTheme.Layout, null, {
            // https://vitepress.dev/guide/extending-default-theme#layout-slots
        })
    },
    enhanceApp({app, router, siteData}) {
        // ...
        app.use(ElementPlus);	
    }
}

设置好之后可以设置 首页显示登录头像 进行测试

首页显示登录头像

element-plus文档

.vitepress/theme/components/Avatar.vue
vue
<template>
  <ClientOnly>
    <!--  将其插入到的位置,如果定义了社交链接可以使用 VPSocialLink 没有则使用 content-body  -->
    <Teleport to=".content-body: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" v-if="!(is_login.value)">登录</el-dropdown-item>
               <el-dropdown-item command="logout" v-if="is_login.value">退出</el-dropdown-item>
             </el-dropdown-menu>
           </template>
         </el-dropdown>
       </div>
    </Teleport>
  </ClientOnly>
</template>

<script setup>
import {onMounted, ref} from "vue";
import {ElMessage} from "element-plus";

const is_login = ref(false)
const url = ref('https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png')

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'
    is_login.value = ref(true)
  }
})

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>

<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>

.vitepress/theme/index.js 中挂载使用

js
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}) {
        // ...
    }
}

自定义登录页

.vitepress/theme/components/login.vue
vue
<template>
  <el-row class="login-container">
    <el-col :lg="16" :md="12" class="left">
      <div>
        <div>vitepress-site</div>
        <div>基于vitepress构建的web站点</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 = () => {
  setTimeout(function () {
    localStorage.setItem('jwt', 'Bearer xasdjssalsdfsadfasdf')
    location.href = '/'
  }, 2000);

  /* TODO:实现后端以后编写
  // 实现校验
  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 {
  margin-top: 15px;
  color: #E5E5E5;
  font-size: 2.6rem;
}

.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>

.vitepress/theme/index.js 中挂载使用

js
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}) {
        // ...
        app.use(ElementPlus);
        app.component("Login", Login);	
    }
}

/docs/ 中创建 login.md (和 .vitepress/ 同级),并在页面中使用

markdown
---
layout: page
---

<Login></Login>