Skip to content

Commit cb4647e

Browse files
自定义组件开发
1 parent 8e23050 commit cb4647e

10 files changed

Lines changed: 346 additions & 0 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<template>
2+
<div id="app"></div>
3+
</template>
4+
5+
<script>
6+
export default {
7+
methods: {
8+
notify () {
9+
this.$notify({
10+
content: 'test $notify',
11+
btn: 'close'
12+
})
13+
}
14+
}
15+
}
16+
</script>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import Notification from './notification.vue'
2+
3+
export default {
4+
extends: Notification,
5+
computed: {
6+
style () {
7+
return {
8+
position: 'fixed',
9+
right: '20px',
10+
bottom: `${this.verticalOffset}px`
11+
}
12+
}
13+
},
14+
mounted () {
15+
this.createTimer()
16+
},
17+
methods: {
18+
createTimer () {
19+
console.log(this.autoClose)
20+
if (this.autoClose) {
21+
this.timer = setTimeout(() => {
22+
this.visible = false
23+
}, this.autoClose)
24+
}
25+
},
26+
clearTimer () {
27+
if (this.timer) {
28+
clearTimeout(this.timer)
29+
}
30+
},
31+
afterEnter () {
32+
this.height = this.$el.offsetHeight
33+
}
34+
},
35+
beforeDestory () {
36+
this.clearTimer()
37+
},
38+
data () {
39+
return {
40+
verticalOffset: 0,
41+
autoClose: 3000,
42+
height: 0,
43+
visible: false // 为了适配transition,绑定afterEnter
44+
}
45+
}
46+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import Vue from 'vue'
2+
import Component from './func-notification'
3+
4+
const NotificationConstructor = Vue.extend(Component)
5+
6+
const instances = []
7+
let seed = 1
8+
9+
const removeInstance = (instance) => {
10+
if (!instance) return
11+
const len = instances.length
12+
const index = instances.findIndex(inst => instance.id === inst.id)
13+
14+
instances.splice(index, 1)
15+
16+
if (len <= 1) return
17+
const removeHeight = instance.vm.height
18+
for (let i = index; i < len - 1; i++) {
19+
instances[i].verticalOffset =
20+
parseInt(instances[i].verticalOffset) - removeHeight - 16
21+
}
22+
}
23+
24+
const notify = (options) => {
25+
if (Vue.prototype.$isServer) return
26+
27+
const {
28+
autoClose,
29+
...rest
30+
} = options
31+
const instance = new NotificationConstructor({
32+
propsData: {
33+
...rest
34+
},
35+
data: {
36+
autoClose: autoClose === undefined ? 3000 : autoClose
37+
}
38+
})
39+
40+
const id = `notification_${seed++}`
41+
instance.id = id
42+
instance.vm = instance.$mount()
43+
document.body.appendChild(instance.vm.$el)
44+
instance.vm.visible = true
45+
46+
let verticalOffset = 0
47+
instances.forEach(item => {
48+
verticalOffset += item.$el.offsetHeight + 16
49+
})
50+
verticalOffset += 16
51+
instance.verticalOffset = verticalOffset
52+
instances.push(instance)
53+
instance.vm.$on('closed', () => {
54+
removeInstance(instance)
55+
document.body.removeChild(instance.vm.$el)
56+
instance.vm.$destroy()
57+
})
58+
instance.vm.$on('close', () => {
59+
instance.vm.visible = false
60+
})
61+
return instance.vm
62+
}
63+
64+
export default notify
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Notification from './notification.vue'
2+
import notify from './function'
3+
4+
export default (Vue) => {
5+
Vue.component(Notification.name, Notification)
6+
Vue.prototype.$notify = notify
7+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<template>
2+
<transition name="fade" @after-leave="afterLeave" @after-enter="afterEnter">
3+
<div
4+
class="notification"
5+
:style="style"
6+
v-show="visible"
7+
@mouseenter="clearTimer"
8+
@mouseleave="createTimer"
9+
>
10+
<span class="content">{{content}}</span>
11+
<a class="btn" @click="handleClose">{{btn}}</a>
12+
</div>
13+
</transition>
14+
</template>
15+
16+
<script>
17+
export default {
18+
name: 'Notification',
19+
// 传参
20+
props: {
21+
content: {
22+
type: String,
23+
required: true
24+
},
25+
btn: {
26+
type: String,
27+
default: '关闭'
28+
}
29+
},
30+
data () {
31+
return {
32+
visible: true
33+
}
34+
},
35+
computed: {
36+
style () {
37+
return {}
38+
}
39+
},
40+
methods: {
41+
// 关闭事件
42+
handleClose (e) {
43+
e.preventDefault()
44+
this.$emit('close')
45+
},
46+
afterLeave () {
47+
this.$emit('closed')
48+
},
49+
afterEnter () {},
50+
clearTimer () {},
51+
createTimer () {}
52+
}
53+
}
54+
</script>
55+
56+
<style lang="stylus" scoped>
57+
.notification
58+
display: inline-flex
59+
background-color #303030
60+
color rgba(255, 255, 255, 1)
61+
align-items center
62+
padding 20px
63+
min-width 280px
64+
box-shadow 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12)
65+
flex-wrap wrap
66+
transition all .3s
67+
.content
68+
padding 0
69+
.btn
70+
color #ff4081
71+
padding-left 24px
72+
margin-left auto
73+
cursor pointer
74+
.fade-enter-active, .fade-leave-active
75+
transition: opacity .5s
76+
.fade-enter, .fade-leave-to
77+
opacity: 0
78+
</style>
79+
80+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Tabs from './tabs.vue'
2+
import Tab from './tab.vue'
3+
4+
export default (Vue) => {
5+
Vue.component(Tabs.name, Tabs)
6+
Vue.component(Tab.name, Tab)
7+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script>
2+
export default {
3+
props: {
4+
panes: {
5+
type: Array,
6+
required: true
7+
}
8+
},
9+
render () {
10+
const contents = this.panes.map(pane => {
11+
return pane.active ? pane.$slots.default : null
12+
})
13+
return (
14+
<div class="tab-content">
15+
{contents}
16+
</div>
17+
)
18+
}
19+
}
20+
</script>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<script>
2+
export default {
3+
name: 'Tab',
4+
props: {
5+
index: {
6+
required: true,
7+
type: [Number, String]
8+
},
9+
label: {
10+
type: String,
11+
default: 'tab'
12+
}
13+
},
14+
mounted () {
15+
this.$parent.panes.push(this)
16+
},
17+
computed: {
18+
active () {
19+
return this.$parent.value === this.index
20+
}
21+
},
22+
methods: {
23+
handleClick () {
24+
this.$parent.onChange(this.index)
25+
}
26+
},
27+
render () {
28+
const tab = this.$slots.label || <span>{this.label}</span>
29+
const classNames = {
30+
tab: true,
31+
active: this.active
32+
}
33+
return (
34+
<li class={classNames} on-click={this.handleClick}>
35+
{tab}
36+
</li>
37+
)
38+
}
39+
}
40+
</script>
41+
42+
<style lang="stylus" scoped>
43+
.tab
44+
list-style none
45+
line-height 40px
46+
margin-right 30px
47+
position relative
48+
bottom -2px
49+
cursor pointer
50+
&.active
51+
border-bottom 2px solid blue
52+
&:last-child
53+
margin-right 0
54+
</style>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<script>
2+
import TabContainer from './tab-container.vue'
3+
4+
export default {
5+
name: 'Tabs',
6+
components: {
7+
TabContainer
8+
},
9+
props: {
10+
value: {
11+
type: [String, Number],
12+
required: true
13+
}
14+
},
15+
data () {
16+
return {
17+
panes: []
18+
}
19+
},
20+
render () {
21+
return (
22+
<div class='tabs'>
23+
<ul class='tabs-header'>
24+
{this.$slots.default}
25+
</ul>
26+
<tab-container panes={this.panes}></tab-container>
27+
</div>
28+
)
29+
},
30+
methods: {
31+
onChange (index) {
32+
this.$emit('change', index)
33+
}
34+
}
35+
}
36+
</script>
37+
38+
<style lang="stylus" scoped>
39+
.tabs-header
40+
display flex
41+
list-style none
42+
margin 0
43+
padding 0
44+
border-bottom 2px solid #ededed
45+
</style>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Vue from 'vue'
2+
3+
import Notification from './components/notification'
4+
5+
Vue.use(Notification)
6+
7+
// ...

0 commit comments

Comments
 (0)