最终效果
使用方式
页面效果
一、需求
vitepress-theme-demoblock插件在编辑md文件时,不支持TSX模式,也无法import其他依赖,并且展开隐藏的vue代码都在md文件中,不够优雅,且每个md文件太长。至此弃用此拆件,改用自己封装demo组件
二、plugins配置:在docs/.vitepress下新建config文件夹
1、在config文件夹新建global.ts文件,代码如下
import { resolve } from 'path'
// 项目目录
export const projRoot = resolve(__dirname, '..', '..', '..')
// 项目名称
export const docsDirName = 'docs'
// 文档库目录
export const docRoot = resolve(projRoot, docsDirName)
2、在docs/.vitepress下新建utils文件夹,并新建highlight.ts文件,代码如下
# 依赖安装
pnpm add chalk escape-html prismjs consola -D
// 代码高亮
import chalk from 'chalk'
// @ts-ignore
import escapeHtml from 'escape-html'
// @ts-ignore
import prism from 'prismjs'
import consola from 'consola'
// prism is listed as actual dep so it's ok to require
// eslint-disable-next-line @typescript-eslint/no-var-requires
// const loadLanguages = require('prismjs/components/index')
import loadLanguages from 'prismjs/components/index'
// required to make embedded highlighting work...
loadLanguages(['markup', 'css', 'javascript'])
function wrap(code: string, lang: string): string {
if (lang === 'text') {
code = escapeHtml(code)
}
return `<pre v-pre><code>${code}</code></pre>`
}
export const highlight = (str: string, lang: string) => {
if (!lang) {
return wrap(str, 'text')
}
lang = lang.toLowerCase()
const rawLang = lang
if (lang === 'vue' || lang === 'html') {
lang = 'markup'
}
if (lang === 'md') {
lang = 'markdown'
}
if (lang === 'ts') {
lang = 'typescript'
}
if (lang === 'py') {
lang = 'python'
}
if (!prism.languages[lang]) {
try {
loadLanguages([lang])
} catch {
// eslint-disable-next-line no-console
consola.warn(
chalk.yellow(
`[vitepress] Syntax highlight for language "${lang}" is not supported.`
)
)
}
}
if (prism.languages[lang]) {
const code = prism.highlight(str, prism.languages[lang], lang)
return wrap(code, rawLang)
}
return wrap(str, 'text')
}
3、在config文件夹新建plugins.ts文件,代码如下
# 依赖安装
pnpm add markdown-it markdown-it-container -D
import path from 'path'
import fs from 'fs'
// @ts-ignore
import MarkdownIt from 'markdown-it'
// @ts-ignore
import mdContainer from 'markdown-it-container'
// @ts-ignore
import type Token from 'markdown-it/lib/token'
import { highlight } from '../utils/highlight'
import { docRoot } from './global'
const localMd = MarkdownIt()
interface ContainerOpts {
marker?: string | undefined
validate?(params: string): boolean
render?(tokens: Token[], index: number): string
}
export const mdPlugin = (md: MarkdownIt) => {
md.use(mdContainer, 'demo', {
validate(params) {
return !!params.trim().match(/^demo\s*(.*)$/)
},
render(tokens, idx) {
const m = tokens[idx].info.trim().match(/^demo\s+(.*)$/)
if (tokens[idx].nesting === 1) {
const description = m && m.length > 1 ? m[1] : ''
const sourceFileToken = tokens[idx + 2]
let source = ''
// demo文件名称
const sourceFile = sourceFileToken.children?.[0].content ?? ''
if (sourceFileToken.type === 'inline') {
// 读取示列代码文件
source = fs.readFileSync(
path.resolve(docRoot, 'examples', `${sourceFile}.vue`),
'utf-8'
)
}
if (!source) throw new Error(`Incorrect source file: ${sourceFile}`)
// opening tag
return `<Demo
source="${encodeURIComponent(highlight(source, 'vue'))}"
path="${sourceFile}"
raw-source="${encodeURIComponent(source)}"
description="${encodeURIComponent(
localMd.render(description)
)}">`
} else {
// closing tag
return '</Demo>\n'
}
},
} as ContainerOpts)
}
三、在config.ts中配置markdown,新增如下代码:
import { mdPlugin } from './config/plugins'
export default defineConfig({
markdown: {
config: (md) => mdPlugin(md),
},
})
四、vp-demo组件封装:在docs/.vitepress下新建vitepress文件夹
1、vitepress下新建components文件夹在新建vp-demo文件夹
1、在vp-demo文件夹下新建vp-example.vue文件(渲染.vue文件),代码如下:
<template>
<ClientOnly>
<div class="example-component">
<component
:is="dynamicComponent"
v-if="dynamicComponent"
v-bind="$attrs"
/>
<div v-else class="example-component--spin">
<div></div>
<div></div>
</div>
</div>
</ClientOnly>
</template>
<script lang="ts" setup>
import { onBeforeMount, shallowRef } from 'vue'
const props: any = defineProps<{
path?: string
}>()
// 创建一个跟踪自身 .value 变化的 ref,但不会使其值也变成响应式的。
let dynamicComponent = shallowRef(null)
onBeforeMount(() => {
// 匹配到的文件默认是懒加载的,通过动态导入实现,并会在构建时分离为独立的 chunk。如果你倾向于直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用),你可以传入 { eager: true } 作为第二个参数:
const modules = import.meta.glob(`../../../../examples/*/*.vue`, {
eager: true,
})
// 动态加载示列组件
for (const modulesKey in modules) {
const module = modules[modulesKey]
// 找到example的组件,并加载
if (modulesKey.split('.vue')[0].endsWith(props.path)) {
dynamicComponent.value = module.default
}
}
})
</script>
<style lang="scss" scoped>
// loading动画
@keyframes lds-ripple {
0% {
top: 18px;
left: 18px;
width: 0;
height: 0;
opacity: 0;
}
4.9% {
top: 18px;
left: 18px;
width: 0;
height: 0;
opacity: 0;
}
5% {
top: 18px;
left: 18px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0px;
left: 0px;
width: 36px;
height: 36px;
opacity: 0;
}
}
.example-component {
// min-height: 86px;
// padding: 1.5rem;
&--spin {
width: 36px;
height: 36px;
display: inline-block;
position: relative;
> div {
position: absolute;
border: 4px solid var(--theme-light);
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
div:nth-child(2) {
animation-delay: -0.5s;
}
}
}
</style>
2、在vp-demo文件夹下新建vp-source-code.vue文件(vue源码展开隐藏文件),代码如下:
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps({
source: {
type: String,
required: true,
},
})
const decoded = computed(() => {
return decodeURIComponent(props.source)
})
</script>
<template>
<div class="example-source-wrapper">
<div class="example-source language-vue" v-html="decoded" />
</div>
</template>
<style scoped lang="scss">
.language-vue {
margin: 0 !important;
border-radius: 0;
}
</style>
3、在vp-demo文件夹下新建index.vue文件,代码如下:
<template>
<ClientOnly>
<p text="sm" v-html="decodedDescription" />
<div class="example">
<Example :path="path" />
<ElDivider class="m-0" />
<div class="op-btns">
<ElTooltip content="复制代码" :show-arrow="false">
<ElIcon :size="16" class="op-btn" @click="copyCode">
<CopyDocument />
</ElIcon>
</ElTooltip>
<ElTooltip content="查看源代码" :show-arrow="false">
<ElIcon :size="16" class="op-btn" @click="toggleSourceVisible()">
<View />
</ElIcon>
</ElTooltip>
</div>
<ElCollapseTransition>
<SourceCode v-show="sourceVisible" :source="source" />
</ElCollapseTransition>
<Transition name="el-fade-in-linear">
<div
v-show="sourceVisible"
class="example-float-control"
@click="toggleSourceVisible(false)"
>
<ElIcon :size="16">
<CaretTop />
</ElIcon>
<span>隐藏源代码</span>
</div>
</Transition>
</div>
</ClientOnly>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { ElMessage } from 'element-plus'
import { useClipboard, useToggle } from '@vueuse/core'
import Example from './vp-example.vue'
import SourceCode from './vp-source-code.vue'
const props = defineProps<{
rawSource: string // 源码
source: string
path: string
description?: string
}>()
const { copy, isSupported } = useClipboard({
source: decodeURIComponent(props.rawSource),
read: false,
})
const [sourceVisible, toggleSourceVisible] = useToggle(false)
const decodedDescription = computed(() =>
decodeURIComponent(props.description!)
)
const copyCode = async () => {
if (!isSupported) {
ElMessage.error('复制失败')
}
try {
await copy()
ElMessage.success('已复制')
} catch (e: any) {
ElMessage.error(e.message)
}
}
</script>
<style lang="scss" scoped>
.example {
border: 1px solid var(--border-color);
border-radius: var(--el-border-radius-base);
.m-0 {
margin: 0;
}
.op-btns {
padding: 0.5rem;
display: flex;
align-items: center;
justify-content: flex-end;
height: 2.5rem;
.el-icon {
&:hover {
color: var(--text-color);
}
}
.op-btn {
margin: 0 0.5rem;
cursor: pointer;
color: var(--text-color-lighter);
transition: 0.2s;
&.github a {
transition: 0.2s;
color: var(--text-color-lighter);
&:hover {
color: var(--text-color);
}
}
}
}
&-float-control {
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid var(--border-color);
height: 44px;
box-sizing: border-box;
background-color: var(--bg-color, #fff);
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
margin-top: -1px;
color: var(--el-text-color-secondary);
cursor: pointer;
position: sticky;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
span {
font-size: 14px;
margin-left: 10px;
}
&:hover {
color: var(--el-color-primary);
}
}
}
</style>
2、vitepress下新建style文件夹
1、新建css-vars.scss文件(变量配置),代码如下
@use './mixins' as *;
// css variables
:root {
// layouts
--vp-screen-max-width: 1376px;
--vp-c-brand: #646cff;
--vp-c-brand-light: #747bff;
--vp-c-brand-lighter: #9499ff;
--vp-c-brand-lightest: #bcc0ff;
--vp-c-brand-dark: #535bf2;
--vp-c-brand-darker: #454ce1;
// colors
--text-color: var(--el-text-color-primary);
--text-color-light: var(--el-text-color-regular);
--text-color-lighter: var(--el-text-color-secondary);
--brand-color: var(--el-color-primary);
--brand-color-light: var(--el-color-primary-light-1);
--bg-brand-color: var(--el-color-primary-light-9);
--bg-color: var(--el-bg-color);
--bg-color-rgb: 255, 255, 255;
--bg-color-soft: #fafafa;
--bg-color-mute: #f2f2f2;
--border-color: var(--el-border-color);
--border-color-light: var(--el-border-color-lighter);
--font-family: var(--el-font-family);
--font-family-mono: 'JetBrains Mono', source-code-pro, Menlo, Monaco, Consolas,
'Courier New', monospace;
// info
--success-color: var(--el-color-success);
--warning-color: var(--el-color-warning);
--danger-color: var(--el-color-danger);
// header vars
--header-height: 55px;
--nav-height: 55px; // alias of --header-height
/* Screen Size */
--vp-screen-max-width: 1362px;
@include respond-to('xxl') {
--vp-sidebar-width-small: 234px;
}
@include respond-to('max') {
--vp-screen-max-width: 1482px;
--vp-sidebar-width-small: 290px;
}
// sidebar
--vp-sidebar-width-mobile: 320px;
--vp-sidebar-width-small: 266px;
--sidebar-width-sm: 16rem;
--sidebar-width-xs: 20rem;
--content-min-width: 16rem;
--content-max-width: 48rem;
--nav-z-index: 11;
--sub-nav-z-index: 11;
--sidebar-z-index: 40;
--overlay-z-index: 30;
// --dropdown-z-index: 22;
// code block vars
--code-line-height: 1.4;
--code-font-size: var(--el-font-size-base);
--code-bg-color: var(--el-fill-color-light);
--code-text-color: var(--text-color);
--code-font-family: var(--font-family-mono);
// tip block
--block-tip-bg-color: rgba(var(--el-color-primary-rgb), 0.1);
--block-warning-bg-color: rgba(var(--el-color-danger-rgb), 0.1);
// link
--link-active-bg-color: rgba(var(--el-color-primary-rgb), 0.1);
}
.dark {
--bg-color-rgb: 0, 0, 0;
--bg-color-soft: #242424;
--bg-color-mute: #2c2c2c;
}
2、新建code.css文件(展开隐藏代码样式),代码如下
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: var(--prism-comment);
font-style: var(--prism-comment-style);
}
.token.namespace {
color: var(--prism-namespace);
}
.token.interpolation {
color: var(--prism-interpolation);
}
.token.string {
color: var(--prism-string);
}
.token.punctuation {
color: var(--prism-punctuation);
}
.token.operator {
color: var(--prism-operator);
}
.token.keyword.module,
.token.keyword.control-flow {
color: var(--prism-keyword-control);
}
.token.url,
.token.symbol,
.token.inserted {
color: var(--prism-symbol);
}
.token.constant {
color: var(--prism-constant);
}
.token.string.url {
text-decoration: var(--prism-url-decoration);
}
.token.boolean,
.language-json .token.boolean {
color: var(--prism-boolean);
}
.token.number,
.language-json .token.number {
color: var(--prism-number);
}
.token.variable {
color: var(--prism-variable);
}
.token.keyword {
color: var(--prism-keyword);
}
.token.atrule,
.token.attr-value,
.token.selector {
color: var(--prism-selector);
}
.token.function {
color: var(--prism-function);
}
.token.deleted {
color: var(--prism-deleted);
}
.token.important,
.token.bold {
font-weight: 700;
}
.token.italic {
font-style: italic;
}
.token.class-name {
color: var(--prism-class);
}
.token.tag,
.token.builtin {
color: var(--prism-builtin);
}
.token.attr-name,
.token.property,
.token.entity {
color: var(--prism-property);
}
.language-json .token.property {
color: var(--prism-json-property);
}
.token.regex {
color: var(--prism-regex);
}
.token.decorator,
.token.annotation {
color: var(--prism-decorator);
}
.line-numbers .line-numbers-rows {
border-right-color: var(--prism-line-number);
}
.line-numbers-rows > span:before {
color: var(--prism-line-number-gutter);
}
.line-highlight {
background: var(--prism-line-highlight-background);
}
[class*='language-']:before {
position: absolute;
top: 0.4em;
right: 0.8em;
z-index: 2;
opacity: var(--prism-marker-opacity);
font-size: var(--prism-marker-font-size);
color: var(--prism-marker-color);
}
[class~='language-html']:before,
[class~='language-markup']:before {
content: 'html';
}
[class~='language-md']:before,
[class~='language-markdown']:before {
content: 'md';
}
[class~='language-css']:before {
content: 'css';
}
[class~='language-sass']:before {
content: 'sass';
}
[class~='language-scss']:before {
content: 'scss';
}
[class~='language-less']:before {
content: 'less';
}
[class~='language-stylus']:before {
content: 'styl';
}
[class~='language-js']:before,
[class~='language-typescript']:before {
content: 'js';
}
[class~='language-ts']:before,
[class~='language-typescript']:before {
content: 'ts';
}
[class~='language-json']:before {
content: 'json';
}
[class~='language-rb']:before,
[class~='language-ruby']:before {
content: 'rb';
}
[class~='language-py']:before,
[class~='language-python']:before {
content: 'py';
}
[class~='language-sh']:before,
[class~='language-bash']:before {
content: 'sh';
}
[class~='language-php']:before {
content: 'php';
}
[class~='language-go']:before {
content: 'go';
}
[class~='language-rust']:before {
content: 'rust';
}
[class~='language-java']:before {
content: 'java';
}
[class~='language-c']:before {
content: 'c';
}
[class~='language-yaml']:before {
content: 'yaml';
}
[class~='language-vue']:before {
content: 'vue';
}
[class~='language-dockerfile']:before {
content: 'dockerfile';
}
[class*='language-']:before {
font-family: var(--code-font-family);
}
span[class~='language-css']:before {
content: '';
}
:root {
--el-fill-color-light: #f5f7fa;
--el-fill-color-lighter: #fafafa;
--prism-marker-opacity: 0.6;
--prism-marker-color: var(--code-text-color);
--prism-line-height: var(--code-line-height);
}
html:not(.dark) {
--prism-builtin: #3182bd;
--prism-comment: #848486;
--prism-deleted: #3182bd;
--prism-function: #6196cc;
--prism-boolean: #c25205;
--prism-number: #c25205;
--prism-property: #717c11;
--prism-punctuation: #a8a9cc;
--prism-keyword: #c792ea;
--prism-variable: #0b8235;
--prism-url-decoration: #67cdcc;
--prism-symbol: green;
--prism-selector: #0b8235;
}
html.dark {
--prism-scheme: dark;
--prism-foreground: #a6accd;
--prism-background: #181818;
--prism-comment: #758575;
--prism-string: #c3e88d;
--prism-literal: #429988;
--prism-keyword: #89ddff;
--prism-boolean: #6394bf;
--prism-number: #6394bf;
--prism-variable: #c2b36e;
--prism-function: #82aaff;
--prism-deleted: #bc6066;
--prism-class: #54b1bf;
--prism-builtin: var(--el-color-primary-light-3);
--prism-property: #c792ea;
--prism-namespace: #db889a;
--prism-punctuation: #89ddff;
--prism-decorator: #bd8f8f;
--prism-regex: #ab5e3f;
--prism-json-property: #6b8b9e;
--prism-line-number: #888888;
--prism-line-number-gutter: #eeeeee;
--prism-line-highlight-background: #444444;
--prism-selection-background: #444444;
--prism-inline-background: #2d2d2d;
}
code {
margin: 0;
border-radius: 4px;
padding: 0.15rem 0.5rem;
font-family: var(--code-font-family);
font-size: var(--code-font-size);
color: var(--code-text-color);
line-height: var(--code-line-height);
background-color: var(--code-bg-color);
}
pre code {
background-color: transparent;
}
a > code {
color: var(--code-link-color);
}
code .token.deleted {
color: #ec5975;
}
code .token.inserted {
color: var(--c-brand);
}
div[class*='language-'] {
position: relative;
margin: 0;
background-color: var(--code-bg-color);
overflow-x: auto;
}
li > div[class*='language-'] {
border-radius: 6px 0 0 6px;
margin: 0;
}
@media (min-width: 420px) {
div[class*='language-'] {
margin: 0;
border-radius: 6px;
}
li > div[class*='language-'] {
margin: 0;
border-radius: 6px;
}
}
[class*='language-'] pre,
[class*='language-'] code {
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
tab-size: 4;
hyphens: none;
}
[class*='language-'] pre {
position: relative;
z-index: 1;
margin: 0;
padding: 0.25rem;
background: transparent;
overflow-x: auto;
}
[class*='language-'] code {
padding: 0;
line-height: var(--code-line-height);
font-size: var(--code-font-size);
color: var(--code-text-color);
}
.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 var(--el-overlay-color-lighter);
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;
}
.navbar-menu {
display: none;
}
@media screen and (min-width: 768px) {
.navbar-menu {
display: flex;
}
}
.navbar-wrapper {
position: relative;
border-bottom: 1px solid var(--border-color);
height: var(--header-height);
padding: 0 12px 0 24px;
background-image: radial-gradient(transparent 1px, var(--bg-color) 1px);
background-size: 4px 4px;
backdrop-filter: saturate(50%) blur(4px);
-webkit-backdrop-filter: saturate(50%) blur(4px);
top: 0;
}
@media screen and (min-width: 768px) {
.navbar-wrapper {
padding: 0 12px 0 32px;
}
}
@media screen and (min-width: 1280px) {
.navbar-wrapper {
padding: 0 32px;
}
}
.navbar-wrapper .header-container {
display: flex;
justify-content: space-between;
margin: 0 auto;
}
.navbar-wrapper .header-container .content {
display: flex;
justify-content: flex-end;
align-items: center;
flex-grow: 1;
}
@media screen and (min-width: 1440px) {
.navbar-wrapper .header-container {
max-width: calc(var(--vp-screen-max-width));
}
}
@media screen and (min-width: 1680px) {
.navbar-wrapper .header-container {
max-width: calc(var(--vp-screen-max-width));
}
}
@media screen and (min-width: 768px) {
.navbar-wrapper .content {
padding-top: 1px;
}
}
.navbar {
top: 0;
left: 0;
position: relative;
z-index: var(--nav-z-index);
}
@media screen and (min-width: 960px) {
.navbar {
position: sticky;
width: 100%;
}
}
.menu + .appearance {
margin-left: 8px;
}
.menu + .social-links {
margin-left: 12px;
}
.appearance + .social-links {
margin-left: 12px;
}
* {
scrollbar-color: var(--el-scrollbar-bg-color) var(--el-fill-color-light);
}
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar:horizontal {
height: 6px;
}
::-webkit-scrollbar-track {
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background-color: #0003;
border-radius: 10px;
transition: all 0.2s ease-in-out;
}
::-webkit-scrollbar-thumb:hover {
cursor: pointer;
background-color: #0000004d;
}
.dark ::-webkit-scrollbar-thumb {
background-color: #fff3;
}
.dark ::-webkit-scrollbar-thumb:hover {
background-color: #fff6;
}
.sub-nav {
border-bottom: 1px solid var(--border-color);
background-color: var(--bg-color);
color: var(--text-color);
transition: border-color var(--el-transition-duration),
background-color var(--el-transition-duration-fast);
position: sticky;
top: 0;
left: 0;
width: 100%;
display: flex;
padding: 0 32px;
justify-content: space-between;
z-index: var(--sub-nav-z-index);
overflow: hidden;
}
@media (max-width: 767px) {
.sub-nav {
padding: 0 24px;
}
}
.sub-nav .go-back-top {
transform: translateY(100%);
opacity: 0;
}
.sub-nav .go-back-top.show {
transform: translateY(0);
opacity: 1;
}
@media screen and (min-width: 960px) {
.sub-nav {
display: none !important;
}
}
.overlay {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, 0.6);
transition: opacity 0.5s;
z-index: var(--overlay-z-index);
}
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: var(--sidebar-z-index);
width: var(--sidebar-width-xs);
background-color: var(--bg-color);
padding: 48px 32px 0;
overflow-y: auto;
transform: translate(-100%);
transition: background-color var(--el-transition-duration-fast), opacity 0.25s,
transform 0.5s cubic-bezier(0.19, 1, 0.22, 1);
}
.sidebar.open {
transform: translate(0);
}
.sidebar .sidebar-groups {
padding: 0 0 5rem;
}
.sidebar .sidebar-groups .sidebar-group__title {
font-size: 1rem;
font-weight: 700;
margin-bottom: 8px;
line-height: 24px;
}
.sidebar .sidebar-groups .sidebar-group + .sidebar-group {
padding-top: 24px;
}
@media (max-width: 767px) {
.sidebar {
width: calc(var(--vp-sidebar-width-mobile) - 14px);
}
}
@media screen and (min-width: 768px) {
.sidebar {
width: calc(var(--vp-sidebar-width-small));
}
}
@media screen and (min-width: 960px) {
.sidebar {
top: var(--header-height);
transform: translate(0);
}
}
@media screen and (min-width: 1440px) {
.sidebar {
padding: 48px 32px 96px calc((100vw - var(--vp-screen-max-width)) / 2);
width: calc(
(100vw - var(--vp-screen-max-width)) / 2 + var(--vp-sidebar-width-small)
);
}
}
@media screen and (min-width: 1680px) {
.sidebar {
padding: 48px 48px 96px calc((100vw - var(--vp-screen-max-width)) / 2);
width: calc(
(100vw - var(--vp-screen-max-width)) / 2 + var(--vp-sidebar-width-small)
);
}
}
.toc-wrapper {
display: none;
padding-left: 64px;
}
.toc-wrapper .toc-content {
position: sticky;
top: calc(var(--header-height) + 32px);
margin-top: 0;
padding: 4px 8px 4px 12px;
margin-bottom: 32px;
width: 200px;
}
.toc-wrapper .toc-content .toc-marker {
opacity: 0;
position: absolute;
background-color: var(--brand-color);
border-radius: 4px;
width: 4px;
height: 14px;
top: 30px;
left: 0;
z-index: 0;
transition: top 0.25s cubic-bezier(0, 1, 0.5, 1), opacity 0.25s,
background-color 0.5s;
}
.toc-wrapper .toc-content__heading {
font-size: 12px;
color: var(--text-color-light);
font-weight: 600;
text-transform: uppercase;
margin-top: 0;
}
.toc-wrapper .toc-content .toc-items {
list-style: none;
padding: 0;
margin: 12px 0 0;
line-height: 1.2;
}
.toc-wrapper .toc-content .toc-items .toc-item {
margin-top: 10px;
font-size: 11px;
color: var(--text-color-lighter);
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
color: inherit;
}
.toc-wrapper .toc-content .toc-items .toc-item .toc-link {
position: relative;
color: var(--text-color-lighter);
transition: color var(--el-transition-duration);
}
.toc-wrapper .toc-content .toc-items .toc-item .toc-link.active {
color: var(--brand-color);
}
.toc-wrapper .toc-content .toc-items .toc-item.subitem {
padding-left: 1rem;
}
@media screen and (min-width: 1440px) {
.toc-wrapper {
display: block;
}
}
@media screen and (min-width: 1680px) {
.toc-wrapper {
padding-left: 96px;
display: block;
}
}
.doc-content-wrapper {
--vp-content-width: 800px;
padding: 32px 24px 96px;
}
.doc-content-wrapper .doc-content-container .doc-content {
width: 100%;
}
@media screen and (min-width: 1440px) {
.doc-content-wrapper .doc-content-container {
width: var(--vp-content-width);
}
}
@media screen and (min-width: 768px) {
.doc-content-wrapper {
padding: 48px 32px 42px;
}
}
@media screen and (min-width: 960px) {
.doc-content-wrapper {
padding: 64px 64px 42px;
}
}
@media screen and (min-width: 1440px) {
.doc-content-wrapper {
padding: 64px 0 42px 64px;
display: flex;
}
}
@media screen and (min-width: 1680px) {
.doc-content-wrapper {
padding: 64px 0 42px 96px;
display: flex;
}
}
.hero-content {
padding: 40px 40px 0;
}
@media (max-width: 768px) {
.hero-content {
padding: 30px 10px 0;
}
}
.vp-tag {
--vp-tag-color: var(--el-color-primary);
--vp-tag-border-color: var(--el-color-primary);
display: inline-block;
padding: 0 7px;
border-radius: 10px;
border: 1px solid var(--vp-tag-border-color);
font-size: var(--el-font-size-extra-small);
color: var(--vp-tag-color);
line-height: 18px;
white-space: nowrap;
}
.vp-tag.beta {
--vp-tag-color: var(--el-color-danger);
--vp-tag-border-color: var(--el-color-danger);
}
.vp-tag.deprecated {
--vp-tag-color: var(--el-color-warning);
--vp-tag-border-color: var(--el-color-warning);
}
.vp-tag.a11y {
--vp-tag-color: var(--purple-color);
--vp-tag-border-color: var(--purple-color-light);
}
.page-content {
outline: none;
}
@media screen and (min-width: 960px) {
.page-content {
padding-top: var(--nav-height);
}
.page-content.has-sidebar {
padding-left: calc(var(--sidebar-width-sm) + 10px);
}
}
@media screen and (min-width: 960px) and (min-width: 1280px) {
.page-content.has-sidebar {
padding-left: calc(var(--vp-sidebar-width-small) - 6px);
}
}
@media screen and (min-width: 960px) and (min-width: 1440px) {
.page-content.has-sidebar {
padding-left: calc(
(100% - var(--vp-screen-max-width)) / 2 + var(--vp-sidebar-width-small)
);
}
}
.page-content .doc-content a {
display: inline-flex;
align-items: center;
}
.page-content .doc-content a.vp-link {
white-space: nowrap;
word-break: keep-all;
}
.page-content .doc-content a .link-icon {
margin-left: 0.25rem;
height: 1em;
width: 1em;
}
.container {
max-width: 100%;
}
@media (min-width: 640px) {
.container {
max-width: 640px;
}
}
@media (min-width: 768px) {
.container {
max-width: 768px;
}
}
@media (min-width: 1024px) {
.container {
max-width: 1024px;
}
}
@media (min-width: 1280px) {
.container {
max-width: 1280px;
}
}
@media (min-width: 1536px) {
.container {
max-width: 1536px;
}
}
.\!visible {
visibility: visible !important;
}
.visible {
visibility: visible;
}
.invisible {
visibility: hidden;
}
.absolute {
position: absolute;
}
.fixed,
[fixed=''] {
position: fixed;
}
.relative {
position: relative;
}
.sticky {
position: sticky;
}
3、新建vars.scss、mixins.scss、index文件,代码如下
// vars.scss
$breakpoint-max: 1680px !default;
$breakpoint-xxl: 1440px !default;
$breakpoint-xlg: 1280px !default;
$breakpoint-lg: 960px !default;
$breakpoint-md: 768px !default;
$breakpoint-sm: 480px !default;
$scrollbar-size: 6px;
// mixins.scss
@use 'sass:map';
@use './vars' as *;
@mixin with-bg {
background-color: var(--bg-color);
color: var(--text-color);
transition: border-color var(--el-transition-duration),
background-color var(--el-transition-duration-fast);
}
@mixin with-border {
border-bottom: 1px solid var(--border-color);
}
$breakpoints: (
'sm': #{$breakpoint-sm},
'md': #{$breakpoint-md},
'lg': #{$breakpoint-lg},
'xlg': #{$breakpoint-xlg},
'xxl': #{$breakpoint-xxl},
'max': #{$breakpoint-max},
) !default;
@mixin respond-to($breakpoint) {
@if #{map.has-key($breakpoints, $breakpoints)} {
@media screen and (min-width: #{map.get($breakpoints, $breakpoint)}) {
@content;
}
}
}
// index.scss
@import "vars.scss";
@import "code.css";
2、vitepress下新建index.ts文件,代码如下:
import 'normalize.css'
import VPDemo from './components/vp-demo/index.vue'
import './style/css-vars.scss'
import './style/index.scss'
export { VPDemo }
五、注册vp-demo组件,在theme文件中新增引入注册
import { VPDemo } from '../vitepress'
export default {
enhanceApp(ctx) {
.....
ctx.app.component('Demo', VPDemo)
},
}
六、在docs文件夹下新增examples文件夹(命名你可以随意——别忘记要与vp-example.vue文件引入一致)
七、在docs文件夹下新增vite.config.ts、tsconfig.json文件,解决examples文件<script setup lang="tsx">
报错
// vite.config.ts
import { defineConfig } from 'vite'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vueSetupExtend from 'vite-plugin-vue-setup-extend' // 设置neme属性
export default defineConfig({
plugins: [vueJsx(), vueSetupExtend()],
server: {
host: '0.0.0.0',
port: 2222,
open: true,
https: false,
},
})
// tsconfig.json
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": false,
"skipLibCheck": true,
"jsx": "preserve",
}
}
八、完成以上配置,那么在md文件中如下编写即可实现vue文件渲染及源码展示与隐藏(以TSelect组件为例)
1、examples下新建TSelect文件夹在新建single.vue文件,代码如下:
<template>
<t-layout-page>
<t-select
placeholder="请选择工序"
v-model="selectVlaue"
:optionSource="stepList"
valueKey="label"
@change="selectChange"
width="200px"
/>
</t-layout-page>
</template>
<script setup lang="ts" name="Single">
import { ref } from 'vue'
const selectVlaue = ref<any>()
const stepList = [
{ label: '开始' },
{ label: 'POSUI' },
{ label: '11' },
{ label: 'GX123' },
{ label: '烘干破碎' },
{ label: '车间仓库' },
{ label: 'ui3333' },
{ label: 'hhh333' },
]
const selectChange = (val: any) => {
console.log('selectChange', val, selectVlaue.value)
}
</script>
2、components下新建TSelect文件夹在新建base.md文件,代码如下:
# TSelect 下拉选择组件
### 单选
:::demo
TSelect/single
:::
3、页面效果如下:
源码地址
相关文章
vue3+ts基于Element-plus再次封装基础组件文档