合格前端系列第八弹-造一个属于自己的ui库


声明:本文转载自https://my.oschina.net/qiangdada/blog/1590089,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

一、项目介绍

Version Downloads License TravisCI

vui : 一个私人的vue ui 组件库(移动端为主)

已有组件

二、安装下载

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 { VUICOMS } from 'x-vui'  VUICOMS.map(component => {   Vue.component(component.name, component) })

3.2.2 按需部分引入

在main.js中写入(假如我只需要Scroller和Select组件)

import {   Scroller,   Select   // ... } from 'x-vui'  Vue.component(Scroller.name, Scroller) Vue.component(Select.name, Select)

3.2.3 全局注册vui插件

import {   $msg,   $modal } from 'x-vui'  Vue.prototype.$dialog = {   msg: $msg,   modal: $modal }


四、组件用法

4.1 swiper

可以自己调配自己想要的swiper,不一定得是轮播图

组件props

props: {   // 'swiper' => 正常swiper 'thum' => 缩略swiper   type: String,   // 自动播放时长,默认3000ms   auto: Number,   // 轮播展示的列表   items: Array,   // 是否展示轮播小圆点   showIndicators: Boolean,   // 轮播图样式控制   styles: Object,   // 重置title内容   resetTitle: String },

组件用法

<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(下拉刷新上拉加载)

组件props

// 常用 props,更多的属性可以转到我github的项目vui进行查看 props: {   // 下拉回调   onRefresh: Function,   // 上拉回调   onInfinite: Function,   // 是否展示上拉加载   isLoadMore: {     type: Boolean,     default: true   },   // 下拉文本内容   refreshText: {     type: String,     default: '下拉刷新'   },   // 无数据文本   noDataText: {     type: String,     default: '没有更多数据啦~'   } },

 组件用法

<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

组件props

props: {   // 搜索节流时长   timeout: {     type: Number,     default: 100   },   // 是否进行节流   async: {     type: Boolean,     default: true   },   // search样式   styles: Object,   placeholder: {     type: String,     default: '搜索'   },   // 是否自动聚焦   autofocus: Boolean, // iOS移动端autofocus无效   // 进行搜索是否清空search框内容   clear: {     type: Boolean,     default: false   }, },

组件用法

1、只有搜索框

<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>

2、拥有默认的搜索结果列表

<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>

3、定制化结果列表,关键字高亮匹配

<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

4.4.1 msg

/**  * props {  *   msg: String,  // 消息  *   timeout: {  *     type: Number,  *     default: 2000  *   },  // message消失延时时长  *   callback: Function,  // 回调  *   icon: String,  // 信息开头是否有特殊icon  * }  */ this.$dialog.msg({msg: 'hello message components ~'})

4.4.2 modal

/**  *  * show: Boolean,  * title: String,  * onOk: Function,  * content: String,  * onCancel: Function,  * cancelText: String,  * okText: String,  * showCloseIcon: {  *   type: Boolean,  *   default: true  * }  */ 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组件, 试用自己特制的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

配合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

组件props

/**   * type = 'date' 日期选择器   * type = 'time' 时间选择器   * type = 'datetime' 日期时间选择器   * type = 'custom' 普通选择器   **/ props: {   // 时间选择粒度,仅限有时间的选择器,默认为1分钟的粒度   timeStep: Number,   // 选择器弹窗标题   title: String,   // 起始年份 默认为今年   startYear: [Number, String],   // 结束年份 默认起始开始算10年的范围   endYear: [Number, String],   // 起始日期  类似 '2017-12-10'   startDate: String,   // 结束日期 验证同起始日期   endDate: String   // 起始时间 0-23   startHour: [Number, String],   // 结束时间 1-23   endHour: [Number, String],   // 起始分钟 0-59   startMinute: [Number, String],   // 结束分钟 1-59   endMinute: [Number, String] }

组件用法

<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

组件props

props: {   // 数据   selectData: Array,   // 默认显示的标题   title: String,   // 是否一直显示默认标题   alwaysShowTitle: Boolean,   // 默认选中的值   defaultValue: [Number, String],   // select组件的宽度,默认继承父元素100%的width   width: {     type: String,     default: '100%'   },   // select文字超过多出省略号的宽度   ellipsisWidth: {     type: String,     default: '120px'   } }

组件用法

<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

这个下拉菜单偏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/xuqiang521/vui

vui npm传送门:https://www.npmjs.com/package/x-vui

如果小伙伴们喜欢我的vui,欢迎star。

如果有什么问题欢迎小伙伴们随时提issue

如果有好的组件欢迎小伙伴们随时提pr,我会不定期进行merge

本文发表于2017年12月15日 10:33
(c)注:本文转载自https://my.oschina.net/qiangdada/blog/1590089,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1811 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1