feat: 自定义vitepress主题

main
ppst 1 year ago
commit 004d027777

@ -0,0 +1,11 @@
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

10
.gitignore vendored

@ -0,0 +1,10 @@
/coverage
/node_modules
dist
TODOs.md
.DS_Store
demo/.vitepress/cache
demo/node_modules
demo/doc/.vitepress/cache
demo/doc/node_modules

@ -0,0 +1,4 @@
semi: false
singleQuote: true
printWidth: 80
trailingComma: none

@ -0,0 +1,206 @@
# vitepress 自定义主题
可以直接克隆,也可以使用下面方法一步步自定义实现
克隆地址
```shell
git clone http://git.ppst.top/pp/custom-vitepress-theme-demo.git
```
## 安装 vitepress
```shell
pnpm install vitepress
```
## 安装主题
```shell
pnpm install custom-vitepress-theme
```
## 安装读取文档插件
```shell
pnpm install markdown-from-posts
```
## 目录结构
```md
.vitepress
theme
index.ts
override.css
config.ts
pages
default.md
posts
文件夹/.md文件
.md文件
public
favicon.ico
index.md
```
### .vitepress文件夹
config.ts - 配置自定义vitepress导航以及标题等
theme/override.css 重置样式
theme/index.ts
#### .vitepress/config.ts
```js
const { getPosts } = require('markdown-from-posts')
async function config() {
return {
extends: {
markdown: {
headers: {
level: [2, 3]
}
},
},
themeConfig: {
posts: await getPosts(),
title: 'BBBB',
description: '自定义主题BBBB',
docRoot: '',
hasDarkSwitch: true,
homeConfig: {
headline: 'BBBB大标题',//大标题
headlineHeight: 'BBBB高亮',//大标题高亮
subheading: 'BBBB小标题',//小标题
subheadingHeight: '小标题高亮',//小标题高亮
description: '自定义主题简短banner描述',//描述
},
nav: [
{ text: '首页', link: '/', icon: '' },
{ text: '归档', link: '/pages/archives', icon: '' },
{ text: '默认', link: '/pages/default', icon: '' },
{ text: '分类', link: '/pages/category', icon: '' },
{ text: '搜索', link: '/pages/search', icon: '' },
],
footer: {
copyright: '湘ICP备aaaaaaaa-1'
}
}
}
}
module.exports = config()
```
#### .vitepress/theme/override.css 样式
```css
:root {
--c-main-color:#80adff;/* 主色调 */
}
```
#### .vitepress/theme/index.ts 【vitepress 引入主题】
```js
import { CustomTheme } from 'custom-vitepress-theme'
import './override.css'
export default {
...CustomTheme,
}
```
### pages文件夹
配置二级目录页面(可自定义布局页面),
默认有
- category.md【分类页面】
- default.md 【默认页面】
- default.md 【搜索页面】
- archives.md 【归档页面】
如果需要自定义布局请定义组件并在.vitepress/theme/index.ts引用
在pages下页面中使用组件即可
### posts 文件夹 【所有文档】
文档顶部:
```md
---
title: default
date: 2018-09-14 13:57:02
category: default
tags:
- default
---
```
- title 为文档标题
- date 创建时间
- category 分类
- tags 标签
#### public文件夹
- favicon.ico 网站图标
- img文件 公共图片文件夹
#### index.html文件
首页配置
```html
---
page: true
date: 2021-06-30
title: 自定义标题
sidebar: false
---
<script setup>
</script>
<Home>
<template #banner>
<img src="/img/page.png" />
</template>
<template #description>
<!-- 一些其他描述 -->
</template>
</Home>
```
## package.json
```json
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vitepress dev --host 0.0.0.0",
"build": "vitepress build "
},
"dependencies": {
"@types/node": "^18.15.11",
"markdown-from-posts": "^1.0.5",
"vitepress": "1.0.0-alpha.64",
"custom-vitepress-theme": "^0.0.2"
}
}
```

6
demo/.gitignore vendored

@ -0,0 +1,6 @@
/coverage
/node_modules
dist
TODOs.md
.DS_Store
demo/.vitepress/cache

@ -0,0 +1,38 @@
const { getPosts } = require('markdown-from-posts')
async function config() {
return {
extends: {
markdown: {
headers: {
level: [2, 3]
}
},
},
themeConfig: {
posts: await getPosts(),
title: 'BBBB',
description: '自定义主题BBBB',
docRoot: '',
hasDarkSwitch: true,
homeConfig: {
headline: 'BBBB大标题',//大标题
headlineHeight: 'BBBB高亮',//大标题高亮
subheading: 'BBBB小标题',//小标题
subheadingHeight: '小标题高亮',//小标题高亮
description: '自定义主题简短banner描述',//描述
},
nav: [
{ text: '首页', link: '/', icon: '' },
{ text: '归档', link: '/pages/archives', icon: '' },
{ text: '默认', link: '/pages/default', icon: '' },
{ text: '分类', link: '/pages/category', icon: '' },
{ text: '搜索', link: '/pages/search', icon: '' },
],
footer: {
copyright: '湘ICP备aaaaaaaa-1'
}
}
}
}
module.exports = config()

@ -0,0 +1,12 @@
import { CustomTheme } from 'custom-vitepress-theme'
// import { h } from 'vue'
import './override.css'
export default {
...CustomTheme,
// Layout() {
// return h(CustomTheme.Layout, null, {
// })
// }
}

@ -0,0 +1,97 @@
:root {
--c-main-color:#80adff;
}
/* card */
.news-box {
display: flex;
align-items: center;
width: 100%;
margin: 0 auto;
padding: 20px 0;
}
.news-box .card-item {
flex-basis: 25%;
padding: 15px;
transition: all 0.3s ease;
}
.news-box .card-item .item-box {
display: flex;
flex-direction: column;
align-items: center;
border-radius: 15px;
padding: 15px;
margin-bottom: 140px;
background: #f8f8fc;
transition: all 0.3s ease;
}
.news-box .card-item .item-box .icon {
font-size: 48px;
color: var(--c-main-color);
}
.news-box .card-item .item-box .title {
font-size: 18px;
line-height: 2;
font-weight: 500;
color: #020312;
}
.news-box .card-item .item-box .desc {
font-size: 16px;
line-height: 2;
color: #717e96;
overflow: hidden;
}
.news-box .card-item .item-box .desc {
font-size: 16px;
line-height: 2;
color: #717e96;
}
.news-box .card-item:hover .item-box {
background: var(--c-main-color);
transition: all 0.3s ease;
}
.news-box .card-item .item-box.active {
background: var(--c-main-color);
margin-bottom: 0;
}
.news-box .card-item:hover .item-box .icon,
.news-box .card-item:hover .item-box .title,
.news-box .card-item:hover .item-box .desc,
.news-box .card-item .item-box.active .icon,
.news-box .card-item .item-box.active .title,
.news-box .card-item .item-box.active .desc {
color: #fff;
}
@media (max-width: 1200px) {
.banner .banner-left {
font-size: 14px;
}
.banner .banner-left .desc {
font-size: 12px;
}
.note-box {
flex-wrap: wrap;
}
.note-box .note-item {
flex-basis: 100%;
}
.news-box {
flex-wrap: wrap;
}
.news-box .card-item {
flex-basis: 100%;
}
.news-box .card-item .item-box {
margin-bottom: 0;
}
.news-box .card-item .item-box .title {
font-size: 14px;
}
.news-box .card-item .item-box .desc {
font-size: 12px;
}
}

@ -0,0 +1,95 @@
---
page: true
date: 2021-06-30
title: 自定义标题
sidebar: false
---
<script setup>
</script>
<Home>
<template #banner>
<img src="/img/page.png" />
</template>
<template #description>
<!-- 一些其他描述 -->
</template>
<div class="card-header max-width">
<div class="title">手册</div>
<div class="desc">基础知识手册(学习基础笔记)</div>
</div>
<div class="note-box max-width">
<div class="note-item">
<div class="item-box">
<div class="title">💡CSS手册</div>
<div class="go">
<span class="button-box"
><a href="https://css.web.ppst.top/">💡CSS手册</a></span
>
</div>
</div>
</div>
<div class="note-item">
<div class="item-box">
<div class="title">💡JS手册</div>
<div class="go">
<span class="button-box"><a href="https://js.web.ppst.top/">💡JS手册</a></span>
</div>
</div>
</div>
<div class="note-item">
<div class="item-box">
<div class="title">💡博客</div>
<div class="go">
<span class="button-box"><a href="https://blog.web.ppst.top/">💡博客</a></span>
</div>
</div>
</div>
</div>
<div class="card-header max-width">
<div class="title">随笔</div>
<div class="desc">最常用随笔分类</div>
</div>
<div class="news-box max-width">
<div class="card-item">
<div class="item-box">
<div class="icon">?</div>
<div class="title">JS笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
<div class="card-item">
<div class="item-box active">
<div class="icon">?</div>
<div class="title">vue笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
<div class="card-item">
<div class="item-box">
<div class="icon">?</div>
<div class="title">服务器笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
<div class="card-item">
<div class="item-box active">
<div class="icon">?</div>
<div class="title">浏览器笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
</div>
</Home>

@ -0,0 +1,19 @@
{
"name": "demo",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "vitepress dev --host 0.0.0.0",
"build": "vitepress build "
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@types/node": "^18.15.11",
"markdown-from-posts": "^1.0.5",
"vitepress": "1.0.0-alpha.64",
"custom-vitepress-theme": "^0.0.2"
}
}

@ -0,0 +1,13 @@
---
page: true
title: Archive
description: Archive
sidebar: false
---
<Archives>
<template #banner>
<img src="/img/banner.png" />
</template>
</Archives>

@ -0,0 +1,13 @@
---
page: true
title: 分类
description: 分类
sidebar: false
---
<CategoryPage title='分类' category='demo' description="分类">
<template #banner>
<img src="/img/banner.png" />
</template>
</CategoryPage>

@ -0,0 +1,13 @@
---
page: true
title: 默认
description: 默认
sidebar: false
---
<DefaultPage title='默认' category='default' description="默认">
<template #banner>
<img src="/img/banner.png" />
</template>
</DefaultPage>

@ -0,0 +1,11 @@
---
page: true
title: 搜索
description: 全文检索
sidebar: false
---
<Search>
<template #banner>
<img src="/img/banner.png" />
</template>
</Search>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,63 @@
---
title: default
date: 2018-09-14 13:57:02
category: default
tags:
- default
---
# 主标题AAAA
## 副标题
我是内容
```js
let a = '我是js代码'
```
```css
.default{
color:red;
}
```
```shell
npm install default
```
### 副标题1
我是内容
```js
let a = '我是js代码'
```
```css
.default{
color:red;
}
```
```shell
npm install default
```
#### 副标题111
我是内容
```js
let a = '我是js代码'
```
```css
.default{
color:red;
}
```
```shell
npm install default
```

@ -0,0 +1,63 @@
---
title: demo
date: 2018-09-14 13:57:02
category: demo
tags:
- demo
---
# 主标题BBBB
## 副标题
我是内容
```js
let a = '我是js代码'
```
```css
.demo{
color:red;
}
```
```shell
npm install demo
```
### 副标题1
我是内容
```js
let a = '我是js代码'
```
```css
.demo{
color:red;
}
```
```shell
npm install demo
```
#### 副标题111
我是内容
```js
let a = '我是js代码'
```
```css
.demo{
color:red;
}
```
```shell
npm install demo
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

@ -0,0 +1,47 @@
{
"name": "custom-vitepress-theme",
"version": "0.0.1",
"description": "自定义vitepress主题",
"main": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./config": {
"types": "./src/vitepress/config/baseConfig.d.ts",
"default": "./src/vitepress/config/baseConfig.js"
}
},
"files": [
"src",
"types"
],
"repository": {
"type": "git",
"url": "http://git.ppst.top/pp/custom-vitepress-theme"
},
"keywords": [
"vue",
"vitepress"
],
"author": "pp",
"scripts": {
"lint": "prettier --check --write --parser typescript \"{__tests__,docs,src,types}/**/*.ts\"",
"lint:fail": "prettier --check --parser typescript \"{__tests__,docs,src,types}/**/*.ts\"",
"type": "tsc --noEmit",
"test": "npm run lint && npm run type",
"demo": "cd demo && vitepress dev demo --host 0.0.0.0"
},
"dependencies": {
"markdown-from-posts": "^1.0.5",
"normalize.css": "^8.0.1",
"vitepress": "1.0.0-alpha.64"
},
"devDependencies": {
"@types/node": "^18.15.11",
"prettier": "^2.7.1",
"typescript": "^4.9.5",
"vue": "^3.2.47"
},
"peerDependencies": {
"vitepress": "^1.0.0-alpha.64"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1 @@
export * from './vitepress'

12
src/shim.d.ts vendored

@ -0,0 +1,12 @@
declare module 'custom-vitepress-theme' {
const CustomTheme: Theme = {
Layout: any
}
export { CustomTheme }
}
declare module '*.vue' {
import { ComponentOptions } from 'vue'
const componentOptions: ComponentOptions
export default componentOptions
}

@ -0,0 +1,76 @@
<template>
<div class="page-banner-box max-width">
<div class="banner">
<div class="banner-left">
<slot name="banner"></slot>
</div>
<div class="banner-right">
<div class="title">归档</div>
<div class="desc">时间顺序进行归档</div>
</div>
</div>
</div>
<div class="archives-box">
<div v-for="(yearList, index) in data" :key="index">
<div class="year">
{{ yearList[0].frontMatter.date.split("-")[0] }}
</div>
<a
:href="withBase(article.regularPath)"
v-for="(article, index) in yearList"
:key="index"
class="article"
>
<div class="title">
<div class="title-o"></div>
{{ article.frontMatter.title }}
</div>
<div class="date">{{ article.frontMatter.date.slice(5) }}</div>
</a>
</div>
<div class="body-bg"></div>
</div>
</template>
<script lang="ts" setup>
import { useData, withBase } from "vitepress";
import { computed } from "vue";
import { useYearSort } from "../composables/functions";
const { theme } = useData();
const data = computed(() => useYearSort(theme.value.posts));
</script>
<style scoped>
.year {
padding: 16px 0 8px 0;
font-size: 1.4rem;
font-weight: 600;
}
.archives-box {
border-radius: 5px;
background: #fbfafc;
box-shadow: 0 1px 2px 0 #fbfafc;
padding: 40px;
}
.dark .archives-box{
background: rgb(0 0 0 / 10%);
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
}
.article {
padding: 4px 0 4px 25px;
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
color: var(--title-color);
font-size: 1rem;
}
.title:hover {
color: var(--c-main-color);
}
.date {
color: var(--date-color);
}
</style>

@ -0,0 +1,3 @@
<template>
<svg t="1656596007290" fill="#666666" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2433" width="32" height="32"><path d="M528.978185 161.824619c-4.505617-4.502547-10.610662-7.033184-16.981766-7.033184-6.369058 0-12.472056 2.52859-16.97358 7.033184L320.073294 336.772117c-9.376555 9.377578-9.376555 24.576745 0 33.952276 9.375531 9.375531 24.576745 9.378601 33.952276 0l157.971871-157.971871 157.972894 157.971871c4.688789 4.688789 10.831696 7.033184 16.977673 7.033184 6.142907 0 12.288884-2.344395 16.978696-7.033184 9.375531-9.376555 9.375531-24.576745 0-33.952276L528.975115 161.824619 528.978185 161.824619 528.978185 161.824619z" p-id="2434"></path><path d="M64.55783 607.030353 167.254793 607.030353 167.254793 869.208565 215.270367 869.208565 215.270367 607.030353 317.968353 607.030353 317.968353 559.016827 64.55783 559.016827 64.55783 607.030353Z" p-id="2435"></path><path d="M582.790788 559.016827l-127.19696 0c-15.358803 0-30.719652 5.860475-42.439578 17.579377-11.718903 11.719926-17.578354 27.079752-17.578354 42.437531l0 190.156898c0 15.361873 5.860475 30.720675 17.578354 42.439578 11.720949 11.720949 27.080775 17.579377 42.439578 17.579377l127.19696 0c15.356756 0 30.719652-5.859451 42.438555-17.579377 11.719926-11.719926 17.579377-27.079752 17.579377-42.439578L642.80872 619.033735c0-15.358803-5.862521-30.718629-17.579377-42.437531C613.509417 564.874231 598.146521 559.016827 582.790788 559.016827L582.790788 559.016827zM594.79417 809.190633c0 2.269693-0.61296 5.582135-3.515057 8.486278-2.908236 2.90619-6.223748 3.518127-8.489348 3.518127l-127.19696 0c-2.267647 0-5.581112-0.610914-8.485255-3.515057-2.90619-2.908236-3.518127-6.222725-3.518127-8.490372L443.589422 619.033735c0-2.267647 0.60989-5.581112 3.515057-8.485255 2.905166-2.90619 6.221702-3.518127 8.488325-3.518127l127.19696 0c2.267647 0 5.581112 0.61296 8.483208 3.515057 2.908236 2.907213 3.520173 6.223748 3.520173 8.488325L594.793146 809.190633 594.79417 809.190633z" p-id="2436"></path><path d="M941.860746 576.59518c-11.719926-11.720949-27.082822-17.579377-42.440601-17.579377L734.803947 559.015803l0 310.192761 48.015573 0L782.81952 733.158982l116.600625 0c15.360849 0 30.722722-5.860475 42.440601-17.579377 11.718903-11.720949 17.5804-27.081799 17.5804-42.439578l0-54.107315C959.441147 603.674932 953.579649 588.315106 941.860746 576.59518L941.860746 576.59518zM911.42762 673.14105c0 2.269693-0.61296 5.580089-3.514033 8.485255-2.90926 2.907213-6.223748 3.518127-8.490372 3.518127L782.81952 685.144432l0-78.113055 116.603695 0c2.26867 0 5.581112 0.61296 8.487302 3.515057 2.904143 2.907213 3.517103 6.223748 3.517103 8.488325L911.42762 673.14105 911.42762 673.14105z" p-id="2437"></path></svg>
</template>

@ -0,0 +1,47 @@
<template>
<div class="page-banner-box max-width">
<div class="banner">
<div class="banner-left">
<slot name="banner"></slot>
</div>
<div class="banner-right">
<div class="title">{{ props.title ? props.title : '标题' }}</div>
<div class="desc">
{{ props.description ? props.description : props.title ? props.title : '' }}
</div>
</div>
</div>
</div>
<div class="card-box max-width">
<div class="card-item" v-for="(article, index) in data.list" :key="index">
<div class="item-box">
<div class="title">
{{ article.frontMatter.title }}
</div>
<div class="mark-box">
<a class="mark" :href="withBase(article.regularPath)">{{ article.frontMatter.title }}</a>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
import { useData, withBase } from "vitepress";
import { Article } from "../config/baseConfig";
import { initLists } from "../composables/functions";
const { theme } = useData();
const data:{list:Array<Article>} = reactive({list:[]})
const props = defineProps({
title: String,
category:String,
description: String,
});
data.list = initLists(theme.value.posts,props.category?props.category:'js');
</script>
<style scoped></style>

@ -0,0 +1,62 @@
<template>
<div class="site-footer">
Copyright ©
<a href="http://beian.miit.gov.cn/" target="_blank" class="site"
>{{ data.footer.copyright }}</a
>
</div>
</template>
<script lang="ts" setup>
import { reactive, onMounted } from "vue";
import { useData } from "vitepress";
import { FooterConfig } from "../config/baseConfig";
const { theme } = useData();
const data: { footer: FooterConfig } = reactive({
footer: {
copyright: "",
},
});
onMounted(() => {
data.footer = theme.value.footer;
});
</script>
<style scoped>
.site-footer {
color: #171a2e;
height: 60px;
text-align: center;
font-size: 14px;
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
background-color: var(--c-bg-white);
position: relative;
bottom: -60px;
left: 0;
right: 0;
}
.dark .site-footer {
background-color: var(--c-bg-black);
}
.site {
color: var(--c-text-color-light);
}
a :link,
a:visited,
a:hover,
a:active {
color: var(--c-text-color-light);
}
.dark .site {
color: var(--c-text-color-dark);
}
.dark a :link,
.dark a:visited,
.dark a:hover,
.dark a:active {
color: var(--c-text-color-dark);
}
</style>

@ -0,0 +1,71 @@
<template>
<div class="page-banner-box max-width">
<div class="banner">
<div class="banner-left">
<slot name="banner"></slot>
</div>
<div class="banner-right">
<div class="title">{{ props.title ? props.title : '标题' }}</div>
<div class="desc">
{{ props.description ? props.description : props.title ? props.title : '' }}
</div>
</div>
</div>
</div>
<div class="card-box max-width">
<div class="card-item" v-for="(article, index) in data.list" :key="index">
<div class="item-box">
<div class="title">{{ article.frontMatter.title }} {{ article.frontMatter.date }}{{ article.frontMatter.tags.join(", ") }}</div>
<div class="desc">
{{ article.frontMatter.title }} : {{ article.frontMatter.description }}
</div>
<div class="mark-box">
<a class="mark" :href="getWithBase(article.regularPath)">前往详情</a>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { reactive } from "vue";
import { useData } from "vitepress";
import { Article } from "../config/baseConfig";
import { initLists,getWithBase } from "../composables/functions";
const {theme} = useData();
const data:{list:Array<Article>} = reactive({list:[]})
const props = defineProps({
title: String,
category:String,
description: String,
});
data.list = initLists(theme.value.posts,props.category?props.category:'js');
</script>
<style scoped>
.card-box .card-item {
flex-basis: 100%;
}
.card-box .card-item .item-box{
justify-content: flex-start;
align-items: flex-start;
}
.card-box .item-box:hover .mark-box,
.card-box .item-box:hover .mark-box .mark {
opacity: 0.8;
color: #000;
font-size: 12px;
background-color: rgba(255, 255, 255, .6);
}
.dark .card-box .item-box:hover .mark-box,
.dark .card-box .item-box:hover .mark-box .mark {
opacity: 0.8;
color: #fff;
font-size: 12px;
background-color: rgba(0, 0, 0, .6);
}
</style>

@ -0,0 +1,83 @@
<template>
<div class="banner-box max-width">
<div class="banner">
<div class="banner-left">
<div class="title">{{ data.config.headline }}<span class="hight">{{ data.config.headlineHeight }}</span></div>
<div class="title"><span class="hight">{{ data.config.subheadingHeight }}</span>{{ data.config.subheading }}</div>
<div class="desc">{{ data.config.description }}</div>
</div>
<div class="banner-right">
<slot name="banner"></slot>
</div>
</div>
</div>
<div class="card-header max-width">
<slot name="description"></slot>
</div>
<slot></slot>
</template>
<script lang="ts" setup>
import { reactive, onMounted } from "vue";
import { useData } from "vitepress";
import { HomeConfig } from "../config/baseConfig";
const { theme } = useData();
const data: { config: HomeConfig } = reactive({
config: {
headline: "", //
headlineHeight: "", //
subheading: "", //
subheadingHeight: "", //
description: "", //
}
});
onMounted(() => {
data.config = theme.value.homeConfig;
});
</script>
<style scoped>
.margin-header {
margin-top: 40px;
}
.banner-box {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: 0 auto;
}
.banner {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: 0 auto;
}
.banner-left {
flex-basis: 50%;
font-size: 36px;
line-height: 2;
padding-left: 10%;
}
.banner-left .title {
color: var(--c-main-color);
}
.banner-left .desc {
font-size: 18px;
color: #888296;
}
.banner-left .hight {
color: var(--c-text-color-light);
}
.dark .banner-left .hight {
color: var(--c-text-color-dark);
}
.banner-right {
width: 500px;
}
.banner-right img {
width: 100%;
height: 100%;
}
</style>

@ -0,0 +1,87 @@
<template>
<div id="top" class="wrap">
<HeaderNav></HeaderNav>
<div class="layout-box max-width">
<div class="resize-layout">
<div class="resize-left" ref="left"> <Sidebar></Sidebar></div>
<div class="resize-right" ref="right">
<div class="right-box">
<Content class="page-box" />
<a href="#top" class="return-top"><BackPng class="return-img" /></a>
</div>
</div>
</div>
</div>
<Copyright />
</div>
</template>
<script setup lang="ts" >
import Copyright from './Copyright.vue'
import HeaderNav from './header/Index.vue'
import Sidebar from './Sidebar.vue'
import BackPng from './BackPng.vue'
import { ref,watch } from 'vue';
import { useRoute } from 'vitepress';
let left = ref();
let right = ref();
const route = useRoute();
watch(() => route.data.frontmatter.sidebar, (newValue, _oldValue) => {
if(newValue===undefined){
showRightBox();
}else{
hideRightBox();
}
});
function showRightBox() {
left.value.style.width = '30%';
right.value.style.width = '70%';
}
function hideRightBox() {
left.value.style.width = '0';
right.value.style.width = '100%';
}
</script>
<style scoped>
.layout-box{
display: flex;
margin: 0 auto;
}
.page-box{
width: 100%;
min-height: 90vh;
}
.resize-layout {
display: flex;
width: 100%;
}
.resize-left {
width: 0;
padding: 0;
overflow: hidden;
}
.resize-right {
width: 100%; /*右侧初始化宽度*/
}
.right-box {
height: 100%;
}
@media (max-width: 1200px) {
.resize-right{
width: 100% !important;
}
.resize-left{
width: 0 !important;
}
}
</style>

@ -0,0 +1,201 @@
<template>
<div class="page-banner-box max-width">
<div class="banner">
<div class="banner-left">
<slot name="banner"></slot>
</div>
<div class="banner-right">
<div class="title">搜索</div>
<div class="desc">根据关键字进行搜索</div>
</div>
</div>
</div>
<div class="banner-box max-width">
<div class="banner">
<div class="search-box">
<div class="search-item">
<span class="search-icon">搜索</span>
<input
type="text"
placeholder="请输入关键词"
v-model="data.key"
@keydown="search"
/>
<span class="search-clean" v-if="data.key != ''" @click="cleanAll"
>+</span
>
</div>
</div>
</div>
</div>
<div class="container" v-if="data.list.length > 0">
<div class="card-box max-width">
<div class="card-item" v-for="(article, index) in data.list" :key="index">
<div class="item-box">
<div class="title">{{ article.frontMatter.title }} {{ article.frontMatter.date }}{{ article.frontMatter.tags.join(", ") }}</div>
<div class="desc">
{{ article.frontMatter.title }} : {{ article.frontMatter.description }}
</div>
<div class="mark-box">
<a class="mark" :href="withBase(article.regularPath)">前往详情</a>
</div>
</div>
</div>
</div>
</div>
<div class="max-width" v-else>
<div class="empty-box"><div class="empty">搜索数据为空试试下面的分类</div></div>
<div class="tag-box max-width">
<div class="tag-item" v-for="(item, key) in tags" :key="key">
<span @click="toggleTag(item)"> {{ item }}</span>
</div>
</div>
</div>
<slot></slot>
</template>
<script lang="ts" setup>
import { computed, reactive } from 'vue'
import { useData, withBase } from 'vitepress'
import { Article } from "../config/baseConfig";
import { getTags,searchData } from "../composables/functions";
const { theme } = useData()
const tags = computed(() => getTags(theme.value.posts))
const data:{
key:string
list: Array<Article>
} = reactive({ key: '', list: [] })
const cleanAll = ()=>{
data.key = '';
data.list = [];
}
const toggleTag = (key: string) => {
data.key = key;
data.list =searchData(theme.value.posts,key)
}
const search = (event: any) => {
if (event.code == 'Enter') {
data.list = []
if(data.key==""){
data.list = [];
}else{
data.list = searchData(theme.value.posts,data.key);
}
}
}
</script>
<style scoped>
.banner-box {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: 0 auto;
}
.banner {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: 0 auto;
}
.search-box {
display: flex;
justify-content: center;
align-items: center;
width: 70vw;
margin: 0 auto;
/* padding: 50px 0; */
}
.search-item {
display: flex;
flex: 1;
align-items: center;
height: 60px;
border-radius: 10px;
border: 1px solid #c9d0e7;
}
.search-icon {
display: flex;
justify-content: center;
align-items: center;
width: 60px;
font-weight: 700;
border-right: 2px solid #c9d0e7;
}
.search-clean {
font-size: 40px;
transform: rotate(45deg);
color: #666;
}
::-webkit-input-placeholder {
line-height: 1.375em;
}
input {
flex: 1;
outline: none;
color: #666;
font-size: 20px;
padding: 10px;
border-radius: 10px;
/* border: 1px solid #e3e3e3; */
border: none;
}
.dark input{
background-color: #1a1a1a;
}
input:hover {
border: none;
/* border: 1px solid #b4a078; */
}
input:focus {
border: none;
/* border: 1px solid #b4a078; */
/* box-shadow: 0 0 0 2px rgba(180, 160, 120, 0.2); */
}
.empty-box{
display: flex;
justify-content: center;
align-items: center;
height: 200px;
color: #555456;
}
.dark .empty-box{
color:#fff;
}
.tag-box {
display: flex;
flex-wrap: wrap;
}
.tag-box .tag-item {
padding: 4px 8px;
color: #555456;
border: 1px solid #e3e1e4;
border-radius: 4px;
margin: 4px;
cursor: pointer;
}
.dark .tag-box .tag-item {
color: #fff;
}
.card-box .card-item{
flex-basis: 100%;
}
.card-box .item-box:hover .mark-box,
.card-box .item-box:hover .mark-box .mark {
opacity: 0.8;
color: #fff;
background-color: var(--c-main-color);
}
</style>

@ -0,0 +1,120 @@
import { h } from 'vue'
import { useRoute, useData, Header } from 'vitepress'
export const hashRE = /#.*$/
export const extRE = /(index)?\.(md|html)$/
export const endingSlashRE = /\/$/
export const outboundRE = /^[a-z]+:/i
export function normalize(path: string) {
return decodeURI(path).replace(hashRE, '').replace(extRE, '')
}
export function isActive(
route: { data: { relativePath: string } },
path: string
) {
if (path === undefined) {
return false
}
const routePath = normalize(`/${route.data.relativePath}`)
const pagePath = normalize(path)
return routePath === pagePath
}
export function joinUrl(base: string, path: string) {
const baseEndsWithSlash = base.endsWith('/')
const pathStartsWithSlash = path.startsWith('/')
if (baseEndsWithSlash && pathStartsWithSlash) {
return base.slice(0, -1) + path
}
if (!baseEndsWithSlash && !pathStartsWithSlash) {
return `${base}/${path}`
}
return base + path
}
export const SideBarLink = (props: any) => {
const route = useRoute()
const { site, frontmatter } = useData()
const depth = props.depth || 1
const maxDepth = frontmatter.value.sidebarDepth || Infinity
const headers = route.data.headers
const text = props.item.text
const link = resolveLink(site.value.base, props.item.link)
const children = props.item.children
const active = isActive(route, props.item.link)
const childItems =
depth < maxDepth
? createChildren(active, children, headers, depth + 1)
: null
return h('li', { class: 'sidebar-link' }, [
h(
link ? 'a' : 'p',
{
class: { 'sidebar-link-item': true, active },
href: link
},
text
),
childItems
])
}
function resolveLink(base: string, path: string) {
if (path === undefined) {
return path
}
// keep relative hash to the same page
if (path.startsWith('#')) {
return path
}
return joinUrl(base, path)
}
function createChildren(
active: boolean,
children: any,
headers: Array<Header> | undefined,
depth = 1
) {
if (children && children.length > 0) {
return h(
'ul',
{ class: 'sidebar-links' },
children.map((c: any) => {
return h(SideBarLink, { item: c, depth })
})
)
}
if (active && headers && Array.isArray(headers)) {
// if(createChildren(false, resolveHeaders(headers), undefined, depth)) return createChildren(false, resolveHeaders(headers), undefined, depth);
}
return null
}
// function resolveHeaders(headers:any) {
// return mapHeaders(groupHeaders(headers));
// }
// function groupHeaders(headers:Array<Header>) {
// headers = headers.map((h:Header) => Object.assign({}, h));
// let lastH2:any;
// headers.forEach((h:Header) => {
// if (h.level === 2) {
// lastH2 = h;
// }
// else if (lastH2) {
// ;
// (lastH2.children || (lastH2.children = [])).push(h);
// }
// });
// return headers.filter((h:any) => h.level === 2);
// }
// function mapHeaders(headers:any) {
// return headers.map((header:any) => {
// let children = [];
// if(header.children ){
// children = mapHeaders(header.children)
// }
// return {
// text: header.title,
// link: `#${header.slug}`,
// children: children
// }
// });
// }

@ -0,0 +1,46 @@
<script setup lang="ts">
import { useSideBar } from './sidebar/sideBar'
import { SideBarLink } from './SideBarLink'
const items = useSideBar()
</script>
<template>
<div class="seize-a-seat" v-if="items.length > 0"></div>
<div class="side-box" v-if="items.length > 0">
<h2>💡 目录</h2>
<ul class="sidebar-links">
<SideBarLink v-for="(item,index) of items" :item="item" :key="index"/>
</ul>
</div>
</template>
<style scoped>
.side-box{
background-color: #f0faf9;
padding: 20px;
background-image: linear-gradient(180deg, #fff, #fff);
margin-right: 10px;
}
.dark .side-box{
background-image: linear-gradient(180deg, #1a1a1a, #1a1a1a);
}
.seize-a-seat{
width: 0;
height: 0;
}
.sidebar-links{
list-style: revert;
}
.sidebar-links{
padding: 0;
padding-left: 20px;
padding-top: 6px;
}
@media (max-width: 1200px){
.side-box{
display: none;
}
}
</style>

@ -0,0 +1,55 @@
<template>
<SwitchButton
class="vt-switch-appearance"
:aria-checked="isDark"
@click="toggle"
>
<SunIcon class="vt-switch-appearance-sun" />
<MoonIcon class="vt-switch-appearance-moon" />
</SwitchButton>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import SwitchButton from './SwitchButton.vue'
import SunIcon from './SunIcon.vue'
import MoonIcon from './MoonIcon.vue'
const storageKey = 'vitepress-theme-appearance'
const { isDark, toggle } = typeof localStorage !== 'undefined'
? useAppearance()
: { isDark: false, toggle: () => {}}
function useAppearance() {
let userPreference = localStorage.getItem(storageKey) || 'auto'
const query = window.matchMedia(`(prefers-color-scheme: dark)`)
const classList = document.documentElement.classList
const isDark = ref(
userPreference === 'auto' ? query.matches : userPreference === 'dark'
)
const setClass = (dark: boolean) => classList[dark ? 'add' : 'remove']('dark')
query.onchange = (e) => {
if (userPreference === 'auto') {
setClass((isDark.value = e.matches))
}
}
const toggle = () => {
setClass((isDark.value = !isDark.value))
localStorage.setItem(
storageKey,
(userPreference = isDark.value
? query.matches
? 'auto'
: 'dark'
: query.matches
? 'light'
: 'auto')
)
}
return { isDark, toggle }
}
</script>

@ -0,0 +1,5 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12.1,22c-0.3,0-0.6,0-0.9,0c-5.5-0.5-9.5-5.4-9-10.9c0.4-4.8,4.2-8.6,9-9c0.4,0,0.8,0.2,1,0.5c0.2,0.3,0.2,0.8-0.1,1.1c-2,2.7-1.4,6.4,1.3,8.4c2.1,1.6,5,1.6,7.1,0c0.3-0.2,0.7-0.3,1.1-0.1c0.3,0.2,0.5,0.6,0.5,1c-0.2,2.7-1.5,5.1-3.6,6.8C16.6,21.2,14.4,22,12.1,22zM9.3,4.4c-2.9,1-5,3.6-5.2,6.8c-0.4,4.4,2.8,8.3,7.2,8.7c2.1,0.2,4.2-0.4,5.8-1.8c1.1-0.9,1.9-2.1,2.4-3.4c-2.5,0.9-5.3,0.5-7.5-1.1C9.2,11.4,8.1,7.7,9.3,4.4z" />
</svg>
</template>

@ -0,0 +1,13 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 24 24">
<path d="M12,18c-3.3,0-6-2.7-6-6s2.7-6,6-6s6,2.7,6,6S15.3,18,12,18zM12,8c-2.2,0-4,1.8-4,4c0,2.2,1.8,4,4,4c2.2,0,4-1.8,4-4C16,9.8,14.2,8,12,8z" />
<path d="M12,4c-0.6,0-1-0.4-1-1V1c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,3.6,12.6,4,12,4z" />
<path d="M12,24c-0.6,0-1-0.4-1-1v-2c0-0.6,0.4-1,1-1s1,0.4,1,1v2C13,23.6,12.6,24,12,24z" />
<path d="M5.6,6.6c-0.3,0-0.5-0.1-0.7-0.3L3.5,4.9c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C6.2,6.5,5.9,6.6,5.6,6.6z" />
<path d="M19.8,20.8c-0.3,0-0.5-0.1-0.7-0.3l-1.4-1.4c-0.4-0.4-0.4-1,0-1.4s1-0.4,1.4,0l1.4,1.4c0.4,0.4,0.4,1,0,1.4C20.3,20.7,20,20.8,19.8,20.8z" />
<path d="M3,13H1c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S3.6,13,3,13z" />
<path d="M23,13h-2c-0.6,0-1-0.4-1-1s0.4-1,1-1h2c0.6,0,1,0.4,1,1S23.6,13,23,13z" />
<path d="M4.2,20.8c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C4.7,20.7,4.5,20.8,4.2,20.8z" />
<path d="M18.4,6.6c-0.3,0-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.4-1.4c0.4-0.4,1-0.4,1.4,0s0.4,1,0,1.4l-1.4,1.4C18.9,6.5,18.6,6.6,18.4,6.6z" />
</svg>
</template>

@ -0,0 +1,9 @@
<template>
<button class="vt-switch" type="button" role="switch">
<span class="vt-switch-check">
<span class="vt-switch-icon" v-if="$slots.default">
<slot />
</span>
</span>
</button>
</template>

@ -0,0 +1,72 @@
<script lang="ts" setup>
import DarkSwitch from "./../darkSwitch/Index.vue";
import { useData } from 'vitepress'
const { theme } = useData()
const data = theme.value.nav
const title = theme.value.title
</script>
<template>
<div class="header-box max-width">
<div class="logo-box">
<span><a href="/" class="logo-title">{{ title }}</a></span>
</div>
<div class="nav-box">
<div class="nav-list">
<span v-for="(item, key) in data" :key="key">
<a v-if="item.link" :href="item.link" class="nav-item">{{ item.text }}</a>
</span>
<div class="nav-item">
<DarkSwitch />
</div>
</div>
</div>
</div>
</template>
<style scoped>
.header-box {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
width: 100%;
margin: 0 auto;
overflow: auto;
}
.header-box .logo-title {
font-size: 20px;
font-weight: 700;
color: var(--c-main-color);
}
.header-box .nav-box {
display: flex;
padding: 0 10px;
justify-content: space-evenly;
}
.header-box .nav-list {
display: flex;
}
.header-box .nav-item {
line-height: 40px;
padding: 8px 16px;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.nav-item:hover {
color: var(--c-main-color);
}
.active {
color: var(--c-main-color);
}
</style>

@ -0,0 +1,101 @@
import { onMounted, onUnmounted, onUpdated } from 'vue'
export function useActiveSidebarLinks() {
let rootActiveLink: HTMLAnchorElement | null = null
let activeLink: Element | null = null
const onScroll = throttleAndDebounce(setActiveLink, 300)
function setActiveLink() {
const sidebarLinks = getSidebarLinks()
const anchors = getAnchors(sidebarLinks)
for (let i = 0; i < anchors.length; i++) {
const anchor = anchors[i]
const nextAnchor = anchors[i + 1]
const [isActive, hash] = isAnchorActive(i, anchor, nextAnchor)
if (isActive) {
history.replaceState(null, document.title, '')
activateLink(hash)
return
}
}
}
function activateLink(hash: any) {
deactiveLink(activeLink)
deactiveLink(rootActiveLink)
activeLink = document.querySelector(`.sidebar a[href="${hash}"]`)
if (!activeLink) {
return
}
activeLink.classList.add('active')
// also add active class to parent h2 anchors
const rootLi = activeLink.closest('.sidebar-links > ul > li')
if (rootLi && rootLi !== activeLink.parentElement) {
rootActiveLink = rootLi.querySelector('a')
rootActiveLink && rootActiveLink.classList.add('active')
} else {
rootActiveLink = null
}
}
function deactiveLink(link: any) {
link && link.classList.remove('active')
}
onMounted(() => {
setActiveLink()
window.addEventListener('scroll', onScroll)
})
onUpdated(() => {
// sidebar update means a route change
activateLink(decodeURIComponent(location.hash))
})
onUnmounted(() => {
window.removeEventListener('scroll', onScroll)
})
}
function getSidebarLinks() {
return [].slice.call(
document.querySelectorAll('.sidebar a.sidebar-link-item')
)
}
function getAnchors(sidebarLinks: any) {
return [].slice
.call(document.querySelectorAll('.header-anchor'))
.filter((anchor: any) =>
sidebarLinks.some((sidebarLink: any) => sidebarLink.hash === anchor.hash)
)
}
function getPageOffset() {
return (document.querySelector('.nav-bar') as HTMLElement).offsetHeight
}
function getAnchorTop(anchor: any) {
const pageOffset = getPageOffset()
return anchor.parentElement.offsetTop - pageOffset - 15
}
function isAnchorActive(index: number, anchor: any, nextAnchor: any) {
const scrollTop = window.scrollY
if (index === 0 && scrollTop === 0) {
return [true, null]
}
if (scrollTop < getAnchorTop(anchor)) {
return [false, null]
}
if (!nextAnchor || scrollTop < getAnchorTop(nextAnchor)) {
return [true, decodeURIComponent(anchor.hash)]
}
return [false, null]
}
function throttleAndDebounce(fn: any, delay: any) {
let timeout: any
let called = false
return () => {
if (timeout) {
clearTimeout(timeout)
}
if (!called) {
fn()
called = true
setTimeout(() => {
called = false
}, delay)
} else {
timeout = setTimeout(fn, delay)
}
}
}

@ -0,0 +1,30 @@
import { computed } from 'vue'
import { useRoute } from 'vitepress'
export const defaultLang = 'zh-CN'
export const PREFERRED_LANG_KEY = 'vuetom_lang'
export const breakpoints = {
sm: 480,
md: 768,
lg: 960,
xlg: 1280,
xxl: 1440
}
export const useLang = () => {
const route = useRoute()
return computed(() => {
// the first part of the first slash
const path = route.data?.relativePath
let lang: string | undefined = ''
if (path && path.includes('/')) {
lang = path.split('/').shift()
} else {
lang = defaultLang
}
return lang
})
}

@ -0,0 +1,67 @@
import { computed } from 'vue'
import { useRoute, useData } from 'vitepress'
import { useLang } from './lang'
import { useActiveSidebarLinks } from './activeSidebarLink'
import { getSideBarConfig } from './support'
export function useSideBar() {
const route = useRoute()
const { site } = useData()
const lang = useLang()
useActiveSidebarLinks()
return computed(() => {
// at first, we'll check if we can find the sidebar setting in frontmatter.
const headers = route.data.headers
const frontSidebar = route.data.frontmatter.sidebar
const sidebarDepth = route.data.frontmatter.sidebarDepth
// if it's `false`, we'll just return an empty array here.
if (frontSidebar === false) {
return []
}
// if it's `atuo`, render headers of the current page
if (frontSidebar === 'auto') {
return resolveAutoSidebar(headers, sidebarDepth)
}
// now, there's no sidebar setting at frontmatter; let's see the configs
const themeSidebar = getSideBarConfig(
site.value.themeConfig.sidebar,
route.data.relativePath,
lang.value ? lang.value : ''
)
if (themeSidebar === false) {
return []
}
if (themeSidebar === 'auto') {
return resolveAutoSidebar(headers, sidebarDepth)
}
return themeSidebar
})
}
function resolveAutoSidebar(
headers: Array<{ level: number; title: string; slug: string }>,
depth: number
) {
const ret: Array<{
text: string
link: string
}> = []
if (headers === undefined) {
return []
}
let lastH2: any = undefined
headers.forEach(({ level, title, slug }) => {
if (level - 1 > depth) {
return
}
const item = {
text: title,
link: `#${slug}`
}
if (level === 2) {
lastH2 = item
ret.push(item)
} else if (lastH2) {
;(lastH2.children || (lastH2.children = [])).push(item)
}
})
return ret
}

@ -0,0 +1,51 @@
import { isArray, ensureStartingSlash, removeExtention } from './utils'
export function isSideBarConfig(sidebar: any) {
return sidebar === false || sidebar === 'auto' || isArray(sidebar)
}
export function isSideBarGroup(item: any) {
return item.children !== undefined
}
export function isSideBarEmpty(sidebar: any) {
return isArray(sidebar) ? sidebar.length === 0 : !sidebar
}
/**
* Get the `SideBarConfig` from sidebar option. This method will ensure to get
* correct sidebar config from `MultiSideBarConfig` with various path
* combinations such as matching `guide/` and `/guide/`. If no matching config
* was found, it will return `auto` as a fallback.
*/
export function getSideBarConfig(sidebar: any, path: string, lang: string) {
if (isSideBarConfig(sidebar)) {
return sidebar
}
path = ensureStartingSlash(path)
for (const dir in sidebar) {
// make sure the multi sidebar key starts with slash too
if (path.startsWith(ensureStartingSlash(`${lang}${dir}`))) {
return sidebar[dir][lang]
}
}
return 'auto'
}
/**
* Get flat sidebar links from the sidebar items. This method is useful for
* creating the "next and prev link" feature. It will ignore any items that
* don't have `link` property and removes `.md` or `.html` extension if a
* link contains it.
*/
export function getFlatSideBarLinks(sidebar: any) {
return sidebar.reduce((links: any, item: any) => {
if (item.link) {
links.push({ text: item.text, link: removeExtention(item.link) })
}
if (isSideBarGroup(item)) {
links = [...links, ...getFlatSideBarLinks(item.children)]
}
return links
}, [])
}

@ -0,0 +1,71 @@
export const hashRE = /#.*$/
export const extRE = /(index)?\.(md|html)$/
export const endingSlashRE = /\/$/
export const outboundRE = /^[a-z]+:/i
export function isNullish(value: string) {
return value === null || value === undefined
}
export function isArray(value: string) {
return Array.isArray(value)
}
export function isExternal(path: string) {
return outboundRE.test(path)
}
export function isActive(
route: { data: { relativePath: string } },
path: string
) {
if (path === undefined) {
return false
}
const routePath = normalize(`/${route.data.relativePath}`)
const pagePath = normalize(path)
return routePath === pagePath
}
export function normalize(path: string) {
return decodeURI(path).replace(hashRE, '').replace(extRE, '')
}
export function joinUrl(base: string, path: string) {
const baseEndsWithSlash = base.endsWith('/')
const pathStartsWithSlash = path.startsWith('/')
if (baseEndsWithSlash && pathStartsWithSlash) {
return base.slice(0, -1) + path
}
if (!baseEndsWithSlash && !pathStartsWithSlash) {
return `${base}/${path}`
}
return base + path
}
/**
* get the path without filename (the last segment). for example, if the given
* path is `/guide/getting-started.html`, this method will return `/guide/`.
* Always with a trailing slash.
*/
export function getPathDirName(path: string) {
const segments = path.split('/')
if (segments[segments.length - 1]) {
segments.pop()
}
return ensureEndingSlash(segments.join('/'))
}
export function ensureSlash(path: string) {
return ensureEndingSlash(ensureStartingSlash(path))
}
export function ensureStartingSlash(path: string) {
return /^\//.test(path) ? path : `/${path}`
}
export function ensureEndingSlash(path: string) {
return /(\.html|\/)$/.test(path) ? path : `${path}/`
}
/**
* Remove `.md` or `.html` extention from the given path. It also converts
* `index` to slush.
*/
export function removeExtention(path: string) {
return path.replace(/(index)?(\.(md|html))?$/, '') || '/'
}

@ -0,0 +1,29 @@
import {
Component,
defineComponent,
h,
inject,
InjectionKey,
provide,
Ref
} from 'vue'
import { useData } from 'vitepress'
import { Config } from '../config'
const configSymbol: InjectionKey<Ref<Config>> = Symbol('config')
export function withConfigProvider(App: Component) {
return defineComponent({
name: 'ConfigProvider',
setup(_, { slots }) {
const { theme } = useData()
provide(configSymbol, theme.value)
return () => h(App, null, slots)
}
})
}
export function useConfig() {
return {
config: inject(configSymbol)!
}
}

@ -0,0 +1,101 @@
import { Article, Post } from '../config/baseConfig'
import { useData, withBase } from 'vitepress'
export function getTags(post: Post[]) {
let data: Array<string> = []
for (let index = 0; index < post.length; index++) {
const element = post[index]
const tags = element.frontMatter.tags
if (tags && tags.length > 0) {
tags.forEach((item) => {
data.push(item)
})
}
}
// data = [...new Set(data)];
return data.sort()
}
export function initTags(post: Post[]) {
const data: any = {}
for (let index = 0; index < post.length; index++) {
const element = post[index]
const tags = element.frontMatter.tags
if (tags && tags.length > 0) {
tags.forEach((item) => {
if (data[item]) {
data[item].push(element)
} else {
data[item] = []
data[item].push(element)
}
})
}
}
return data
}
export function searchData(post: Post[], keyword: string) {
const data: Array<Article> = []
for (let index = 0; index < post.length; index++) {
const element = post[index]
let hasArticle = false
if (element.frontMatter.title.includes(keyword)) {
hasArticle = true
}
if (element.frontMatter.category.includes(keyword)) {
hasArticle = true
}
if (element.frontMatter.description.includes(keyword)) {
hasArticle = true
}
const tags = element.frontMatter.tags
if (tags && tags.length > 0) {
tags.forEach((tag) => {
if (tag && tag.includes(keyword)) {
hasArticle = true
}
})
}
if (hasArticle) {
data.push(element)
}
}
return data
}
export function initLists(post: Post[], category: string) {
const data: Array<Article> = []
for (let index = 0; index < post.length; index++) {
const element = post[index]
if (element.frontMatter.category == category) {
data.push(element)
}
}
return data
}
export function useYearSort(post: Post[]) {
const data = []
let year = '0'
let num = -1
for (let index = 0; index < post.length; index++) {
const element = post[index]
if (element.frontMatter.date) {
const y = element.frontMatter.date.split('-')[0]
if (y === year) {
data[num].push(element)
} else {
num++
data[num] = [] as any
data[num].push(element)
year = y
}
}
}
return data
}
export function getWithBase(url: string) {
let baseUrl = url
const { theme } = useData()
if (theme.value.docRoot) {
baseUrl = url.replace('/' + theme.value.docRoot + '/', '')
}
return withBase(baseUrl)
}

@ -0,0 +1,96 @@
import { FooterConfig, HomeConfig, NavItem } from './config/baseConfig'
export interface Config {
title: string
homeConfig: HomeConfig
hasDarkSwitch: boolean
docRoot: string
nav?: NavItem[]
footer: FooterConfig
/**
* Algolia configuration for the site search.
*/
algolia?: AlgoliaSearchOptions
/**
* CarbonAds configuration
*/
carbonAds?: {
code: string
placement: string
}
/**
* Translation/Locales links
*/
}
/**
* The Algolia search options. Partially copied from
* @docsearch/react/dist/esm/DocSearch.d.ts
*/
export interface AlgoliaSearchOptions {
appId?: string
apiKey: string
indexName: string
placeholder?: string
searchParameters?: any
disableUserPersonalization?: boolean
initialQuery?: string
translations?: Partial<DocSearchTranslations>
}
export interface DocSearchTranslations {
button?: ButtonTranslations
modal?: ModalTranslations
}
export interface ButtonTranslations {
buttonText?: string
buttonAriaLabel?: string
}
export interface ModalTranslations extends ScreenStateTranslations {
searchBox?: {
resetButtonTitle?: string
resetButtonAriaLabel?: string
cancelButtonText?: string
cancelButtonAriaLabel?: string
}
footer?: {
selectText?: string
selectKeyAriaLabel?: string
navigateText?: string
navigateUpKeyAriaLabel?: string
navigateDownKeyAriaLabel?: string
closeText?: string
closeKeyAriaLabel?: string
searchByText?: string
}
}
export interface ScreenStateTranslations {
errorScreen?: {
titleText?: string
helpText?: string
}
startScreen?: {
recentSearchesTitle?: string
noRecentSearchesText?: string
saveRecentSearchButtonTitle?: string
removeRecentSearchButtonTitle?: string
favoriteSearchesTitle?: string
removeFavoriteSearchButtonTitle?: string
}
noResultsScreen?: {
noResultsText?: string
suggestedQueryText?: string
reportMissingResultsText?: string
reportMissingResultsLinkText?: string
}
}
export interface MessageWithLink {
before?: string
link?: string
after?: string
}

@ -0,0 +1,39 @@
import { UserConfig } from 'vitepress'
import { Config } from '../config'
declare const config: UserConfig<Config>
export default config
export interface Article {
regularPath: string
frontMatter: {
description: string
date: string
title: string
tags: Array<string>
}
}
export type Post = {
frontMatter: {
date: string
title: string
category: string
tags: string[]
description: string
}
regularPath: string
}
export interface HomeConfig {
headline: string //大标题
headlineHeight: string //大标题高亮
subheading: string //小标题
subheadingHeight: string //小标题高亮
description: string //描述
}
export interface NavItem {
text: string
link: string
icon?: string
}
export interface FooterConfig {
copyright: string
}

@ -0,0 +1,34 @@
const { getPosts } = require('markdown-from-posts')
async function config() {
return {
extends: {
markdown: {
headers: {
level: [2, 3]
}
},
},
themeConfig: {
posts:await getPosts(),
title: 'AAAAA',
description: '自定义主题AAAA',
docRoot:'demo',
hasDarkSwitch:true,
homeConfig:{
headline:'AAAA大标题',//大标题
headlineHeight:'AAAA高亮',//大标题高亮
subheading:'AAAA小标题',//小标题
subheadingHeight:'小标题高亮',//小标题高亮
description:'自定义主题简短banner描述',//描述
},
nav: [
{ text: '首页', link: '/', icon: '' }
],
footer: {
copyright: '湘ICP备aaaaaaaa-1'
}
}
}
}
module.exports = config()

@ -0,0 +1,28 @@
import 'normalize.css/normalize.css'
import './styles/variables.css'
import './styles/switch.css'
import './styles/custom.css'
import './styles/theme.css'
import Layout from './components/Layout.vue'
import Search from './components/Search.vue'
import Archives from './components/Archives.vue'
import Home from './components/Home.vue'
import DefaultPage from './components/DefaultPage.vue'
import CategoryPage from './components/CategoryPage.vue'
import { Theme } from 'vitepress'
import { withConfigProvider } from './composables/config'
const CustomTheme: Theme = {
Layout: withConfigProvider(Layout),
NotFound: () => 'custom 404',
enhanceApp({ app }) {
app.component('Search', Search)
app.component('Archives', Archives)
app.component('Home', Home)
app.component('DefaultPage', DefaultPage)
app.component('CategoryPage', CategoryPage)
}
}
export { CustomTheme }
export type { Config } from './config'

@ -0,0 +1,359 @@
/*keep the change things*/
html {
overflow-x: hidden;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: transparent;
font-size: 16px;
color: var(--c-text-color-light)
}
.dark body {
color: var(--c-text-color-dark);
}
/*theme reset*/
a,a:link,a:visited,a:hover,a::after{
color: var(--c-text-color-light)
}
.dark a,.dark a:link,.dark a:visited,.dark a:hover,.dark a::after {
color: var(--c-text-color-dark);
}
a:link {
text-decoration: none;
}
h1 {
font-size: 1.7rem;
}
h2 {
font-size: 1.125rem;
margin-bottom: inherit;
}
h3 {
font-size: 1rem;
}
h2+h3+h3+h4+h5 {
margin-top: 1.5rem;
}
/* 设置滚动条的样式 */
::-webkit-scrollbar {
width: 1px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.3);
border-radius: 1px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius: 1px;
background: rgba(0, 0, 0, 0.1);
-webkit-box-shadow: inset006pxrgba(0, 0, 0, 0.5);
}
::-webkit-scrollbar-thumb:window-inactive {
background: #fff;
}
.return-top{
position: fixed;
bottom: 80px;
right: 10px;
}
.max-width {
max-width: 1200px;
}
.mg-header {
margin-top: 40px;
}
/* card */
.card-header {
width: 100%;
margin: 0 auto;
}
.card-header .title {
color: var(--c-main-color);
font-size: 36px;
text-align: center;
line-height: 2;
}
.card-header .desc {
color: var(--c-text-color-dark);
font-size: 18px;
text-align: center;
line-height: 2;
}
.dark .card-header .desc {
color: var(--c-text-color-dark);
}
[class*='language-'] pre {
background-color: #000;
}
.note-box {
display: flex;
align-items: center;
width: 100%;
margin: 0 auto;
padding: 20px 0;
}
.note-box .note-item {
flex-basis: 33%;
padding: 15px;
transition: all 0.3s ease;
}
.note-box .note-item .item-box {
display: flex;
justify-content: center;
align-items: center;
height: 120px;
background-color: rgba(229, 234, 241, 0.6);
border-radius: 15px;
position: relative;
}
.note-box .note-item .item-box .title {
color: rgba(65, 86, 120, 1);
}
.dark .note-box .note-item .item-box {
background-color: rgb(82 85 89 / 60%);
}
.dark .note-box .note-item .item-box .title {
color: #fff;
}
.note-box .note-item .item-box .go {
opacity: 0;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: 15px;
background-color: var(--c-main-color);
}
.button-box {
color: #fff;
cursor: pointer;
}
.card-box {
width: 100%;
display: flex;
flex-wrap: wrap;
margin: 0 auto;
padding: 20px 0;
}
.card-box .card-item {
flex-basis: 25%;
padding: 15px;
transition: all 0.3s ease;
}
.card-box .card-item .item-box {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 15px;
padding: 15px;
background: #f8f8fc;
transition: all 0.3s ease;
}
.card-box .card-item .item-box {
border-radius: 5px;
background: #fbfafc;
box-shadow: 0 1px 2px 0 #fbfafc;
padding: 40px;
}
.dark .card-box .card-item .item-box{
background: rgb(0 0 0 / 10%);
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 10%);
}
.card-box .card-item .item-box .title {
font-size: 16px;
line-height: 2;
font-weight: 500;
color: #020312;
}
.dark .card-box .card-item .item-box .title{
color: #fbfafc;
}
.card-box .card-item .item-box .desc {
font-size: 16px;
line-height: 2;
color: #717e96;
overflow: hidden;
}
.card-box .card-item:hover .item-box .icon,
.card-box .card-item:hover .item-box .title,
.card-box .card-item:hover .item-box .desc,
.card-box .card-item .item-box.active .icon,
.card-box .card-item .item-box.active .title,
.card-box .card-item .item-box.active .desc {
color: var(--c-main-color);
}
.card-box .item-box .mark-box {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
transition: all 0.5s ease;
border-radius: 15px;
margin: auto;
opacity: 0;
}
.mark-box .mark {
color: #fff;
opacity: 0;
}
.page-banner-box {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: 0 auto;
padding: 60px 0;
}
.page-banner-box .banner {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin: 0 auto;
}
.page-banner-box .banner-right {
flex-basis: 50%;
font-size: 28px;
line-height: 2;
padding-left: 8%;
}
.page-banner-box .banner-right .title {
color: var(--c-main-color);
}
.page-banner-box .banner-right .desc {
font-size: 18px;
color: #888296;
}
.page-banner-box .banner-right .hight {
color: #000;
}
.page-banner-box .banner-left {
width: 320px;
}
.page-banner-box .banner-left img {
width: 100%;
height: 100%;
}
@media (max-width: 1200px) {
body {
font-size: 12px;
}
::-webkit-scrollbar {
display: none;
}
.logo-box {
display: none;
}
.card-header .title {
font-size: 16px;
}
.card-header .desc {
font-size: 14px;
}
.page-banner-box .banner-right {
font-size: 14px
}
.page-banner-box .banner-right .desc {
font-size: 12px
}
.note-box {
flex-wrap: wrap;
}
.note-box .note-item {
flex-basis: 100%;
}
.card-box {
flex-wrap: wrap;
}
.card-box .card-item {
flex-basis: 100%;
}
.card-box .card-item .item-box {
margin-bottom: 0;
}
.card-box .item-box .mark-box {
opacity: 1;
font-size: 12px;
}
.card-box .card-item .item-box .title {
font-size: 14px;
}
.card-box .card-item .item-box .desc {
font-size: 12px;
}
}

@ -0,0 +1,3 @@
@import 'vitepress/dist/client/theme-default/styles/fonts.css';
@import '../../core/styles/index.css';
@import './variables.css';

@ -0,0 +1,69 @@
.vt-switch {
position: relative;
border-radius: 11px;
display: block;
width: 40px;
height: 22px;
flex-shrink: 0;
border: 1px solid var(--c-bg-white);
transition: border-color 0.25s, background-color 0.25s;
}
.vt-switch:hover {
border-color: var(--c-bg-white);
}
.vt-switch-check {
position: absolute;
top: 1px;
left: 1px;
width: 18px;
height: 18px;
border-radius: 50%;
box-shadow: var(--vt-shadow-1);
transition: background-color 0.25s, transform 0.25s;
}
.vt-switch-icon {
position: relative;
display: block;
width: 18px;
height: 18px;
border-radius: 50%;
overflow: hidden;
}
.vt-switch-icon svg {
position: absolute;
top: 3px;
left: 3px;
width: 12px;
height: 12px;
fill: var(--vt-c-text-2);
}
.dark .vt-switch-icon svg {
fill: var(--vt-c-text-1);
transition: opacity 0.25s;
}
.vt-switch-appearance-sun {
opacity: 1;
}
.vt-switch-appearance-moon {
opacity: 0;
}
.dark .vt-switch-appearance-sun {
opacity: 0;
}
.dark .vt-switch-appearance-moon {
opacity: 1;
}
.dark .vt-switch-appearance .vt-switch-check {
transform: translateX(18px);
}

@ -0,0 +1,613 @@
*,:before,:after {
box-sizing: border-box
}
html {
line-height: 1.4;
font-size: 16px;
-webkit-text-size-adjust: 100%
}
body {
margin: 0;
width: 100%;
min-width: 320px;
min-height: 100vh;
line-height: 1.4;
font-family: var(--font-family-base);
font-size: 16px;
font-weight: 400;
color: var(--c-text);
background-color: var(--c-bg);
direction: ltr;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale
}
main {
display: block
}
h1,h2,h3,h4,h5,h6 {
margin: 0;
line-height: 1.25
}
h1,h2,h3,h4,h5,h6,strong,b {
font-weight: 600
}
h1:hover .header-anchor,h1:focus .header-anchor,h2:hover .header-anchor,h2:focus .header-anchor,h3:hover .header-anchor,h3:focus .header-anchor,h4:hover .header-anchor,h4:focus .header-anchor,h5:hover .header-anchor,h5:focus .header-anchor,h6:hover .header-anchor,h6:focus .header-anchor {
opacity: 1
}
h1 {
margin-top: 1.5rem;
font-size: 1.9rem
}
@media screen and (min-width: 420px) {
h1 {
font-size:2.2rem
}
}
h2 {
margin-top: 2.25rem;
margin-bottom: 1.25rem;
border-bottom: 1px solid var(--c-divider);
padding-bottom: .3rem;
line-height: 1.25;
font-size: 1.65rem
}
h2+h3 {
margin-top: 1.5rem
}
h3 {
margin-top: 2rem;
font-size: 1.35rem
}
h4 {
font-size: 1.15rem
}
p,ol,ul {
margin: 1rem 0;
line-height: 1.7
}
a,area,button,[role=button],input,label,select,summary,textarea {
touch-action: manipulation
}
a {
text-decoration: none;
color: var(--c-brand)
}
a:hover {
text-decoration: underline
}
a.header-anchor {
float: left;
margin-top: .125em;
margin-left: -.87em;
padding-right: .23em;
font-size: .85em;
opacity: 0
}
a.header-anchor:hover,a.header-anchor:focus {
text-decoration: none
}
figure {
margin: 0
}
img {
max-width: 100%
}
ul,ol {
padding-left: 1.25em
}
li>ul,li>ol {
margin: 0
}
table {
display: block;
border-collapse: collapse;
margin: 1rem 0;
overflow-x: auto
}
tr {
border-top: 1px solid #dfe2e5
}
tr:nth-child(2n) {
background-color: #f6f8fa
}
th,td {
border: 1px solid #dfe2e5;
padding: .6em 1em
}
blockquote {
margin: 1rem 0;
border-left: .2rem solid #dfe2e5;
padding: .25rem 0 .25rem 1rem;
font-size: 1rem;
color: #999
}
blockquote>p {
margin: 0
}
form {
margin: 0
}
.theme.sidebar-open .sidebar-mask {
display: block
}
.theme.no-navbar>h1,.theme.no-navbar>h2,.theme.no-navbar>h3,.theme.no-navbar>h4,.theme.no-navbar>h5,.theme.no-navbar>h6 {
margin-top: 1.5rem;
padding-top: 0
}
.theme.no-navbar aside {
top: 0
}
@media screen and (min-width: 720px) {
.theme.no-sidebar aside {
display:none
}
.theme.no-sidebar main {
margin-left: 0
}
}
.sidebar-mask {
position: fixed;
z-index: 2;
display: none;
width: 100vw;
height: 100vh
}
code {
margin: 0;
border-radius: 3px;
padding: .25rem .5rem;
font-family: var(--code-font-family);
font-size: .85em;
color: var(--c-text-light);
background-color: var(--code-inline-bg-color)
}
code .token.deleted {
color: #ec5975
}
code .token.inserted {
color: var(--c-brand)
}
div[class*=language-] {
position: relative;
margin: 1rem -1.5rem;
background-color: var(--code-bg-color);
overflow-x: auto
}
li>div[class*=language-] {
border-radius: 6px 0 0 6px;
margin: 1rem -1.5rem 1rem -1.25rem;
line-height: initial
}
@media (min-width: 420px) {
div[class*=language-] {
margin:1rem 0;
border-radius: 6px
}
li>div[class*=language-] {
margin: 1rem 0 1rem 0rem;
border-radius: 6px
}
}
[class*=language-] pre,[class*=language-] code {
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
background: transparent
}
[class*=language-] pre {
position: relative;
z-index: 1;
margin: 0;
padding: 1.25rem 1.5rem;
overflow-x: auto
}
[class*=language-] code {
padding: 0;
line-height: var(--code-line-height);
font-size: var(--code-font-size);
color: #eee
}
.highlight-lines {
position: absolute;
top: 0;
bottom: 0;
left: 0;
padding: 1.25rem 0;
width: 100%;
line-height: var(--code-line-height);
font-family: var(--code-font-family);
font-size: var(--code-font-size);
user-select: none;
overflow: hidden
}
.highlight-lines .highlighted {
background-color: #000000a8
}
div[class*=language-].line-numbers-mode {
padding-left: 3.5rem
}
.line-numbers-wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
z-index: 3;
border-right: 1px solid rgba(0,0,0,.5);
padding: 1.25rem 0;
width: 3.5rem;
text-align: center;
line-height: var(--code-line-height);
font-family: var(--code-font-family);
font-size: var(--code-font-size);
color: #888
}
div[class*=language-]:before {
position: absolute;
top: .6em;
right: 1em;
z-index: 2;
font-size: .8rem;
color: #888
}
div[class~=language-html]:before,div[class~=language-markup]:before {
content: "html"
}
div[class~=language-md]:before,div[class~=language-markdown]:before {
content: "md"
}
div[class~=language-css]:before {
content: "css"
}
div[class~=language-sass]:before {
content: "sass"
}
div[class~=language-scss]:before {
content: "scss"
}
div[class~=language-less]:before {
content: "less"
}
div[class~=language-stylus]:before {
content: "styl"
}
div[class~=language-js]:before,div[class~=language-javascript]:before {
content: "js"
}
div[class~=language-ts]:before,div[class~=language-typescript]:before {
content: "ts"
}
div[class~=language-json]:before {
content: "json"
}
div[class~=language-rb]:before,div[class~=language-ruby]:before {
content: "rb"
}
div[class~=language-py]:before,div[class~=language-python]:before {
content: "py"
}
div[class~=language-sh]:before,div[class~=language-bash]:before {
content: "sh"
}
div[class~=language-php]:before {
content: "php"
}
div[class~=language-go]:before {
content: "go"
}
div[class~=language-rust]:before {
content: "rust"
}
div[class~=language-java]:before {
content: "java"
}
div[class~=language-c]:before {
content: "c"
}
div[class~=language-yaml]:before {
content: "yaml"
}
div[class~=language-dockerfile]:before {
content: "dockerfile"
}
div[class~=language-vue]:before {
content: "vue"
}
.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata {
color: #999
}
.token.punctuation {
color: #ccc
}
.token.tag,.token.attr-name,.token.namespace,.token.deleted {
color: #e2777a
}
.token.function-name {
color: #6196cc
}
.token.boolean,.token.number,.token.function {
color: #f08d49
}
.token.property,.token.class-name,.token.constant,.token.symbol {
color: #f8c555
}
.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin {
color: #cc99cd
}
.token.string,.token.char,.token.attr-value,.token.regex,.token.variable {
color: #7ec699
}
.token.operator,.token.entity,.token.url {
color: #67cdcc
}
.token.important,.token.bold {
font-weight: 700
}
.token.italic {
font-style: italic
}
.token.entity {
cursor: help
}
.token.inserted {
color: green
}
.custom-block.tip,.custom-block.info,.custom-block.warning,.custom-block.danger {
margin: 1rem 0;
border-left: .5rem solid;
padding: .1rem 1.5rem;
overflow-x: auto
}
.custom-block.tip {
background-color: #f3f5f7;
border-color: var(--c-brand)
}
.custom-block.info {
background-color: #f3f5f7;
border-color: var(--c-text-light-2)
}
.custom-block.warning {
border-color: #e7c000;
color: #6b5900;
background-color: #ffe5644d
}
.custom-block.warning .custom-block-title {
color: #b29400
}
.custom-block.warning a {
color: var(--c-text)
}
.custom-block.danger {
border-color: #c00;
color: #4d0000;
background-color: #ffe6e6
}
.custom-block.danger .custom-block-title {
color: #900
}
.custom-block.danger a {
color: var(--c-text)
}
.custom-block.details {
position: relative;
display: block;
border-radius: 2px;
margin: 1.6em 0;
padding: 1.6em;
background-color: #eee
}
.custom-block.details h4 {
margin-top: 0
}
.custom-block.details figure:last-child,.custom-block.details p:last-child {
margin-bottom: 0;
padding-bottom: 0
}
.custom-block.details summary {
outline: none;
cursor: pointer
}
.custom-block-title {
margin-bottom: -.4rem;
font-weight: 600
}
.sidebar-links {
margin: 0;
padding: 0;
list-style: none
}
.sidebar-link-item {
display: block;
margin: 0;
border-left: .25rem solid transparent;
color: var(--c-text)
}
a.sidebar-link-item:hover {
text-decoration: none;
color: var(--c-brand)
}
a.sidebar-link-item.active {
color: var(--c-brand)
}
.sidebar>.sidebar-links {
padding: .75rem 0 5rem
}
@media (min-width: 720px) {
.sidebar>.sidebar-links {
padding:1.5rem 0
}
}
.sidebar>.sidebar-links>.sidebar-link+.sidebar-link {
padding-top: .5rem
}
@media (min-width: 720px) {
.sidebar>.sidebar-links>.sidebar-link+.sidebar-link {
padding-top:1.25rem
}
}
.sidebar>.sidebar-links>.sidebar-link>.sidebar-link-item {
padding: .35rem 1.5rem .35rem 1.25rem;
font-size: 1.1rem;
font-weight: 700
}
.sidebar>.sidebar-links>.sidebar-link>a.sidebar-link-item.active {
border-left-color: var(--c-brand);
font-weight: 600
}
.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-link-item {
display: block;
padding: .35rem 1.5rem .35rem 2rem;
line-height: 1.4;
font-size: 1rem;
font-weight: 400
}
.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>a.sidebar-link-item.active {
border-left-color: var(--c-brand);
font-weight: 600
}
.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-link-item {
display: block;
padding: .3rem 1.5rem .3rem 3rem;
line-height: 1.4;
font-size: .9rem;
font-weight: 400
}
.sidebar>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-links>.sidebar-link>.sidebar-link-item {
display: block;
padding: .3rem 1.5rem .3rem 4rem;
line-height: 1.4;
font-size: .9rem;
font-weight: 400
}
.wrap {
background-color: var(--c-bg-white);
}
.dark .wrap {
background-color: var(--c-bg-black);
}

@ -0,0 +1,59 @@
:root {
--c-bg-white: #ffffff;
--c-bg-black: #1a1a1a;
--c-text-white: #ffffff;
--c-text-black: #1a1a1a;
--c-main-color:#80adff;
--c-text-color-light:#000000;
--c-text-color-dark:#ffffff;
--c-divider-light: #e2e8f0;
--c-divider-dark: rgba(84, 84, 88, 0.48);
--c-brand: #000;
--c-brand-light: #80adff;
--font-family-base: PingFangSC-regular, 'Microsoft Yahei', sans-serif;
--font-family-mono: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
--header-height: 3.6rem;
--code-bg-color: #fff;
/* pure theme color */
--li-dot-color: #737373;
--title-color: #334155;
--date-color: #64748b;
--date-font-family: Georgia, sans-serif;
--tag-bg: #eff6ff;
--c-white: #ffffff;
--c-white-dark: #f8f8f8;
--c-black: #000000;
--c-divider-light: rgba(60, 60, 67, .12);
--c-divider-dark: rgba(84, 84, 88, .48);
--c-text-light-1: #2c3e50;
--c-text-light-2: #476582;
--c-text-light-3: #90a4b7;
--font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
--font-family-mono: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
--z-index-navbar: 10;
--z-index-sidebar: 6;
--shadow-1: 0 1px 2px rgba(0, 0, 0, .04), 0 1px 2px rgba(0, 0, 0, .06);
--shadow-2: 0 3px 12px rgba(0, 0, 0, .07), 0 1px 4px rgba(0, 0, 0, .07);
--shadow-3: 0 12px 32px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .08);
--shadow-4: 0 14px 44px rgba(0, 0, 0, .12), 0 3px 9px rgba(0, 0, 0, .12);
--shadow-5: 0 18px 56px rgba(0, 0, 0, .16), 0 4px 12px rgba(0, 0, 0, .16);
--header-height: 3.6rem
}
:root {
--c-divider: var(--c-divider-light);
--c-text: var(--c-text-light-1);
--c-text-light: var(--c-text-light-2);
--c-text-lighter: var(--c-text-light-3);
--c-bg: var(--c-white);
--c-bg-accent: var(--c-white-dark);
--code-line-height: 24px;
--code-font-family: var(--font-family-mono);
--code-font-size: 14px;
--code-inline-bg-color: rgba(27, 31, 35, .05);
--code-bg-color: #282c34
}

@ -0,0 +1,44 @@
import path from 'path'
import baseConfig from '../../src/vitepress/config/baseConfig'
async function config() {
return {
extends: baseConfig,
vite: {
build: {
minify: false
},
resolve: {
alias: {
'custom-vitepress-theme': path.join(__dirname, '../../src')
}
}
},
themeConfig: {
title: 'AAAAA',
description: '自定义主题AAAA',
docRoot:'test',
hasDarkSwitch:true,
homeConfig:{
headline:'AAAA大标题',//大标题
headlineHeight:'AAAA高亮',//大标题高亮
subheading:'AAAA小标题',//小标题
subheadingHeight:'小标题高亮',//小标题高亮
description:'自定义主题简短banner描述',//描述
},
nav: [
{ text: '首页', link: '/', icon: '' },
{ text: '归档', link: '/pages/archives', icon: '' },
{ text: '默认', link: '/pages/default', icon: '' },
{ text: '分类', link: '/pages/category', icon: '' },
{ text: '搜索', link: '/pages/search', icon: '' },
],
footer: {
copyright: '湘ICP备aaaaaaaa-1'
}
}
}
}
module.exports = config()

@ -0,0 +1,12 @@
import { CustomTheme } from 'custom-vitepress-theme'
import { h } from 'vue'
import './override.css'
export default {
...CustomTheme,
Layout() {
return h(CustomTheme.Layout, null, {
})
}
}

@ -0,0 +1,97 @@
:root {
--c-main-color:#80adff;
}
/* card */
.news-box {
display: flex;
align-items: center;
width: 100%;
margin: 0 auto;
padding: 20px 0;
}
.news-box .card-item {
flex-basis: 25%;
padding: 15px;
transition: all 0.3s ease;
}
.news-box .card-item .item-box {
display: flex;
flex-direction: column;
align-items: center;
border-radius: 15px;
padding: 15px;
margin-bottom: 140px;
background: #f8f8fc;
transition: all 0.3s ease;
}
.news-box .card-item .item-box .icon {
font-size: 48px;
color: var(--c-main-color);
}
.news-box .card-item .item-box .title {
font-size: 18px;
line-height: 2;
font-weight: 500;
color: #020312;
}
.news-box .card-item .item-box .desc {
font-size: 16px;
line-height: 2;
color: #717e96;
overflow: hidden;
}
.news-box .card-item .item-box .desc {
font-size: 16px;
line-height: 2;
color: #717e96;
}
.news-box .card-item:hover .item-box {
background: var(--c-main-color);
transition: all 0.3s ease;
}
.news-box .card-item .item-box.active {
background: var(--c-main-color);
margin-bottom: 0;
}
.news-box .card-item:hover .item-box .icon,
.news-box .card-item:hover .item-box .title,
.news-box .card-item:hover .item-box .desc,
.news-box .card-item .item-box.active .icon,
.news-box .card-item .item-box.active .title,
.news-box .card-item .item-box.active .desc {
color: #fff;
}
@media (max-width: 1200px) {
.banner .banner-left {
font-size: 14px;
}
.banner .banner-left .desc {
font-size: 12px;
}
.note-box {
flex-wrap: wrap;
}
.note-box .note-item {
flex-basis: 100%;
}
.news-box {
flex-wrap: wrap;
}
.news-box .card-item {
flex-basis: 100%;
}
.news-box .card-item .item-box {
margin-bottom: 0;
}
.news-box .card-item .item-box .title {
font-size: 14px;
}
.news-box .card-item .item-box .desc {
font-size: 12px;
}
}

@ -0,0 +1,95 @@
---
page: true
date: 2021-06-30
title: 自定义标题
sidebar: false
---
<script setup>
</script>
<Home>
<template #banner>
<img src="/img/page.png" />
</template>
<template #description>
<!-- 一些其他描述 -->
</template>
<div class="card-header max-width">
<div class="title">手册</div>
<div class="desc">基础知识手册(学习基础笔记)</div>
</div>
<div class="note-box max-width">
<div class="note-item">
<div class="item-box">
<div class="title">💡CSS手册</div>
<div class="go">
<span class="button-box"
><a href="https://css.web.ppst.top/">💡CSS手册</a></span
>
</div>
</div>
</div>
<div class="note-item">
<div class="item-box">
<div class="title">💡JS手册</div>
<div class="go">
<span class="button-box"><a href="https://js.web.ppst.top/">💡JS手册</a></span>
</div>
</div>
</div>
<div class="note-item">
<div class="item-box">
<div class="title">💡博客</div>
<div class="go">
<span class="button-box"><a href="https://blog.web.ppst.top/">💡博客</a></span>
</div>
</div>
</div>
</div>
<div class="card-header max-width">
<div class="title">随笔</div>
<div class="desc">最常用随笔分类</div>
</div>
<div class="news-box max-width">
<div class="card-item">
<div class="item-box">
<div class="icon">?</div>
<div class="title">JS笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
<div class="card-item">
<div class="item-box active">
<div class="icon">?</div>
<div class="title">vue笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
<div class="card-item">
<div class="item-box">
<div class="icon">?</div>
<div class="title">服务器笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
<div class="card-item">
<div class="item-box active">
<div class="icon">?</div>
<div class="title">浏览器笔记</div>
<div class="desc">
我就是记录一些笔记,以及异常处理经验记录,工作中遇到问题记录解决方案
</div>
</div>
</div>
</div>
</Home>

@ -0,0 +1,13 @@
---
page: true
title: Archive
description: Archive
sidebar: false
---
<Archives>
<template #banner>
<img src="/img/banner.png" />
</template>
</Archives>

@ -0,0 +1,13 @@
---
page: true
title: 分类
description: 分类
sidebar: false
---
<CategoryPage title='分类' category='demo' description="分类">
<template #banner>
<img src="/img/banner.png" />
</template>
</CategoryPage>

@ -0,0 +1,13 @@
---
page: true
title: 默认
description: 默认
sidebar: false
---
<DefaultPage title='默认' category='default' description="默认">
<template #banner>
<img src="/img/banner.png" />
</template>
</DefaultPage>

@ -0,0 +1,11 @@
---
page: true
title: 搜索
description: 全文检索
sidebar: false
---
<Search>
<template #banner>
<img src="/img/banner.png" />
</template>
</Search>

@ -0,0 +1,63 @@
---
title: default
date: 2018-09-14 13:57:02
category: default
tags:
- default
---
# 主标题
## 副标题
我是内容
```js
let a = '我是js代码'
```
```css
.default{
color:red;
}
```
```shell
npm install default
```
### 副标题1
我是内容
```js
let a = '我是js代码'
```
```css
.default{
color:red;
}
```
```shell
npm install default
```
#### 副标题111
我是内容
```js
let a = '我是js代码'
```
```css
.default{
color:red;
}
```
```shell
npm install default
```

@ -0,0 +1,63 @@
---
title: demo
date: 2018-09-14 13:57:02
category: demo
tags:
- demo
---
# 主标题
## 副标题
我是内容
```js
let a = '我是js代码'
```
```css
.demo{
color:red;
}
```
```shell
npm install demo
```
### 副标题1
我是内容
```js
let a = '我是js代码'
```
```css
.demo{
color:red;
}
```
```shell
npm install demo
```
#### 副标题111
我是内容
```js
let a = '我是js代码'
```
```css
.demo{
color:red;
}
```
```shell
npm install demo
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

@ -0,0 +1,20 @@
{
"compilerOptions": {
"baseUrl": ".",
"outDir": "dist",
"allowJs": true,
"module": "esnext",
"moduleResolution": "node",
"strict": true,
"jsx": "preserve",
"esModuleInterop": true,
"noUnusedLocals": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"paths": {
"custom-vitepress-theme": ["src/index.ts"]
}
},
"include": ["src", "demo/.vitepress/theme", "test/.vitepress/theme"],
"exclude": ["node_modules"]
}
Loading…
Cancel
Save