# 文档搭建

# VuePress

  • Vue 驱动的静态网站生成器。
  • 以 Markdown 为中心的项目结构,以最少的配置帮助你专注于写作。
  • 享受 Vue + webpack 的开发体验,可以在 Markdown 中使用 Vue 组件,又可以使用 Vue 来开发自定义主题。
  • VuePress 会为每个页面预渲染生成静态的 HTML,同时,每个页面被加载的时候,将作为 SPA 运行。

VuePress文档 (opens new window)

# 项目生成和配置

  1. 安装

    mkdir xqh-vue-ui-doc && cd xqh-vue-ui-doc
    npm init -y  // 创建package.json
    npm install vuepress -D // 安装vuepress框架
    
  2. 在package.json中配置scripts

    "scripts":{
        "docs:dev": "vuepress dev docs",
        "docs:build": "vuepress build docs",
    }
    
  3. 项目目录

    ├─docs
    │  │─.vuepress
    │  │   components # demo
    |  |   styles # 文档展示样式
    |  |   config.js # 配置
    |  |   enhanceApp.js 
    │  │  
    │  ├─components # 所有组件的文档
    |  |   button.md
    │  │       
    │  └─README.md
    |
    │  .gitignore # 忽略文件
    │  package-lock.json
    │  package.json
    │  README.md 
    
  4. 配置入口界面

    docs/README.md

    ---
    home: true // 是否首页
    actionText: 开始使用  // 首页文本
    actionLink: /components/installation
    features:
      - title: xqh-vue-ui
        details: 一套基于 Vue.js 的高质量 UI 组件库
      - title: 指南
        details: 了解设计指南,帮助产品设计人员搭建逻辑清晰、结构合理且高效易用的产品。
      - title: 组件demo
        details: xqh-vue-components 不定期更新~
    footer: MIT Licensed | COPYRIGHT © 2021-2022 懒鱼 版权所有
    ---
    
  5. 配置导航

    docs/.vuepress/config.js

    module.exports = {
      title: 'xqh-vue-ui', // 设置网站标题
      description: 'xqh-vue-ui 是一款轻量、好用的 Vue 组件库', // 描述
      // dest: './build', // 设置输出目录
      port: 1234, // 端口
      head: [['link', { rel: 'icon', href: 'logo.png' }]],  // 设置 logo
      themeConfig: { // 主题配置
        nav: [ // 头部导航条
          {
            text: '组件',
            link: '/components/installation'
          },
        ],
        // 为以下路由添加侧边栏
        sidebar: {
          '/components/': [
            {
              title: '快速上手',
              collapsable: false,
              children: ['/components/installation']
            },
            // 其他设置
          ]
        }
      }
    }
    
  6. 覆盖默认样式

    docs/.vuepress/styles/palette.styl

    // 颜色
    $accentColor = #ECC68B;
    $textColor = #4C506D;
    $borderColor = #E6E1D7;
    $codeBgColor = #fafafa;
    $arrowBgColor = #D4A46F;
    $badgeTipColor = #C1C19C;
    // $badgeWarningColor = darken(#ffe564, 35%)
    // $badgeErrorColor = #DA5961
    
    // 布局
    $navbarHeight = 4rem;
    $sidebarWidth = 15rem;
    $contentWidth = 900px;
    $homePageWidth = 1000px;
    
    .theme-default-content pre code, .theme-default-content pre[class*='language-'] code {
      color: #476582;
    }
    
  7. 应用级别的配置

    docs/.vuepress/enhanceApp.js

    • 下载插件和依赖:element-ui、highlight.js(代码高亮)、sass、sass-loader

    • link开发的组件库:这样就可以一边开发组件库,一边写文档啦,最终上线改为下载npm包并引入就可以了。

      # 进入xqh-vue-ui项目,创建链接对象
      npm link
      # 进入xqh-vue-ui-doc项目,建立xqh-vue-ui项目的链接
      npm link xqh-vue-ui
      # 解除链接
      npm unlink xqh-vue-ui
      

    enhanceApp.js文件作为入口(启动当前的vuepress,这个文件就是当前项目的一个入口),框架规定的,名字不能改。这个文件用于添加组件Demo展示的优化的配置。

    import Vue from 'vue';
    import ElementUI from 'element-ui'; // 全局引入element-ui
    import 'element-ui/lib/theme-chalk/index.css';
    
    import hljs from 'highlight.js'; // 代码高亮
    import 'highlight.js/styles/googlecode.css';
    
    import xqhVueUI from 'xqh-vue-ui' // 要编写对应的文档的包
    import 'xqh-vue-ui/dist/xqh-vue-ui.css'  // 引入组件库样式
    
    // 全局注册指令
    Vue.directive('highlight',function (el) {
      let blocks = el.querySelectorAll('pre code');
      blocks.forEach((block)=>{
        hljs.highlightBlock(block)
      })
    })
    export default ({
      Vue
    }) => {
      Vue.use(ElementUI);
      Vue.use(xqhVueUI)
    }
    

# 代码块

docs/.vuepress/components/demo-block.vue

像elementui那种查看例子时,可以展开代码的功能

<template>
  <div class="demo-block" :class="[blockClass, { 'hover': hovering }]" @mouseenter="hovering = true" @mouseleave="hovering = false">
    <div style="padding:24px">
      <ClientOnly>
        <slot name="source"></slot>
      </ClientOnly>
    </div>
    <div class="meta" ref="meta">
      <div class="description" v-if="$slots.default">
        <slot></slot>
      </div>
      <div class="highlight " v-highlight>
        <slot name="highlight"></slot>
      </div>
    </div>
    <div class="demo-block-control" ref="control" @click="isExpanded = !isExpanded">
      <transition name="arrow-slide">
        <i :class="[iconClass, { 'hovering': hovering }]"></i>
      </transition>
      <transition name="text-slide">
        <span v-show="hovering">{{ controlText }}</span>
      </transition>
    </div>
  </div>
</template>

<script type="text/babel">
export default {
  data () {
    return {
      hovering: false,
      isExpanded: false,
      fixedControl: false,
      scrollParent: null,
      langConfig: {
        "hide-text": "隐藏代码",
        "show-text": "显示代码",
        "button-text": "在线运行",
        "tooltip-text": "前往 jsfiddle.net 运行此示例"
      }
    };
  },
  mounted () {
    console.log(1)
    console.log(this.$slots)
  },
  props: {
    jsfiddle: Object,
    default () {
      return {};
    }
  },
  methods: {
    scrollHandler () {
      const { top, bottom, left } = this.$refs.meta.getBoundingClientRect();
      this.fixedControl = bottom > document.documentElement.clientHeight &&
        top + 44 <= document.documentElement.clientHeight;
    },
    removeScrollHandler () {
      this.scrollParent && this.scrollParent.removeEventListener('scroll', this.scrollHandler);
    }
  },
  computed: {
    lang () {
      return this.$route.path.split('/')[1];
    },
    blockClass () {
      return `demo-${this.lang} demo-${this.$router.currentRoute.path.split('/').pop()}`;
    },
    iconClass () {
      return this.isExpanded ? 'el-icon-caret-top' : 'el-icon-caret-bottom';
    },
    controlText () {
      return this.isExpanded ? this.langConfig['hide-text'] : this.langConfig['show-text'];
    },
    codeArea () {
      return this.$el.getElementsByClassName('meta')[0];
    },
    codeAreaHeight () {
      if (this.$el.getElementsByClassName('description').length > 0) {
        return this.$el.getElementsByClassName('description')[0].clientHeight +
          this.$el.getElementsByClassName('highlight')[0].clientHeight + 20;
      }
      return this.$el.getElementsByClassName('highlight')[0].clientHeight;
    }
  },
  watch: {
    isExpanded (val) {
      this.codeArea.style.height = val ? `${this.codeAreaHeight + 1}px` : '0';
      if (!val) {
        this.fixedControl = false;
        this.$refs.control.style.left = '0';
        this.removeScrollHandler();
        return;
      }
      setTimeout(() => {
        this.scrollParent = document.querySelector('.page-component__scroll > .el-scrollbar__wrap');
        this.scrollParent && this.scrollParent.addEventListener('scroll', this.scrollHandler);
        this.scrollHandler();
      }, 200);
    }
  },
  mounted () {
    this.$nextTick(() => {
      let highlight = this.$el.getElementsByClassName('highlight')[0];
      if (this.$el.getElementsByClassName('description').length === 0) {
        highlight.style.width = '100%';
        highlight.borderRight = 'none';
      }
    });
  },
  beforeDestroy () {
    this.removeScrollHandler();
  }
};
</script>

<style lang="scss">
.demo-block {
  border: solid 1px #ebebeb;
  border-radius: 3px;
  transition: 0.2s;
  &.hover {
    box-shadow: 0 0 8px 0 rgba(232, 237, 250, 0.6),
      0 2px 4px 0 rgba(232, 237, 250, 0.5);
  }
  code {
    font-family: Menlo, Monaco, Consolas, Courier, monospace;
  }
  .demo-button {
    float: right;
  }
  .source {
    padding: 24px;
  }
  .meta {
    background-color: #fafafa;
    border-top: solid 1px #eaeefb;
    overflow: hidden;
    height: 0;
    transition: height 0.2s;
  }
  .description {
    padding: 20px;
    box-sizing: border-box;
    border: solid 1px #ebebeb;
    border-radius: 3px;
    font-size: 14px;
    line-height: 22px;
    color: #666;
    word-break: break-word;
    margin: 10px;
    background-color: #fff;
    p {
      margin: 0;
      line-height: 26px;
    }
    code {
      color: #5e6d82;
      background-color: #e6effb;
      margin: 0 4px;
      display: inline-block;
      padding: 1px 5px;
      font-size: 12px;
      border-radius: 3px;
      height: 18px;
      line-height: 18px;
    }
  }
  .highlight {
    pre {
      margin: 0;
    }
    code.hljs {
      margin: 0;
      border: none;
      max-height: none;
      border-radius: 0;
      line-height: 1.8;
      color: black;
      &::before {
        content: none;
      }
    }
  }
  .demo-block-control {
    border-top: solid 1px #eaeefb;
    height: 44px;
    box-sizing: border-box;
    background-color: #fff;
    border-bottom-left-radius: 4px;
    border-bottom-right-radius: 4px;
    text-align: center;
    margin-top: -1px;
    color: #d3dce6;
    cursor: pointer;
    position: relative;
    &.is-fixed {
      position: fixed;
      bottom: 0;
      width: 868px;
    }
    i {
      font-size: 16px;
      line-height: 44px;
      transition: 0.3s;
      &.hovering {
        transform: translateX(-40px);
      }
    }
    > span {
      position: absolute;
      transform: translateX(-30px);
      font-size: 14px;
      line-height: 44px;
      transition: 0.3s;
      display: inline-block;
    }
    &:hover {
      color: #409eff;
      background-color: #f9fafc;
    }
    & .text-slide-enter,
    & .text-slide-leave-active {
      opacity: 0;
      transform: translateX(10px);
    }
    .control-button {
      line-height: 26px;
      position: absolute;
      top: 0;
      right: 0;
      font-size: 14px;
      padding-left: 5px;
      padding-right: 25px;
    }
  }
}
</style>

# 编写对应组件Demo和md文档

以button组件为例

docs/.vuepress/components/button/basic.vue

  • 文档中就可以用<button-basic></button-basic>组件了
<template>
  <div class="button-basic">
    <x-space>
      <x-button type="default">Default</x-button>
      <x-button type="primary">Primary</x-button>
      <x-button type="link">Link</x-button>
      <x-button type="ghost">Ghost</x-button>
      <x-button type="delicate">Delicate</x-button>
    </x-space>
    <x-space>
      <x-button type="default" status="success">success</x-button>
      <x-button type="default" status="info">info</x-button>
      <x-button type="default" status="warning">warning</x-button>
      <x-button type="default" status="danger">danger</x-button>
    </x-space>
    <x-space>
      <x-button type="primary" status="success">success</x-button>
      <x-button type="primary" status="info">info</x-button>
      <x-button type="primary" status="warning">warning</x-button>
      <x-button type="primary" status="danger">danger</x-button>
    </x-space>
    <x-space>
      <x-button type="primary" shape="square">square</x-button>
      <x-button type="primary" shape="square" icon="search"></x-button>
      <x-button type="primary" shape="round">round</x-button>
      <x-button type="primary" shape="circle" icon="edit"></x-button>
    </x-space>
  </div>
</template>

docs/components/button.md

  • 包括组件介绍、demo引用、参数介绍、事件介绍
# Button 按钮

网页常用 button 按钮,常用于响应一个事件或处理某个逻辑。

### 基础用法

<br/>

<demo-block>
:::slot source
<button-basic></button-basic>
:::

使用`type``status``shape`属性来定义 Button 的样式。

:::slot highlight

```html
<x-space>
  <x-button type="default">Default</x-button>
  <x-button type="primary">Primary</x-button>
  <x-button type="link">Link</x-button>
  <x-button type="ghost">Ghost</x-button>
  <x-button type="delicate">Delicate</x-button>
</x-space>
<x-space>
  <x-button type="default" status="success">success</x-button>
  <x-button type="default" status="info">info</x-button>
  <x-button type="default" status="warning">warning</x-button>
  <x-button type="default" status="danger">danger</x-button>
</x-space>
<x-space>
  <x-button type="primary" status="success">success</x-button>
  <x-button type="primary" status="info">info</x-button>
  <x-button type="primary" status="warning">warning</x-button>
  <x-button type="primary" status="danger">danger</x-button>
</x-space>
<x-space>
  <x-button type="primary" shape="square">square</x-button>
  <x-button type="primary" shape="square" icon="search"></x-button>
  <x-button type="primary" shape="round">round</x-button>
  <x-button type="primary" shape="circle" icon="edit"></x-button>
</x-space>
```

:::
</demo-block>

### Attributes 参数

| 参数         | 说明           | 类型    | 可选值                                      | 默认值  |
| :----------- | :------------- | :------ | :------------------------------------------ | :------ |
| type         | 按钮类型       | String  | default / primary / link / ghost / delicate | default |
| status       | 按钮状态       | String  | success / info / warning / danger           | default |
| shape        | 按钮形状       | String  | square / round / circle                     | square  |
| size         | 按钮尺寸       | String  | xs / ms / md / lg                           | md      |
| icon         | 图标按钮       | String  | 和图标的值一样                              ||
| iconPosition | 图标位置       | String  | left / right                                | left    |
| loading      | 是否为加载状态 | Boolean || false   |
| disabled     | 是否为禁用状态 | Boolean || false   |

### Event 事件

| 事件名 | 说明             | 回调参数       |
| :----- | :--------------- | :------------- |
| click  | 按钮在点击时触发 | (event: Event) |

运行npm run docs:dev,访问 http://localhost:1234 (opens new window) 就可以看到文档了。