Merge branch 'feature/scroll-top' into develop

Former-commit-id: 20513f5973c4f750679950d78476b393ce5125a1 [formerly 20513f5973c4f750679950d78476b393ce5125a1 [formerly 20513f5973c4f750679950d78476b393ce5125a1 [formerly 20513f5973c4f750679950d78476b393ce5125a1 [formerly 42d4c1cec6aeb5029b085e9a70e43baefea1b650 [formerly 790a794420a94f5b7fe72da206d69fdacb1680d4]]]]]
Former-commit-id: 592385d117bb1763fb619679159bce950458c15a
Former-commit-id: 3638259ef4ef1eeaf0c0717169b3351ea70cd2f3
Former-commit-id: 794dceb777ded5c48f03b3d22db5bac3a398cf90 [formerly b0612da147126bacf5ed70e6807c41c976710b54]
Former-commit-id: da6449c8921c4412ed54ccff528f50b305af7f0e
Former-commit-id: 93bea1350ea2e53cb6cd6ca1eea192f595a49029
Former-commit-id: 1c0112918013c8ca0c2efcd8e7e1c7bc7df009f1
Former-commit-id: f53d0e033a304c030014c1f46aa7701a7e902676
Former-commit-id: 555a79be517f2dabd41361fdd5ed746cbe46b201
This commit is contained in:
liyang 2018-11-16 15:59:56 +08:00
commit 2a614a44b6
16 changed files with 499 additions and 114 deletions

View File

@ -130,8 +130,6 @@
D2Admin 是完全开源免费的项目,旨在帮助开发者更方便地进行管理系统开发,同时也提供 2000 人的 QQ 交流群和微信群,前后端的朋友可以相互答疑,项目组成员全部在内,所有 D2 相关项目使用问题欢迎在群内提问。
> 隐性福利:重要通知会发群红包
![join](https://raw.githubusercontent.com/FairyEver/d2-admin/master/doc/image/join@2x.png)
## License

View File

@ -3,7 +3,7 @@
<div v-if="$slots.header" class="d2-container-card__header" ref="header">
<slot name="header"/>
</div>
<div class="d2-container-card__body">
<div class="d2-container-card__body" ref="body">
<div class="d2-container-card__body-card">
<slot/>
</div>
@ -15,7 +15,19 @@
</template>
<script>
import scroll from './mixins/normal'
export default {
name: 'd2-container-card'
name: 'd2-container-card',
mixins: [
scroll
],
mounted () {
//
this.addScrollListener()
},
beforeDestroy () {
//
this.removeScrollListener()
}
}
</script>

View File

@ -3,7 +3,7 @@
<div v-if="$slots.header" class="d2-container-full__header" ref="header">
<slot name="header"/>
</div>
<div class="d2-container-full__body">
<div class="d2-container-full__body" ref="body">
<slot/>
</div>
<div v-if="$slots.footer" class="d2-container-full__footer" ref="footer">
@ -13,7 +13,19 @@
</template>
<script>
import scroll from './mixins/normal'
export default {
name: 'd2-container-full'
name: 'd2-container-full',
mixins: [
scroll
],
mounted () {
//
this.addScrollListener()
},
beforeDestroy () {
//
this.removeScrollListener()
}
}
</script>

View File

@ -3,7 +3,7 @@
<div v-if="$slots.header" class="d2-container-ghost__header" ref="header">
<slot name="header"/>
</div>
<div class="d2-container-ghost__body">
<div class="d2-container-ghost__body" ref="body">
<slot/>
</div>
<div v-if="$slots.footer" class="d2-container-ghost__footer" ref="footer">
@ -11,3 +11,21 @@
</div>
</div>
</template>
<script>
import scroll from './mixins/normal'
export default {
name: 'd2-container-ghost',
mixins: [
scroll
],
mounted () {
//
this.addScrollListener()
},
beforeDestroy () {
//
this.removeScrollListener()
}
}
</script>

View File

@ -0,0 +1,74 @@
<template>
<div
class="d2-source"
:class="{ 'd2-source--active': isActive }"
@click="handleClick">
<d2-icon name="code"/> SourceCode
</div>
</template>
<script>
export default {
props: {
//
filename: {
type: String,
default: ''
}
},
data () {
return {
isActive: false
}
},
mounted () {
//
setTimeout(() => {
this.isActive = true
}, 1000)
},
methods: {
//
handleClick () {
const file = this.filename.split('?')[0]
const url = file
? `https://github.com/d2-projects/d2-admin/blob/master/${file}`
: 'https://github.com/d2-projects/d2-admin'
this.$open(url)
}
}
}
</script>
<style lang="scss" scoped>
@import '~@/assets/style/public.scss';
.d2-source {
$borderRadius: 4px;
$paddingLR: 15px;
$paddingTB: 7px;
$fontSize: 12px;
$rightOuter: $paddingLR / 2;
opacity: 0;
position: absolute;
right: - $borderRadius - $rightOuter;
bottom: 20px;
font-size: $fontSize;
line-height: $fontSize;
font-weight: bold;
border-radius: $borderRadius;
padding: $paddingTB $paddingLR;
padding-right: $borderRadius + $paddingLR;
background-color: rgba(#000, .7);
border: 1px solid #000;
color: #FFF;
transition: all .3s;
@extend %unable-select;
&.d2-source--active {
opacity: 1;
}
&:hover {
right: - $borderRadius;
background-color: rgba(#000, .9);
}
}
</style>

View File

@ -1,6 +1,7 @@
import BScroll from 'better-scroll'
export default {
props: {
// 滚动优化的选项
betterScrollOptions: {
type: Object,
required: false,
@ -20,6 +21,7 @@ export default {
},
methods: {
scrollInit () {
// 初始化 bs
this.BS = new BScroll(this.$refs.wrapper, Object.assign({
mouseWheel: true,
scrollbar: {
@ -27,6 +29,11 @@ export default {
interactive: false
}
}, this.betterScrollOptions))
// 滚动时发出事件 并且统一返回的数据格式
this.BS.on('scroll', ({x, y}) => this.$emit('scroll', {
x: -x,
y: -y
}))
},
scrollDestroy () {
// https://github.com/d2-projects/d2-admin/issues/75
@ -36,6 +43,19 @@ export default {
delete this.BS
this.BS = null
}
},
// 外部调用的方法 返回顶部
scrollToTop () {
if (this.BS) this.BS.scrollTo(0, 0, 300)
},
// 手动发出滚动事件
scroll () {
if (this.BS) {
this.$emit('scroll', {
x: -this.BS.x,
y: -this.BS.y
})
}
}
}
}

View File

@ -0,0 +1,67 @@
// 提供滚动方面的功能
// 非滚动优化模式通用
import { throttle } from 'lodash'
// 生成滚动事件的 handler
function handleMaker (wait) {
return throttle(e => {
this.$emit('scroll', {
x: e.target.scrollLeft,
y: e.target.scrollTop
})
}, wait)
}
export default {
props: {
// 滚动事件节流间隔
scrollDelay: {
type: Number,
required: false,
default: 10
}
},
data () {
return {
handleScroll: null
}
},
watch: {
scrollDelay (val) {
// 移除旧的监听
this.removeScrollListener()
// 生成新的 handle 方法
this.handleScroll = handleMaker.call(this, val)
// 添加新的监听
this.addScrollListener()
}
},
methods: {
// 增加滚动事件监听
addScrollListener () {
if (typeof this.handleScroll !== 'function') {
// mounted 生命周期内调用这个方法的时候会进入这里的判断
this.handleScroll = handleMaker.call(this, this.scrollDelay)
}
// 添加监听
this.$refs.body.addEventListener('scroll', this.handleScroll)
},
// 移除滚动事件监听
removeScrollListener () {
this.$refs.body.removeEventListener('scroll', this.handleScroll)
},
// 外部调用的方法 返回顶部
scrollToTop () {
const smoothscroll = () => {
const body = this.$refs.body
const currentScroll = body.scrollTop
if (currentScroll > 0) {
window.requestAnimationFrame(smoothscroll)
body.scrollTo(0, currentScroll - (currentScroll / 5))
}
}
smoothscroll()
}
}
}

View File

@ -0,0 +1,111 @@
// 组件
import d2ContainerFull from './components/d2-container-full.vue'
import d2ContainerFullBs from './components/d2-container-full-bs.vue'
import d2ContainerGhost from './components/d2-container-ghost.vue'
import d2ContainerGhostBs from './components/d2-container-ghost-bs.vue'
import d2ContainerCard from './components/d2-container-card.vue'
import d2ContainerCardBs from './components/d2-container-card-bs.vue'
import d2Source from './components/d2-source.vue'
export default {
name: 'd2-container',
props: {
// 容器样式
type: {
type: String,
required: false,
default: 'full'
},
// 滚动优化
betterScroll: {
type: Boolean,
required: false,
default: false
}
},
computed: {
// 始终返回渲染组件
component () {
if (this.type === 'card' && !this.betterScroll) return d2ContainerCard
if (this.type === 'card' && this.betterScroll) return d2ContainerCardBs
if (this.type === 'ghost' && !this.betterScroll) return d2ContainerGhost
if (this.type === 'ghost' && this.betterScroll) return d2ContainerGhostBs
if (this.type === 'full' && !this.betterScroll) return d2ContainerFull
if (this.type === 'full' && this.betterScroll) return d2ContainerFullBs
else {
return 'div'
}
}
},
render (h) {
const slots = [
h('div', this.$slots.default)
]
if (this.$slots.header) slots.push(h('div', { slot: 'header' }, [ this.$slots.header ]))
if (this.$slots.footer) slots.push(h('div', { slot: 'footer' }, [ this.$slots.footer ]))
return h('div', {
ref: 'container',
class: 'container-component'
}, [
h(this.component, {
ref: 'component',
props: this.$attrs,
on: {
scroll: e => this.$emit('scroll', e)
}
}, slots),
// 只在预览和开发模式下显示源码按钮
process.env.VUE_APP_BUILD_MODE === 'TRAVIS' || process.env.NODE_ENV === 'development' ? h(d2Source, {
props: this.$attrs
}) : undefined
])
},
methods: {
// 返回顶部
scrollToTop () {
this.$refs.component.scrollToTop()
// 如果开启了 better scroll 还需要手动触发一遍 scroll 事件
const bs = this.$refs.component.BS
if (bs) this.$refs.component.scroll()
},
// 用法同原生方法 scrollBy
scrollBy (x = 0, y = 0, time = 300) {
if (this.betterScroll) {
const bs = this.$refs.component.BS
if (bs) {
bs.scrollBy(-x, -y, time)
// 手动触发一遍 scroll 事件
this.$refs.component.scroll()
}
} else {
this.$refs.component.$refs.body.scrollBy(x, y)
}
},
// 用法同原生方法 scrollTo
scrollTo (x = 0, y = 0, time = 300) {
if (this.betterScroll) {
const bs = this.$refs.component.BS
if (bs) {
bs.scrollTo(-x, -y, time)
// 手动触发一遍 scroll 事件
this.$refs.component.scroll()
}
} else {
this.$refs.component.$refs.body.scrollTo(x, y)
}
},
// 用法同原生方法 scrollTop
scrollTop (top = 0, time = 300) {
if (this.betterScroll) {
const bs = this.$refs.component.BS
if (bs) {
bs.scrollTo(bs.x, -top, time)
// 手动触发一遍 scroll 事件
this.$refs.component.scroll()
}
} else {
this.$refs.component.$refs.body.scrollTop = top
}
}
}
}

View File

@ -1,75 +0,0 @@
<template>
<div class="container-component" ref="container">
<!-- [card] 卡片容器 -->
<d2-container-card v-if="type === 'card' && !betterScroll">
<slot v-if="$slots.header" name="header" slot="header"/>
<slot/>
<slot v-if="$slots.footer" name="footer" slot="footer"/>
</d2-container-card>
<!-- [card] 卡片容器 滚动优化 -->
<d2-container-card-bs v-bind="$attrs" v-if="type === 'card' && betterScroll">
<slot v-if="$slots.header" name="header" slot="header"/>
<slot/>
<slot v-if="$slots.footer" name="footer" slot="footer"/>
</d2-container-card-bs>
<!-- [ghost] 隐形容器 -->
<d2-container-ghost v-if="type === 'ghost' && !betterScroll">
<slot v-if="$slots.header" name="header" slot="header"/>
<slot/>
<slot v-if="$slots.footer" name="footer" slot="footer"/>
</d2-container-ghost>
<!-- [ghost] 隐形容器 滚动优化 -->
<d2-container-ghost-bs v-bind="$attrs" v-if="type === 'ghost' && betterScroll">
<slot v-if="$slots.header" name="header" slot="header"/>
<slot/>
<slot v-if="$slots.footer" name="footer" slot="footer"/>
</d2-container-ghost-bs>
<!-- [container-full] 填充 -->
<d2-container-full v-if="type === 'full' && !betterScroll">
<slot v-if="$slots.header" name="header" slot="header"/>
<slot/>
<slot v-if="$slots.footer" name="footer" slot="footer"/>
</d2-container-full>
<!-- [container-full-bs] 填充 滚动优化 -->
<d2-container-full-bs v-bind="$attrs" v-if="type === 'full' && betterScroll">
<slot v-if="$slots.header" name="header" slot="header"/>
<slot/>
<slot v-if="$slots.footer" name="footer" slot="footer"/>
</d2-container-full-bs>
</div>
</template>
<script>
//
import d2ContainerFull from './components/d2-container-full.vue'
import d2ContainerFullBs from './components/d2-container-full-bs.vue'
import d2ContainerGhost from './components/d2-container-ghost.vue'
import d2ContainerGhostBs from './components/d2-container-ghost-bs.vue'
import d2ContainerCard from './components/d2-container-card.vue'
import d2ContainerCardBs from './components/d2-container-card-bs.vue'
export default {
name: 'd2-container',
components: {
'd2-container-full': d2ContainerFull,
'd2-container-full-bs': d2ContainerFullBs,
'd2-container-ghost': d2ContainerGhost,
'd2-container-ghost-bs': d2ContainerGhostBs,
'd2-container-card': d2ContainerCard,
'd2-container-card-bs': d2ContainerCardBs
},
props: {
//
type: {
type: String,
required: false,
default: 'full'
},
//
betterScroll: {
type: Boolean,
required: false,
default: false
}
}
}
</script>

View File

@ -9,15 +9,37 @@ export default {
title: '布局容器',
icon: 'window-restore',
children: [
{ path: `${pre}container/full`, title: '填充' },
{ path: `${pre}container/full-slot`, title: '填充 插槽' },
{ path: `${pre}container/full-bs`, title: '填充 滚动优化' },
{ path: `${pre}container/ghost`, title: '隐形' },
{ path: `${pre}container/ghost-slot`, title: '隐形 插槽' },
{ path: `${pre}container/ghost-bs`, title: '隐形 滚动优化' },
{ path: `${pre}container/card`, title: '卡片' },
{ path: `${pre}container/card-slot`, title: '卡片 插槽' },
{ path: `${pre}container/card-bs`, title: '卡片 滚动优化' }
{
title: '填充型',
children: [
{ path: `${pre}container/full`, title: '基础' },
{ path: `${pre}container/full-slot`, title: '插槽' },
{ path: `${pre}container/full-bs`, title: '滚动优化' }
]
},
{
title: '隐形模式',
children: [
{ path: `${pre}container/ghost`, title: '基础' },
{ path: `${pre}container/ghost-slot`, title: '插槽' },
{ path: `${pre}container/ghost-bs`, title: '滚动优化' }
]
},
{
title: '卡片型',
children: [
{ path: `${pre}container/card`, title: '基础' },
{ path: `${pre}container/card-slot`, title: '插槽' },
{ path: `${pre}container/card-bs`, title: '滚动优化' }
]
},
{
title: '方法',
children: [
{ path: `${pre}container/api?bs=false`, title: '滚动控制' },
{ path: `${pre}container/api?bs=true`, title: '滚动控制 BS' }
]
}
]
},
{

View File

@ -0,0 +1,105 @@
<template>
<d2-container
ref="container"
:type="containerType"
:better-scroll="betterScroll"
:scroll-delay="scrollDelay"
@scroll="({x, y}) => { scrollTop = y }">
<template slot="header">
<el-form
:inline="true"
size="mini">
<el-form-item
label="布局类型"
class="d2-mb-0">
<el-radio-group v-model="containerType">
<el-radio-button label="full"></el-radio-button>
<el-radio-button label="card"></el-radio-button>
<el-radio-button label="ghost"></el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item
label="滚动距离"
class="d2-mb-0">
<el-input
:value="scrollTop"
style="width: 130px;">
<template slot="append">px</template>
</el-input>
</el-form-item>
<el-form-item
v-if="!betterScroll"
label="事件延迟(ms)"
class="d2-mb-0">
<el-input-number
v-model="scrollDelay"
:min="10"
:max="2000"
:step="100"
style="width: 110px;"/>
</el-form-item>
<el-form-item class="d2-mb-0">
<el-button
v-if="scrollTop >= 55"
type="primary"
@click="$refs.container.scrollToTop">
回到顶部
</el-button>
</el-form-item>
</el-form>
</template>
<el-alert
type="success"
:title="`${betterScroll ? '此示例开启了 BetterScroll ' : ''}请向下滚动`"
class="d2-mb-10"
center/>
<d2-demo-article
v-for="i in 10"
:key="i"
:style="articleStyle"
long/>
<template slot="footer">
<el-form
:inline="true"
size="mini">
<el-form-item class="d2-mb-0">
<el-button @click="$refs.container.scrollBy(0, 30)">相对滚动 (0, 30) 像素</el-button>
</el-form-item>
<el-form-item class="d2-mb-0">
<el-button @click="$refs.container.scrollTo(0, 100)">滚动到 (0, 100) 像素位置</el-button>
</el-form-item>
<el-form-item class="d2-mb-0">
<el-button @click="$refs.container.scrollTop(100)">滚动到垂直位置 100</el-button>
</el-form-item>
</el-form>
</template>
</d2-container>
</template>
<script>
import d2DemoArticle from './components/d2-demo-article'
export default {
components: {
'd2-demo-article': d2DemoArticle
},
data () {
return {
containerType: 'full',
scrollDelay: 10,
scrollTop: 0
}
},
computed: {
// better scroll
betterScroll () {
return this.$route.query.bs === 'true'
},
//
articleStyle () {
return {
opacity: this.scrollTop > 55 ? '1' : '.1'
}
}
}
}
</script>

View File

@ -1,37 +1,50 @@
<template>
<div>
<div class="d2-demo-article-control">
<div class="d2-demo-article">
<div v-if="!long" class="d2-demo-article__control">
<el-switch
v-model="isLong"
active-text="长内容"
inactive-text="短内容">
</el-switch>
inactive-text="短内容"/>
</div>
<d2-markdown v-show="isLong" :source="long"/>
<d2-markdown v-show="!isLong" :source="short"/>
<d2-markdown v-show="isLong" :source="sourceLong"/>
<d2-markdown v-show="!isLong" :source="sourceShort"/>
</div>
</template>
<script>
import long from '../md/long.md'
import short from '../md/short.md'
import sourceLong from '../md/long.md'
import sourceShort from '../md/short.md'
export default {
props: {
//
long: {
type: Boolean,
required: false,
default: false
}
},
data () {
return {
long,
short,
sourceLong,
sourceShort,
isLong: false
}
},
created () {
this.isLong = this.long
}
}
</script>
<style lang="scss" scoped>
.d2-demo-article-control {
padding: 8px 16px;
margin-bottom: 10px;
box-sizing: border-box;
border-radius: 4px;
background-color: #f4f4f5;
.d2-demo-article {
transition: opacity .3s;
.d2-demo-article__control {
padding: 8px 16px;
margin-bottom: 10px;
box-sizing: border-box;
border-radius: 4px;
background-color: #f4f4f5;
}
}
</style>

View File

@ -1,9 +1,7 @@
<template>
<d2-container type="card">
<template slot="header">process.env</template>
<p class="d2-mt-0">NODE_ENV = {{env}}</p>
<p>BASE_URL = {{baseUrl}}</p>
<p class="d2-mb-0">VUE_APP_TITLE = {{title}}</p>
<d2-highlight :code="env"/>
</d2-container>
</template>
@ -11,9 +9,7 @@
export default {
data () {
return {
env: process.env.NODE_ENV,
baseUrl: process.env.BASE_URL,
title: process.env.VUE_APP_TITLE
env: JSON.stringify(process.env, null, 2)
}
}
}

View File

@ -1,5 +1,7 @@
<template>
<d2-container class="page">
<d2-container
class="page"
:filename="filename">
<d2-page-cover
:title="`D2 Admin ${version}`"
sub-title="优雅的中后台集成方案">
@ -43,6 +45,11 @@ export default {
D2HelpBtn,
D2Badge
},
data () {
return {
filename: __filename
}
},
computed: {
...mapState('d2admin/releases', [
'version'

View File

@ -18,6 +18,7 @@ export default {
{ path: 'container/card', name: `${pre}container-card`, component: () => import('@/pages/demo/components/container/card.vue'), meta: { ...meta, title: '布局组件 卡片' } },
{ path: 'container/card-slot', name: `${pre}container-card-slot`, component: () => import('@/pages/demo/components/container/card-slot.vue'), meta: { ...meta, title: '布局组件 卡片 插槽' } },
{ path: 'container/card-bs', name: `${pre}container-card-bs`, component: () => import('@/pages/demo/components/container/card-bs.vue'), meta: { ...meta, title: '布局组件 卡片 滚动优化' } },
{ path: 'container/api', name: `${pre}container-api`, component: () => import('@/pages/demo/components/container/api.vue'), meta: { ...meta, title: '布局组件 API' } },
{ path: 'contextmenu/simple', name: `${pre}contextmenu-simple`, component: () => import('@/pages/demo/components/contextmenu/simple.vue'), meta: { ...meta, title: '右键菜单 基础' } },
{ path: 'contextmenu/divier', name: `${pre}contextmenu-divier`, component: () => import('@/pages/demo/components/contextmenu/divier.vue'), meta: { ...meta, title: '右键菜单 分割线' } },
{ path: 'contextmenu/group', name: `${pre}contextmenu-group`, component: () => import('@/pages/demo/components/contextmenu/group.vue'), meta: { ...meta, title: '右键菜单 分组' } },

View File

@ -52,6 +52,10 @@ module.exports = {
// 重新设置 alias
config.resolve.alias
.set('@', resolve('src'))
// node
config.node
.set('__dirname', true)
.set('__filename', true)
// babel-polyfill 加入 entry
const entry = config.entry('app')
entry