Commit caa7ba030ad86973b7f846fe8539a9c3af38f066
1 parent
15c8e131
Exists in
master
and in
1 other branch
组件布局增加右侧锚点导航
Showing
8 changed files
with
119 additions
and
27 deletions
Show diff stats
examples/views/docs/detail.md
examples/views/docs/form.md
examples/views/docs/scheme.md
examples/views/docs/search.md
examples/views/docs/select.md
examples/views/docs/table.md
examples/views/layout/component.vue
| 1 | 1 | <template> |
| 2 | - <div id="app"> | |
| 3 | - <el-container> | |
| 4 | - <layout-header></layout-header> | |
| 5 | - <el-container class="layout-container__component"> | |
| 6 | - <el-aside class="layout-aside__component" width="200px"> | |
| 7 | - <el-menu :default-active="activeMenu" class="layout-aside-menu__component" @select="handleSelect"> | |
| 8 | - <h4 style="padding: 0 20px;">组件</h4> | |
| 9 | - <el-menu-item-group v-for="(component, idx) in components" :key="idx"> | |
| 10 | - <template slot="title">{{ component.group }}</template> | |
| 11 | - <el-menu-item v-for="(data, index) in component.children" :key="index" :index="data.name">{{ data.meta.title }}</el-menu-item> | |
| 12 | - </el-menu-item-group> | |
| 13 | - </el-menu> | |
| 14 | - </el-aside> | |
| 15 | - <el-main class="layout-main__component"> | |
| 16 | - <router-view></router-view> | |
| 17 | - </el-main> | |
| 18 | - </el-container> | |
| 2 | + <el-container> | |
| 3 | + <layout-header></layout-header> | |
| 4 | + <el-container class="layout-container__component"> | |
| 5 | + <el-aside class="layout-aside__component" width="200px"> | |
| 6 | + <el-menu :default-active="activeMenu" class="layout-aside-menu__component" @select="handleSelect"> | |
| 7 | + <h4 style="padding: 0 20px;">组件</h4> | |
| 8 | + <el-menu-item-group v-for="(component, idx) in componentList" :key="idx"> | |
| 9 | + <template slot="title">{{ component.group }}</template> | |
| 10 | + <el-menu-item v-for="(data, index) in component.children" :key="index" :index="data.name">{{ data.meta.title }}</el-menu-item> | |
| 11 | + </el-menu-item-group> | |
| 12 | + </el-menu> | |
| 13 | + </el-aside> | |
| 14 | + <a class="target-fix"></a> | |
| 15 | + <el-main class="layout-main__component"> | |
| 16 | + <router-view></router-view> | |
| 17 | + </el-main> | |
| 18 | + <el-aside class="layout-aside__preview" width="200px"> | |
| 19 | + <a class="anchor" :class="{ 'active': item.hash === currentAnchor }" v-for="(item, index) in anchorList" :key="index" :href="item.hash">{{ item.text }}</a> | |
| 20 | + </el-aside> | |
| 19 | 21 | </el-container> |
| 20 | - </div> | |
| 22 | + </el-container> | |
| 21 | 23 | </template> |
| 22 | 24 | |
| 23 | 25 | <script> |
| ... | ... | @@ -30,14 +32,74 @@ export default { |
| 30 | 32 | data() { |
| 31 | 33 | return { |
| 32 | 34 | activeMenu: 'select', |
| 33 | - components | |
| 35 | + componentList: components, | |
| 36 | + anchorList: [], | |
| 37 | + currentAnchor: '', | |
| 34 | 38 | } |
| 35 | 39 | }, |
| 36 | 40 | created() { |
| 37 | 41 | const { name } = this.$route || {}; |
| 38 | 42 | this.activeMenu = name; |
| 39 | 43 | }, |
| 44 | + mounted() { | |
| 45 | + this.initAnchorList(); | |
| 46 | + window.onscroll = ((e) => { | |
| 47 | + if (!this.isBottom()) { | |
| 48 | + for (let index in this.anchorList) { | |
| 49 | + let anchor = this.anchorList[index] || {}; | |
| 50 | + if (window.pageYOffset <= anchor.offsetTop - 65) { | |
| 51 | + this.currentAnchor = (anchor || this.anchorList[0]).hash; | |
| 52 | + break; | |
| 53 | + } | |
| 54 | + } | |
| 55 | + } | |
| 56 | + }); | |
| 57 | + }, | |
| 58 | + watch:{ | |
| 59 | + $route(to, from) { | |
| 60 | + this.currentAnchor = to.hash; | |
| 61 | + if (to.path !== from.path) { | |
| 62 | + this.$nextTick(this.initAnchorList); | |
| 63 | + } | |
| 64 | + if (!this.isBottom()) { | |
| 65 | + setTimeout(() => { | |
| 66 | + window.scrollBy(0, -65); | |
| 67 | + }, 1); | |
| 68 | + } | |
| 69 | + } | |
| 70 | + }, | |
| 40 | 71 | methods: { |
| 72 | + isBottom() { | |
| 73 | + // 变量scrollTop是滚动条滚动时,距离顶部的距离 | |
| 74 | + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; | |
| 75 | + // 变量windowHeight是可视区的高度 | |
| 76 | + const windowHeight = document.documentElement.clientHeight || document.body.clientHeight; | |
| 77 | + // 变量scrollHeight是滚动条的总高度 | |
| 78 | + const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight; | |
| 79 | + // 滚动条到底部的条件 | |
| 80 | + return scrollTop + windowHeight === scrollHeight; | |
| 81 | + }, | |
| 82 | + initAnchorList() { | |
| 83 | + this.anchorList = []; | |
| 84 | + const domList = document.querySelectorAll('.header-anchor'); | |
| 85 | + let anchorList = []; | |
| 86 | + for (let index in domList) { | |
| 87 | + let dom = domList[index] || {}; | |
| 88 | + const text = `${dom.parentNode.innerHTML}`.replace(/<\/?.*[^>]*>/g, '').trim(); | |
| 89 | + if (text === 'API') { | |
| 90 | + break; | |
| 91 | + } else { | |
| 92 | + anchorList.push({ | |
| 93 | + text: `${dom.parentNode.innerHTML}`.replace(/<\/?.*[^>]*>/g, '').trim(), | |
| 94 | + href: dom.href, | |
| 95 | + hash: dom.hash, | |
| 96 | + offsetTop: dom.offsetTop, | |
| 97 | + }); | |
| 98 | + } | |
| 99 | + } | |
| 100 | + this.anchorList = anchorList; | |
| 101 | + this.currentAnchor = anchorList[0].hash; | |
| 102 | + }, | |
| 41 | 103 | handleSelect(key) { |
| 42 | 104 | this.$router.push({ name: key }); |
| 43 | 105 | } |
| ... | ... | @@ -61,6 +123,7 @@ export default { |
| 61 | 123 | height: 90%; |
| 62 | 124 | border-right: 1px solid #e6e6e6; |
| 63 | 125 | } |
| 126 | + left: 0; | |
| 64 | 127 | position: fixed; |
| 65 | 128 | height: calc(100vh - 60px); |
| 66 | 129 | background: #fff; |
| ... | ... | @@ -80,8 +143,30 @@ export default { |
| 80 | 143 | border-right: 0; |
| 81 | 144 | } |
| 82 | 145 | } |
| 146 | + .layout-aside__preview { | |
| 147 | + position: fixed; | |
| 148 | + right: 0; | |
| 149 | + height: calc(100vh - 60px); | |
| 150 | + width: 150px !important; | |
| 151 | + padding: 30px 0; | |
| 152 | + .anchor { | |
| 153 | + width: 100%; | |
| 154 | + display: inline-block; | |
| 155 | + font-size: 12px; | |
| 156 | + text-decoration: none; | |
| 157 | + padding: 5px 20px; | |
| 158 | + color: $text; | |
| 159 | + transition: all 300ms; | |
| 160 | + border-left: 1px solid $border; | |
| 161 | + } | |
| 162 | + .active { | |
| 163 | + color: $primary; | |
| 164 | + border-left: 1px solid $primary; | |
| 165 | + } | |
| 166 | + } | |
| 83 | 167 | .layout-main__component { |
| 84 | 168 | margin-left: 200px; |
| 169 | + margin-right: 150px; | |
| 85 | 170 | padding: 20px 40px; |
| 86 | 171 | } |
| 87 | 172 | } | ... | ... |
examples/views/layout/default.vue
| 1 | 1 | <template> |
| 2 | - <div id="app"> | |
| 3 | - <el-container> | |
| 4 | - <layout-header></layout-header> | |
| 5 | - <el-container class="layout-container__default"> | |
| 6 | - <router-view></router-view> | |
| 7 | - </el-container> | |
| 2 | + <el-container> | |
| 3 | + <layout-header></layout-header> | |
| 4 | + <el-container class="layout-container__default"> | |
| 5 | + <router-view></router-view> | |
| 8 | 6 | </el-container> |
| 9 | - </div> | |
| 7 | + </el-container> | |
| 10 | 8 | </template> |
| 11 | 9 | |
| 12 | 10 | <script> | ... | ... |