造一个属于自己的 UI 库
2018-01-30 00:08
176 查看
一、项目介绍
![](https://static.oschina.net/uploads/space/2017/1216/230619_OSTG_2912341.png)
vui : 一个私人的vue ui 组件库(移动端为主)
文档官网
已有组件
swiperscroller
search
message
modal
table
picker
select
dropdown
二、安装下载
npm install x-vui -S
三、快速开始
3.1 构建项目(配合vue-cli)
# 全局安装 vue-cli npm install --global vue-cli # 创建一个基于 webpack 模板的新项目 vue init webpack my-vui-project # 安装依赖,并下载x-vui cd my-vui-project npm install && npm install x-vui # 项目启动 默认端口localhost:8080 npm run dev
3.2 引入vui组件库
你可以引入整个 vui,或是根据需要仅引入部分组件。我们先介绍如何引入完整的 vui。3.2.1 完整引入
在main.js中写入import Vue from 'vue' import vui from 'x-vui' import 'x-vui/lib/vui-css/index.css'; Vue.use(vui)
3.2.2 按需部分引入
在main.js中写入(假如我只需要Scroller和Select组件)import Vue from 'vue' import { Scroller, Select // ... } from 'x-vui' import 'x-vui/lib/vui-css/scroller.css'; import 'x-vui/lib/vui-css/select.css'; Vue.component(Scroller.name, Scroller) Vue.com 20000 ponent(Select.name, Select)
3.2.3 全局注册vui插件
注:完整引入了vui,则无需再注册插件import Vue from 'vue'; import { $Toast, $Dialog // ... } from 'x-vui'; Vue.prototype.$toast = $Toast Vue.prototype.$dialog = $Dialog
四、组件用法
4.1 swiper
可以自己调配自己想要的swiper,不一定得是轮播图![](https://static.oschina.net/uploads/img/201712/16180430_6F2Z.gif)
4.1.1 Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
type | swiper类型 | string | swiper(正常)/thum(缩略) | swiper |
auto | 自动播放时长 | number | — | 5000 |
items | swiper展示的列表 | array | — | [] |
showIndicators | 是否展示swiper小圆点 | boolean | — | true |
styles | swiper样式控制 | object | — | {} |
resetTitle | 重置title内容 | string | — | — |
4.1.2 Events
事件名称 | 说明 | 回调参数 |
---|---|---|
change | swiper滑动回调 | 当前swiper item索引 |
4.1.3 用法
<template> <div class="swiper-page"> <p>正常swiper</p> <x-swiper type='swiper' :items='items' :styles="{height: '180px'}"></x-swiper> <p>缩略swiper</p> <x-swiper type='swiper' :items='items' :type="'thum'" :styles="{height: '208px'}"></x-swiper> </div> </template> <script> export default { data () { return { items: [ require('assets/beauty_1.png'), require('assets/beauty_2.png'), require('assets/beauty_3.png'), require('assets/beauty_4.png'), require('assets/beauty_5.png') ], } } } </script> <style lang="stylus" scoped> .swiper-page { height auto } </style>
4.2 scroller(下拉刷新上拉加载)
![](https://static.oschina.net/uploads/img/201712/16180439_EZCm.gif)
4.2.1 Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
onRefresh | 下拉回调 | function | — | — |
onInfinite | 上拉回调 | function | — | — |
width | scroller宽度 | string | — | 100% |
height | scroller高度 | string | — | 100% |
isLoadMore | 是否展示上拉加载 | boolean | — | true |
refreshText | 下拉文本内容 | string | — | 下拉刷新 |
noDataText | 无数据文本 | string | — | 没有更多数据啦~ |
refreshLayerColor | 下拉文本颜色 | string | — | #AAA |
loadingLayerColor | 上拉文本颜色 | string | — | #AAA |
animating | 是否有动画 | boolean | — | true |
animationDuration | 动画间隔 | number | — | 250 |
bouncing | 是否有反弹效果 | string | — | true |
cssClass | content css class | string | — | — |
4.2.2 用法
<style scoped> .scroller-page { height: 330px } ul { padding: 20px 0 } li { width: 100%; height: 35px; line-height: 35px; border-bottom: 1px solid #eee; text-align: center; } </style> <template> <div class="scroller-page"> <x-scroller :on-refresh="refresh" :on-infinite="infinite" :noDataText="noDataText" > <!-- content is here --> <ul> <li>数据1</li> <li>数据2</li> <li>数据3</li> <li>数据4</li> <li>数据5</li> <li>数据6</li> </ul> </x-scroller> </div> </template> <script> export default { data () { return { noDataText: '没有更多数据啦~' } }, methods: { refresh (done) { setTimeout(done, 1000) this.noDataText = '' console.log('refresh'); }, infinite (done) { setTimeout(done, 1000, true) this.noDataText = '没有更多数据啦~' console.log('infinite'); } } } </script>
4.3 search
![](https://static.oschina.net/uploads/img/201712/16180441_IMEA.gif)
4.3.1 Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
async | 是否进行节流 | boolean | — | true |
timeout | 搜索节流时长 | number | — | 100 |
styles | search样式 | object | — | — |
placeholder | placeholder | string | — | '搜索' |
autofocus | 是否自动聚焦(iOS端autofocus无效) | boolean | — | — |
clear | 进行搜索是否清空search框内容 | boolean | — | false |
4.3.2 Events
事件名称 | 说明 | 回调参数 |
---|---|---|
search | search搜索回调 | 搜索文本 |
enter | enter时搜索回调 | 搜索文本 |
close | 点击搜索关闭按钮回调 | '' |
4.3.3 用法
只有搜索框<style lang="stylus"> .search-page { padding: 0 10px; margin-top: 10px; } </style> <template> <div> <x-search placeholder="请输入搜索关键字" @search="searchFn" @enter="searchEnter" @close="closeFn" ></x-search> </div> </template> <script> export default { methods: { searchFn (query) { console.log('search', query) }, searchEnter (query) { console.log('enter', query) }, closeFn (query) { console.log('close', query) } } } </script>
拥有默认的搜索结果列表
<style lang="stylus"> .search-page { padding: 0 10px; margin-top: 10px; } </style> <template> <div class="search-page" v-title data-title="search"> <x-search placeholder="请输入搜索关键字" :autofocus="true" :async="false" @search="searchFn" @enter="searchEnter" @close="closeFn" > <x-search-list :result="filterResult" @listSearch="listSearch" v-show="visible"></x-search-list> </x-search> </div> </template> <script> export default { data () { return { keyword: '', visible: false, // 点击列表,列表是否消失 defaultResult: [ 'Apple', 'Banana', 'Orange', 'Durian', 'Lemon', 'Peach', 'Cherry', 'Berry', 'Core', 'Fig', 'Haw', 'Melon', 'Plum', 'Pear', 'Peanut', 'Other' ] } }, watch: { keyword (val) { if (!val) { this.visible = false; } } }, methods: { searchFn (query) { this.keyword = query; this.visible = true; console.log('search', query) }, searchEnter (query) { this.keyword = query; console.log('enter', query) }, closeFn (query) { this.keyword = query; console.log('close', query) }, listSearch (index) { this.visible = false; console.log(index, this.defaultResult[index]) } }, computed: { filterResult() { return this.defaultResult.filter(item => new RegExp(this.keyword, 'i').test(item)); } } } </script>
定制化结果列表,关键字高亮匹配
<style lang="stylus"> .search-page { padding: 0 10px; margin-top: 10px; .search-result { position: relative; overflow: hidden; .l { width: 100%; margin-bottom: 5px; } .r { position: absolute; right: 0; top: 50%; margin-top: -10px; line-height: 20px; } .price { color: #ff6f5c; } .gray { font-size: 12px; } } } </style> <template> <div class="search-page" v-title data-title="search"> <x-search placeholder="请输入搜索关键字" :autofocus="true" :async="false" @search="searchFn" @enter="searchEnter" @close="closeFn" > <x-search-list :result="filterResult" @listSearch="listSearch" v-show="visible"> <div class="search-result" slot="list-item" slot-scope="props"> <p class="l" v-html="props.slotValue.name"></p> <p class="gray" v-show="props.slotValue.price">¥{{props.slotValue.price}}/斤</p> <div class="gray r" v-show="props.slotValue.amount">剩余{{props.slotValue.amount}}斤</div> </div> </x-search-list> </x-search> </div> </template> <script> export default { data () { return { keyword: '', visible: false, defaultResult: [ {name: 'Apple', price: 5, amount: 20}, {name: 'Banana', price: 5, amount: 30}, {name: 'Orange', price: 3, amount: 10}, {name: 'Durian', price: 10, amount: 25}, {name: 'Lemon', price: 4, amount: 30}, {name: 'Peach', price: 5, amount: 40}, {name: 'Cherry', price: 20, amount: 50}, {name: 'Berry', price: 15, amount: 60}, {name: 'Core', price: 10, amount: 21}, {name: 'Fig', price: 10, amount: 22}, {name: 'Haw', price: 10, amount: 23}, {name: 'Melon', price: 10, amount: 24}, {name: 'Plum', price: 10, amount: 25}, {name: 'Pear', price: 10, amount: 26}, {name: 'Peanut', price: 10, amount: 27}, {name: 'Other'} ], // 防止defaultResult值被污染 copy: [] } }, watch: { keyword (val) { if (!val) { this.visible = false; } } }, methods: { searchFn (query) { this.keyword = query; this.visible = true; console.log('search', query) }, searchEnter (query) { this.keyword = query; console.log('enter', query) }, closeFn (query) { this.keyword = query; console.log('close', query) }, listSearch (index) { this.visible = false; console.log(index, this.defaultResult[index].name) } }, computed: { filterResult() { // i 忽略大小写 let result = this.defaultResult.filter(item => new RegExp(this.keyword, 'i').test(item.name)); // 关键字高亮匹配 this.copy = JSON.parse(JSON.stringify(result)) this.copy.forEach((item, index) => { let name = item.name, word = this.keyword; name = name.toLowerCase(); word = word.toLowerCase(); if (word && name.indexOf(word) !== -1) { let arr = item.name.split('') let i = name.indexOf(word); let len = word.length; let active = '<span class="price">' + arr.splice(i, len).join('') + '</span>'; arr.splice(i, 0, active); item.name = arr.join(''); } }) return this.copy; } } } </script>
4.4 dialog
![](https://static.oschina.net/uploads/img/201712/16180441_uNTH.gif)
4.4.1 Attributes
message
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
msg | msg文本内容 | string | — | — |
timeout | msg显示时长 | number | — | 2000 |
callback | 回调函数 | function | — | — |
icon | 特殊icon | string | — | — |
modal
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
show | modal是否显示 | boolean | — | — |
title | modal标题 | string | — | — |
content | modal内容 | string | — | — |
onOk | 确定按钮回调 | function | — | — |
onCancel | 取消按钮回调 | function | — | — |
okText | 确定按钮内容 | string | — | — |
cancelText | 取消按钮内容 | string | — | — |
showCloseIcon | 是否显示关闭icon | boolean | — | true |
4.4.2 用法
msg
this.$dialog.msg({msg: 'hello message components ~'})
modal(插件)
this.$dialog.modal({ title: 'Demo Modal', cancelText: '取消', okText: '确定', content: '测试,测试,测试,测试,测试,测试,测试,测试,测试', onOk () { console.log('click ok btn to do someting'); }, onCancel () { console.log('click cancel btn to do someting'); } })
modal(组件)
<style lang="stylus"> .dialog-page { .dialog-btn { width 100% position absolute top 50% left 0 transform translateY(-50%) > p { width 80% height 50px line-height 50px margin 40px auto 0 border 1px solid #CCC border-radius 10px font-size 16px font-weight bold letter-spacing 2px text-align center &:first-child { margin-top 0 } } } .modal-text { text-align: center; } } </style> <template> <div class="dialog-page"> <div class="dialog-btn"> <p @click="message">message dialog</p> <p @click="open">modal dialog</p> </div> <x-modal title="Demo Modal" cancelText="取消" :onCancel="close" :show="selectModel" okText="确认" :onOk="close"> <p class="modal-text">modal components test is awesome!!!</p> </x-modal> </div> </template> <script> export default { data () { return { selectModel: false } }, methods: { message () { return this.$dialog.msg({msg: 'this is a message dialog'}) }, open () { this.selectModel = true }, close () { this.selectModel = false } } } </script>
4.5 table
![](https://static.oschina.net/uploads/img/201712/16180442_C5QQ.png)
4.5.1 Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
tableData | table数据 | array | — | — |
label | thead标题(TableColum) | — | — | |
prop | 绑定的数据 | string | — | — |
4.5.2 用法
配合scroller进行展示(注:目前table较为简单,后期将进行完善,使得其可以应对不同场景)<template> <div class="table" v-title data-title="table"> <x-scroller :on-refresh="refresh" :on-infinite="infinite" :noDataText="noDataText" class="table-content" > <x-table :tableData="items"> <x-table-column prop="list_1" label="LIST ONE"></x-table-column> <x-table-column prop="list_2" label="LIST TWO"></x-table-column> <x-table-column prop="list_3" label="LIST THREE"></x-table-column> <x-table-column prop="list_4" label="LIST FOUR"></x-table-column> </x-table> </x-scroller> </div> </template> <script> export default { data () { return { items: [ { list_1: '2017.12.09', list_2: '路人1', list_3: '爱过', list_4: '有' }, { list_1: '2017.12.10', list_2: '路人2', list_3: '爱过', list_4: '有' }, { list_1: '2017.12.11', list_2: '路人3', list_3: '爱过', list_4: '没有' }, { list_1: '2017.12.12', list_2: '路人4', list_3: '爱过', list_4: '没有' } ], noDataText: '没有更多数据啦~' } }, methods: { refresh (done) { setTimeout(done, 1000) this.noDataText = '' console.log('refresh'); }, infinite (done) { setTimeout(done, 1000, true) this.noDataText = '没有更多数据啦~' console.log('infinite'); } } } </script>
4.6 picker
![](https://static.oschina.net/uploads/img/201712/16180443_9Lsx.gif)
4.6.1 Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
default | picker默认选中的值 | string/number | — | — |
type | picker类型 | string | date/time/datetime/custom | datetime |
title | 选择器弹窗标题 | string | — | — |
placeholder | placeholder | string | — | 请选择时间 |
timeStep | 时间选择粒度(有分钟的选择器) | number | — | 1 |
startYear | 起始年份 | number/string | — | 今年 |
endYear | 结束年份 | number/string | — | 10年的范围 |
startDate | 起始日期 | string | — | — |
endDate | 结束日期 | string | — | — |
startHour | 起始时间 | number/string | — | 0 |
endHour | 结束时间 | number/string | — | 23 |
startMinute | 起始分钟 | number/string | — | 0 |
endMinute | 结束分钟 | number/string | — | 59 |
yearFormat | “年“的格式化 | string | — | '{value}年' |
monthFormat | “月“的格式化 | string | — | '{value}月' |
dayFormat | “日“的格式化 | string | — | '{value}日' |
hourFormat | “时“的格式化 | string | — | '{value}时' |
minuteFormat | “分“的格式化 | string | — | '{value}分' |
4.6.2 用法
<style lang="stylus"> .picker-page { .x-list { padding: 0 0.32rem; background: #fff; color: #333; font-size: 14px; > li { min-height: 60px; padding-top: 21px; border-bottom: 1px solid #f2f2f2; overflow: hidden; > label { float: left; } > div { float: right; } .x-list-arrow { min-width: 100px; margin-right: 10px; position: relative; > div { float: right; text-align: right; margin-right: 10px; } &:after { content: ''; position: absolute; top: 4px; right: -5px; width: 10px; height: 10px; border-top: 1px solid #ccc; border-right: 1px solid #ccc; transform: rotate(45deg); -webkit-transform: rotate(45deg); } } } } } </style> <template> <div class="picker-page" v-title data-title="picker"> <ul class='x-list'> <li> <label>日期选择</label> <div class="x-list-arrow"> <x-picker title="选择日期" startYear="2016" startDate="2015-01-01" endDate="2019-12-01" placeholder="请选择日期" v-model="now_date" type="date"></x-picker> </div> </li> <li> <label>时间选择</label> <div class="x-list-arrow"> <x-picker title="选择时间" placeholder="请选择时间" startMinute="2" endMinute="30" v-model="now_time" type="time"></x-picker> </div> </li> <li> <label>日期时间选择</label> <div class="x-list-arrow"> <x-picker title="选择日期时间" placeholder="请选择日期时间" v-model="now_datetime" :timeStep="20" type="datetime"></x-picker> </div> </li> <li> <label>性别选择</label> <div class="x-list-arrow"> <x-picker v-model="gender.value" placeholder="请选择性别" :default="gender.default" title="选择性别" type="custom"></x-picker> </div> </li> </ul> </div> </template> <script> export default { data() { return { gender: { default: -1, value: [ { name: "保密", value: 0 }, { name: "男", value: 1 }, { name: "女", value: 2 } ] }, now_date: null, now_time: null, now_datetime: null // new Date().getTime()/1000 }; } }; </script>
4.7 select
![](https://static.oschina.net/uploads/img/201712/16180444_CjIt.gif)
4.7.1 Attributes
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
selectData | 下拉数据 | array | — | [] |
title | 默认显示的标题 | string | — | '' |
alwaysShowTitle | 是否一直显示默认标题 | boolean | — | false |
defaultValue | 默认选中的值 | number/string | — | 0 |
width | select组件的宽度 | string | — | '100%' |
ellipsisWidth | select文字超过多出省略号的宽度 | string | — | '120px' |
4.7.2 Events
事件名称 | 说明 | 回调参数 |
---|---|---|
search | select 选择时的回调函数 | 参数1:索引,参数2:所中项的id值 |
4.7.3 用法
<template> <div class="select-page" v-title data-title="select"> <x-select @search="searchFn" :selectData="selectData" title="LIST ONE" :alwaysShowTitle="false" width="50%" defaultValue="0" ></x-select> <x-select @search="searchFn1" :selectData="selectData1" title="LIST TWO" width="50%" ellipsisWidth="65px" defaultValue="1" ></x-select> </div> </template> <script> export default { data() { return { selectData: [ { id: 1, name: "LIST ONE 1" }, { id: 2, name: "LIST ONE 2" }, { id: 3, name: "LIST ONE 3" }, { id: 4, name: "LIST ONE 4" }, { id: 5, name: "LIST ONE 5" } ], selectData1: [ { id: 1, name: "LIST TWO 1" }, { id: 2, name: "LIST TWO 2" }, { id: 3, name: "LIST TWO 3" }, { id: 4, name: "LIST TWO 4" }, { id: 5, name: "LIST TWO 5" } ] }; }, methods: { searchFn(index, id) { console.log(index, id); }, searchFn1(index, id) { console.log(index, id); } } }; </script>
4.8 dropdown
![](https://static.oschina.net/uploads/img/201712/16180444_7sq2.png)
这个下拉菜单偏PC端的这里就不多做介绍了
<template> <div class="test"> <x-dropdown trigger="click" @command="commandHandle" :hide-on-click="true"> <span class="drop-down_link">下拉菜单</span> <x-dropdown-menu> <x-dropdown-list command="a">下拉列表1</x-dropdown-list> <x-dropdown-list command="b">下拉列表2</x-dropdown-list> <x-dropdown-list command="c"><h4>下拉列表3</h4></x-dropdown-list> </x-dropdown-menu> </x-dropdown> </div> </template> <script> export default { name: 'Dropdown', methods: { commandHandle (command, instance) { console.log(command, instance); } } } </script>
以上组件便是目前vui所有的组件了,后期会不断的进行维护并进行新组件的开发。
vui github传送门:https://github.com/Brickies/vui
vui npm传送门:https://www.npmjs.com/package/x-vui
如果小伙伴们喜欢我的vui,欢迎star。
如果有什么问题欢迎小伙伴们随时提issue
如果有好的组件欢迎小伙伴们随时提pr,我会不定期进行merge
相关文章推荐
- 合格前端系列第八弹-造一个属于自己的 UI 库
- 一个基于Yahoo UI的Ajax俄罗斯方块游戏
- Android UI 之居中绘制文本内容的正确方法——实现自定义一个TextView
- 金山应该有一个很棒的UI设计团队
- Android training(1)-Building a Dynamic UI with Fragments(用碎片创造一个动态的UI)(C)
- 一个强大的LogParser的UI工具--logparserlizard简介
- 做一个属于自己的照片编辑器
- 打造一个属于自己的BaseActivity(沉浸状态栏,dialog,完全退出程序。应有尽有)
- [android ui]android 获取上一个activity的返回值
- 【微信开发】02.搭建一个属于自己的微信公众平台
- 实现一个UI效果,有关文字倾斜
- iOS开发UI篇章使用UItableview完成一个简单的QQ好友列表(一)
- 新人如何搭建(copy)一个属于自己的博客
- 如何在一个Activity更新另一个Activity的UI(发送广播的方法)
- [Andriod官方训练教程]使用Fragment创建一个动态的UI之使用Support Library
- [Andriod官方训练教程]使用Fragment创建一个动态的UI之与其他Fragments进行交互
- vue用mint-ui的picker组件封装一个省市区三级联动组件
- 一个高效的UI才是一个拉风的UI(二)
- android为什么不允许新开启一个线程来更新UI,而是用handler来更新界面
- 制作一个属于自己的BHO吧!(C#) (转)