Skip to content

开发自己的包裹容器

为了方便逻辑复用,现在插件把大分部逻辑都封装在了useDemoBox这个hooks里,如果需要,可以通过引入这个hooks包装自己文档风格的容器。

ts
import { useDemoBox } from 'vitepress-better-demo-plugin'

可以参考element-plus风格容器的实现:

vue
<script lang="ts" setup>
import type { VitepressDemoBoxProps } from '@/types'
import { ElCollapseTransition, ElDivider, ElIcon, ElMessage, ElRadio, ElRadioButton, ElRadioGroup, ElTooltip } from 'element-plus'
import { useDemoBox } from '@/shared/composables/useDemoBox'
import { COMPONENT_TYPE } from '@/shared/constant'
import { i18n } from '@/shared/locales/i18n'
import { useEpNameSpace } from '@/shared/utils/namespace'
import {
  CodeOpenIcon,
  CodeSandboxIcon,
  CopyIcon,
  FoldIcon,
  GithubIcon,
  GitlabIcon,
  StackblitzIcon,
} from './icons/index'

const props = withDefaults(defineProps<VitepressDemoBoxProps>(), {
  title: '标题',
  description: '描述内容',
  visible: true,
  select: COMPONENT_TYPE.VUE,
  order: 'vue,react,html',
  github: '',
  gitlab: '',
  htmlWriteWay: 'write',
  codeHighlights: '',
})

const emit = defineEmits(['mount'])

function onCopySuccess() {
  ElMessage.success(i18n.value.copySuccess)
}

const {
  stackblitz,
  codesandbox,
  isCodeFold,
  setCodeFold,
  type,
  tabs,
  currentFiles,
  activeFile,
  currentCode,
  currentCodeHtml,
  openGithub,
  openGitlab,
  clickCodeCopy,
  htmlContainerRef,
  reactContainerRef,
} = useDemoBox(props, emit, {
  onCopySuccess,
})

const ns = useEpNameSpace()
</script>

<template>
  <div :class="[ns.e('container')]">
    <!-- 预览区 -->
    <section class="vp-raw" :class="[ns.bem('preview')]" :style="{ background: props.background }">
      <slot v-if="type === 'vue'" name="vue" />
      <div v-else-if="type === 'html'" ref="htmlContainerRef">
        <iframe style="width: 100%; height: auto; border: none" />
      </div>
      <div v-else-if="type === 'react'" ref="reactContainerRef" />
    </section>
    <!-- 描述及切换 -->
    <section :class="[ns.bem('description')]">
      <ElDivider v-if="title" :class="[ns.bem('description', 'title')]" content-position="left">
        {{ title }}
      </ElDivider>
      <div
        v-if="description"
        :class="[ns.bem('description', 'content')]"
        v-html="description"
      />
      <div
        v-if="props.description || (!props.title && !props.description)"
        :class="[ns.bem('description', 'split-line')]"
      />
      <div v-if="tabs.length > 1 && visible" :class="[ns.bem('lang-tabs')]">
        <ElRadioGroup v-model="type">
          <ElRadio
            v-for="tab in tabs"
            :key="tab"
            :value="tab"
          >
            {{ tab }}
          </ElRadio>
        </ElRadioGroup>
      </div>
      <div :class="[ns.bem('description', 'handle-btn-op-bar')]">
        <ElTooltip v-if="stackblitz.show" :content="i18n.openInStackblitz">
          <ElIcon :class="ns.bem('description', 'handle-btn')">
            <StackblitzIcon
              :code="currentCode"
              :type="type"
              :scope="scope || ''"
              :templates="stackblitz.templates || []"
            />
          </ElIcon>
        </ElTooltip>
        <ElTooltip v-if="codesandbox.show" :content="i18n.openInCodeSandbox">
          <ElIcon :class="ns.bem('description', 'handle-btn')">
            <CodeSandboxIcon
              :code="currentCode"
              :type="type"
              :scope="scope || ''"
              :templates="codesandbox.templates || []"
            />
          </ElIcon>
        </ElTooltip>
        <ElTooltip v-if="github" :content="i18n.openInGithub">
          <ElIcon :class="ns.bem('description', 'handle-btn')">
            <GithubIcon @click="openGithub" />
          </ElIcon>
        </ElTooltip>
        <ElTooltip v-if="gitlab" :content="i18n.openInGitlab">
          <ElIcon :class="ns.bem('description', 'handle-btn')">
            <GitlabIcon @click="openGitlab" />
          </ElIcon>
        </ElTooltip>
        <ElTooltip :content="i18n.copyCode">
          <ElIcon :class="ns.bem('description', 'handle-btn')">
            <CopyIcon @click="clickCodeCopy" />
          </ElIcon>
        </ElTooltip>
        <ElTooltip :content="i18n.expandCode">
          <ElIcon :class="ns.bem('description', 'handle-btn')">
            <CodeOpenIcon @click="setCodeFold(!isCodeFold)" />
          </ElIcon>
        </ElTooltip>
      </div>
    </section>
    <!-- 代码展示区 -->
    <section :class="[ns.bem('source')]">
      <ElCollapseTransition>
        <div v-show="!isCodeFold">
          <div v-if="Object.keys(currentFiles).length" :class="[ns.bem('file-tabs')]">
            <ElRadioGroup v-model="activeFile">
              <ElRadioButton
                v-for="file in Object.keys(currentFiles)"
                :key="file"
                size="small"
                :value="file"
              >
                {{ file }}
              </ElRadioButton>
            </ElRadioGroup>
          </div>
          <div v-html="currentCodeHtml" />
        </div>
      </ElCollapseTransition>

      <Transition name="el-fade-in-linear">
        <div
          v-show="!isCodeFold"
          :class="[ns.bem('float-control')]"
          tabindex="0"
          role="button"
          @click="setCodeFold(!isCodeFold)"
        >
          <ElIcon :size="16">
            <FoldIcon />
          </ElIcon>
          <span>{{ i18n.collapseCode }}</span>
        </div>
      </Transition>
    </section>
  </div>
</template>

目前二次开发的逻辑还不算完善,部分类型或者值没有导出,可能需要自己实现,开发完成后可以参考多主题的章节注册使用自己的容器。

Released under the MIT License.