本文将分两部分,上篇主要描述组件功能的开发,下篇将描述打包发布

首先给大家拜个早年。。。没想到距离上一篇竟然隔了那么久,中间发生了一些事吧。。。现在整个人应该成熟了些,社会啊

转入正题,近两年是各种mvvm框架发展的鼎盛时期啊,公司也一直在努力,终于在几个月前逐渐全员切换到了vue开心

然额,人生总是不能随你所欲,技术是切换了,但在UI框架上仍然使用着普通框架,比如semantic。

我不是针对谁,其他人虽然没啥感觉,但对于玩过了vuetify, mint-ui等我来说,在一个可以数据双向绑定的地方强迫被使用semantic的数据交互方式,这是何等的**啊。。淌血而且前后端还没分离,没分离,没分离。。。

好在这是一个月前的事了,现在我的建(强)议(迫)下,我们进行了重构分离了前后端并开始使用element-ui(跪谢饿了么前端大佬们)

正文

咳,感觉说了很多废话。其实这个小项目早在两个月前就写好了发到git上了,迫于各种因素(懒)一直没有好好写一个笔记记录,现在好像忘的差不多了。。。README.md还是有好好写的,可以到git看:vue-semantic-dropdown

当时使用semantic做一些下拉菜单的需求,经常会有级联选择的那种,一来来三个,本来可以用v-modal分分钟搞定的,但奈何semantic的下拉菜单结构都是这样的

1
2
3
4
5
6
7
8
9
<div class="ui selection dropdown">
<input type="hidden" name="gender">
<i class="dropdown icon"></i>
<div class="default text">Gender</div>
<div class="menu">
<div class="item" data-value="1">Male</div>
<div class="item" data-value="0">Female</div>
</div>
</div>

你说我绑哪!绑最外层div肯定不对,看它交互是吧选中value放input里的,但绑在一个hidden的input也不管用啊,一个级联愣是写了几十行。。。恶心

通过查阅资料,我发现可以这样手动设置
<input type="hidden" v-model="value">
then
this.$emit('input', theValue)
把它封在组件内部,在外面引用的也可以把这坨当成一个普通的input使用v-model进行操作,因垂死庭思考

查资料的时候发现遇到这个问题的小伙伴还不少,如果我把它开源了,是不是就能帮助到别人,至少是公司的小伙伴?兴奋~

say do let’s do, 先看下都有哪些需要设置成可配置的:

  • class 不同的风格可能有不同需求,
  • name 在表单里可能会用到
  • default text 默认显示的文本
  • options 下拉菜单的主要数据list,每项包含value和text

ok:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<div :class="classList">
<input type="hidden" :name="name" v-model="value">
<i class="dropdown icon"></i>
<div class="default text">{{ defaultText }}</div>
<div class="menu">
<div class="item" v-for="(option, index) in list" :key="index"
:data-value="option.value" :data-text="option.text"
>{{ option.text }}</div>
</div>
</div>
</template>
<script>
export default {
props: {
addClass: {
type: String
},
name: {
type: String,
required: true
},
value: {
required: true
},
defaultText: {
required: true
},

options: {
required: true
},
},

computed: {
classList: function() {
return 'ui dropdown ' + this.addClass;
},
}
</script>

然后是数据处理

1
2
3
4
5
6
7
8
9
10
11
12
13
methods: {
initDropdown: function() {
var self = this,
settings = {
action: 'activate',
onChange: function(value, text){
self.$emit('input', value)
}
}

$(this.$el).dropdown(settings)
}
}

然后再把这个函数放到mounted()里,是不是超简单。不过既然要给别人用,还是要尽量满足多样化的需求,比如人家的action不是activate啦;人家的option的key叫id和text啦;dropdown()里有其他的操作啦

最后经过优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<template>
<div :class="classList">
<input type="hidden" :name="name" v-model="value">
<i class="dropdown icon"></i>
<div class="default text">{{ defaultText }}</div>
<div class="menu">
<div class="item" v-for="(option, index) in list" :key="index"
:data-value="option.value" :data-text="option.text"
>{{ option.text }}</div>
</div>
</div>
</template>
<script>
export default {
props: {
addClass: {
type: String
},
name: {
type: String,
required: true
},
value: {
required: true
},
defaultText: {
required: true
},

options: {
required: true
},
textFiled: {
default: 'text'
},
valueFiled: {
default: 'value'
},

action: {
type: String,
default: 'activate'
},
setting: {
type: Object
}
},

mounted() {
this.$nextTick(() => {
this.initDropdown()
})
},

updated() {
this.$nextTick(() => {
this.initDropdown()
})
},

computed: {
classList: function() {
return 'ui dropdown ' + this.addClass;
},

list: function() {
if(!this.options) {
console.error('options is null')
}

return this.options.map((option) => {
if(option.constructor === Object) {
return {
text: option[this.textFiled],
value: option[this.valueFiled]
}
} else {
return {
text: option,
value: option
}
}
})
}
},

methods: {
initDropdown: function() {
var self = this,
settings = $.extend({
action: this.action,
onChange: function(value, text){
self.$emit('input', value)
// 传递选中的文本
self.$emit('dropdown-selected', text)
}
}, this.setting)

$(this.$el).dropdown(settings)
}
}
}
</script>

最后加上update()和this.$nextTick()双重保障以防value在刷新和重新渲染的时候出现异常

使用的时候import然后注册组件然后就可以这样使用咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<Dropdown addClass="selection" name="selection" defaultText="请选择"
v-model="selectedValue" :options="options"
textFiled="value" valueFiled="id"
@dropdown-selected="(text) => { selectedText = text}"
></Dropdown>
<span>id: {{ selectedValue }} name:{{ selectedText }} </span>

...

data() {
return {
options: [
{
id: 'litteRed',
value: '小红'
},
{
id: 'litteBlue',
value: '小蓝'
}
],
selectedValue: null,
selectedText: null,
}
}

详细可以装一个看看咯,包里面有例子的咯,
npm install vue-semantic-dropdown --save
求点赞哟