├── .gitignore ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── copyright │ └── profiles_settings.xml ├── dictionaries │ └── .xml ├── inspectionProfiles │ └── Project_Default.xml ├── misc.xml ├── modules.xml ├── vcs.xml ├── vueComponents.iml ├── watcherTasks.xml ├── webResources.xml └── workspace.xml ├── .travis.yml ├── LICENSE ├── README.MD ├── babel.config.js ├── button.js ├── deploy.sh ├── docs ├── .vuepress │ ├── components │ │ ├── button-demos.vue │ │ ├── cascader-demos.vue │ │ ├── collapse-demos.vue │ │ ├── grid-demos.vue │ │ ├── icon-demos.vue │ │ ├── input-demos.vue │ │ ├── layout-demos.vue │ │ ├── nav-demos.vue │ │ ├── nav2-demos.vue │ │ ├── pager-demos.vue │ │ ├── popover-demos.vue │ │ ├── slides-demos.vue │ │ ├── table-demos.vue │ │ ├── tabs-demos.vue │ │ └── toast-demos.vue │ ├── config.js │ └── styles │ │ └── palette.styl ├── README.md ├── components │ ├── button.md │ ├── cascader.md │ ├── collapse.md │ ├── grid.md │ ├── icon.md │ ├── input.md │ ├── layout.md │ ├── nav.md │ ├── pager.md │ ├── popover.md │ ├── slides.md │ ├── table.md │ ├── tabs.md │ └── toast.md ├── get-started │ └── README.md └── install │ └── README.md ├── karma.config.js ├── package.json ├── src ├── app.js ├── button │ ├── button-group.vue │ └── button.vue ├── cascader │ ├── cascader-items.vue │ ├── cascader.vue │ └── demo.vue ├── click-outside.js ├── collapse │ ├── collapse-item.vue │ └── collapse.vue ├── demo.vue ├── grid │ ├── col.vue │ └── row.vue ├── icon.vue ├── icon │ └── icon.vue ├── input │ └── input.vue ├── layout │ ├── content.vue │ ├── footer.vue │ ├── header.vue │ ├── layout.vue │ └── sider.vue ├── main.js ├── nav │ ├── nav-item.vue │ ├── nav.vue │ └── sub-nav.vue ├── pager │ ├── demo.vue │ └── pager.vue ├── plugin.js ├── popover │ └── popover.vue ├── slides │ ├── slides-item.vue │ └── slides.vue ├── svg.js ├── table │ ├── demo.vue │ └── table.vue ├── tabs │ ├── tabs-body.vue │ ├── tabs-head.vue │ ├── tabs-item.vue │ ├── tabs-pane.vue │ └── tabs.vue ├── toast │ └── toast.vue └── validate.js ├── styles └── var.scss ├── tests ├── fixtures │ └── db.js └── unit │ ├── button.spec.js │ ├── button.test.js │ ├── col.test.js │ ├── input.test.js │ ├── nav.spec.js │ ├── pager.spec.js │ ├── popover.spec.js │ ├── row.test.js │ ├── slides.spec.js │ ├── tabs-body.test.js │ ├── tabs-head.test.js │ ├── tabs-item.test.js │ ├── tabs-pane.test.js │ ├── tabs.test.js │ ├── toast.test.js │ └── validate.spec.js ├── vue.config.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | .cache/ -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/dictionaries/.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ipad 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/vueComponents.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/webResources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | #- "9" 5 | - "10" 6 | addons: 7 | chrome: stable 8 | sudo: required 9 | before_script: 10 | - "sudo chown root /opt/google/chrome/chrome-sandbox" 11 | - "sudo chmod 4755 /opt/google/chrome/chrome-sandbox" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 陈晓拉尼 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Govue- 一个 使用Vue写的UI组件 2 | [![Build Status](https://travis-ci.org/mywebc/vueComponents.svg?branch=master)](https://travis-ci.org/mywebc/vueComponents) 3 | ## 介绍 4 | 这是在学习vue中,造的UI组件 5 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /button.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mywebc/vueComponents/cb288b97fb3519f9fedac2828a1cd09852bcab18/button.js -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | set -e 2 | 3 | # 生成静态文件 4 | npm run docs:build 5 | 6 | # 进入生成的文件夹 7 | cd docs/.vuepress/dist 8 | 9 | # 如果是发布到自定义域名 10 | # echo 'www.example.com' > CNAME 11 | 12 | git init 13 | git add -A 14 | git commit -m 'deploy' 15 | 16 | # 如果发布到 https://.github.io 17 | # git push -f git@github.com:/.github.io.git master 18 | 19 | # 如果发布到 https://.github.io/ 20 | git push -f git@github.com:mywebc/vueComponents.git master:gh-pages 21 | 22 | cd - -------------------------------------------------------------------------------- /docs/.vuepress/components/button-demos.vue: -------------------------------------------------------------------------------- 1 | 9 | 19 | -------------------------------------------------------------------------------- /docs/.vuepress/components/cascader-demos.vue: -------------------------------------------------------------------------------- 1 | 11 | 55 | -------------------------------------------------------------------------------- /docs/.vuepress/components/collapse-demos.vue: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /docs/.vuepress/components/grid-demos.vue: -------------------------------------------------------------------------------- 1 | 45 | 56 | -------------------------------------------------------------------------------- /docs/.vuepress/components/icon-demos.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /docs/.vuepress/components/input-demos.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /docs/.vuepress/components/layout-demos.vue: -------------------------------------------------------------------------------- 1 | 28 | 45 | -------------------------------------------------------------------------------- /docs/.vuepress/components/nav-demos.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 48 | 49 | -------------------------------------------------------------------------------- /docs/.vuepress/components/nav2-demos.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 48 | 49 | -------------------------------------------------------------------------------- /docs/.vuepress/components/pager-demos.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | -------------------------------------------------------------------------------- /docs/.vuepress/components/popover-demos.vue: -------------------------------------------------------------------------------- 1 | 71 | -------------------------------------------------------------------------------- /docs/.vuepress/components/slides-demos.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 34 | 35 | -------------------------------------------------------------------------------- /docs/.vuepress/components/table-demos.vue: -------------------------------------------------------------------------------- 1 | 42 | 102 | 110 | -------------------------------------------------------------------------------- /docs/.vuepress/components/tabs-demos.vue: -------------------------------------------------------------------------------- 1 | 32 | -------------------------------------------------------------------------------- /docs/.vuepress/components/toast-demos.vue: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: 'GoUI', 3 | base: '/vueComponents/', 4 | description: '一些自己写的UI组件', 5 | themeConfig: { 6 | sidebarDepth: 0,// 禁用所有链接 7 | nav: [ 8 | {text: '主页', link: '/'}, 9 | {text: '博客', link: 'https://www.chenxiaolani.com/'}, 10 | {text: 'Github', link: 'https://github.com/mywebc/vueComponents'}, 11 | ], 12 | sidebar: [ 13 | '/get-started/', 14 | // { 15 | // title: '介绍', 16 | // children: [ 17 | // '/install/', 18 | // // '/get-started/' 19 | // ] 20 | // }, 21 | { 22 | title: '组件', 23 | collapsable: false, 24 | children: [ 25 | { 26 | title: "基础", 27 | collapsable: false, 28 | children: [ 29 | '/components/icon', 30 | '/components/button' 31 | ], 32 | }, 33 | { 34 | title: "布局", 35 | collapsable: false, 36 | children: [ 37 | '/components/grid', 38 | '/components/layout', 39 | ] 40 | }, 41 | { 42 | title: "导航", 43 | collapsable: false, 44 | children: [ 45 | '/components/nav', 46 | '/components/pager', 47 | '/components/slides', 48 | '/components/tabs', 49 | '/components/collapse', 50 | ] 51 | }, 52 | { 53 | title: "提示", 54 | collapsable: false, 55 | children: [ 56 | '/components/popover', 57 | '/components/toast', 58 | ] 59 | }, 60 | { 61 | title: "数据", 62 | collapsable: false, 63 | children: [ 64 | '/components/input', 65 | '/components/table', 66 | '/components/cascader', 67 | ] 68 | } 69 | 70 | ] 71 | } 72 | ] 73 | } 74 | } -------------------------------------------------------------------------------- /docs/.vuepress/styles/palette.styl: -------------------------------------------------------------------------------- 1 | 2 | $accentColor = #1fbcb6//默认主题颜色 3 | $textColor = #2c3e50//默认字体颜色 #02717d 4 | $borderColor = #eaecef//默认边框颜色 5 | $codeBgColor = #282c34 //默认背景颜色 6 | 7 | //$accentColor = #3eaf7c 8 | //$textColor = #2c3e50 9 | //$borderColor = #eaecef 10 | //$codeBgColor = #282c34 11 | . sidebar-group .sidebar-heading { 12 | opacity:1; 13 | font-weight 700; 14 | } 15 | 16 | .sidebar-links .sidebar-group-items li .active{ 17 | background: #1ca9a430 18 | } 19 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ### GoUI 2 | #### 一套基于Vue.js2.5的ui组件库 3 | -------------------------------------------------------------------------------- /docs/components/button.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Button - 按钮 3 | --- 4 | 5 | ## Button-按钮 6 | 7 | ### 示例 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |
15 | Button 16 | Button 17 | Button 18 | Button 19 |
20 | ``` 21 | 22 | ### Attributes 23 | | 参数 | 说明 | 类型 | 默认值 | 24 | | ------ | ------ | ------ | ------ | 25 | | icon | 可设置icon | String | 无 | 26 | | icon-position | icon位置 | String | left [right]| 27 | | loading | 是否loading | Boolean | false | 28 | | disabled | 是否禁用 | Boolean | false | -------------------------------------------------------------------------------- /docs/components/cascader.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cascader - Cascader 3 | --- 4 | 5 | ## Cascader-Cascader 6 | 7 | ### 示例 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |
15 | 20 |
21 | ``` 22 | 23 | ### Attributes 24 | | 参数 | 说明 | 类型 | 默认值 | 25 | | ------ | ------ | ------ | ------ | 26 | | source | 数据源 | Array | [] | 27 | | popover-height | 下拉高度 | String | 无| 28 | | selected | 默认选中值 | Array | [] | 29 | | load-data | 加载函数 | Function | 无 | -------------------------------------------------------------------------------- /docs/components/collapse.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Collapse - 手风琴 3 | --- 4 | 5 | ## Collapse-手风琴 6 | ### 示例 7 | 8 | 9 | 10 | 11 | ### 代码 12 | ```HTML 13 | 14 | 内容1 15 | 内容2 16 | 内容3 17 | 18 |
19 |

手风琴模式

20 | 21 | 内容1 22 | 内容2 23 | 内容3 24 | 25 | ``` 26 | 27 | ### Attributes 28 | | 参数 | 说明 | 类型 | 默认值 | 29 | | ------ | ------ | ------ | ------ | 30 | | selected | 默认选中块 | Array | 无 | 31 | | single | 是否为手风琴模式 | Boolean | false | 32 | -------------------------------------------------------------------------------- /docs/components/grid.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Grid - 网格 3 | --- 4 | 5 | ## Grid-网格 6 | ### 示例 7 | 8 | 9 | 10 | 11 | ### 代码 12 | ```HTML 13 | 14 |
等分
15 |
等分
16 |
17 | 18 |
右对齐
19 |
右对齐
20 |
21 | 22 |
间隙
23 |
间隙
24 |
25 | 26 |
偏移
27 |
偏移
28 |
29 | ``` 30 | ### g-row Attributes 31 | | 参数 | 说明 | 类型 | 默认值 | 32 | | ------ | ------ | ------ | ------ | 33 | | align | 栅格对齐方式 | String | [left right center] | 34 | | gutter | 栅格间隔 | String,Number | 无| 35 | 36 | 37 | ### g-col Attributes 38 | | 参数 | 说明 | 类型 | 默认值 | 39 | | ------ | ------ | ------ | ------ | 40 | | span | 栅格占比 | String,Number | 无 | 41 | | offset | 栅格偏移差 | String,Number | 无| 42 | -------------------------------------------------------------------------------- /docs/components/icon.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Icon - 图标 3 | --- 4 | 5 | ## Icon-图标 6 | ### 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |
15 | 16 | 17 | 18 | 19 |
20 | 21 | ``` 22 | 23 | ### Attributes 24 | | 参数 | 说明 | 类型 | 默认值 | 25 | | ------ | ------ | ------ | ------ | 26 | | name | icon名称 | String | 无| 27 | -------------------------------------------------------------------------------- /docs/components/input.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Input - 输入框 3 | --- 4 | 5 | ## Input-输入框 6 | ### 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |
15 | 16 | 17 | 18 | 19 |
20 | ``` 21 | 22 | ### Attributes 23 | | 参数 | 说明 | 类型 | 默认值 | 24 | | ------ | ------ | ------ | ------ | 25 | | value | 输入框默认值 | String | 无| 26 | | disabled | 是否禁用 | Boolean | false | 27 | | readonly | 是否只读 | Boolean | false | 28 | | error | 提示信息 | String | 无 | -------------------------------------------------------------------------------- /docs/components/layout.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Layout - 布局 3 | --- 4 | 5 | ## Layout-布局 6 | ## 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 | 15 | header 16 | content 17 | footer 18 | 19 |
20 | 21 | header 22 | 23 | sider 24 | content 25 | 26 | footer 27 | 28 |
29 | 30 | sider 31 | 32 | header 33 | content 34 | footer 35 | 36 | 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/components/nav.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Nav - Nav 3 | --- 4 | 5 | ## Nav-Nav 6 | ### 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |
15 | 16 | 首页 17 | 18 | 19 | 企业文化 20 | 开发团队 21 | 22 | 23 | 微信 24 | QQ 25 | 26 | 27 | 移动 28 | 联通 29 | 电信 30 | 31 | 32 | 33 | 招聘 34 | 35 | 36 | 37 | 首页 38 | 39 | 40 | 41 | 企业文化 42 | 开发团队 43 | 44 | 45 | 微信 46 | QQ 47 | 48 | 49 | 移动 50 | 联通 51 | 电信 52 | 53 | 54 | 55 | 招聘 56 | 57 |
58 | ``` 59 | 60 | ### Attributes 61 | | 参数 | 说明 | 类型 | 默认值 | 62 | | ------ | ------ | ------ | ------ | 63 | | selected | 默认展示nav | String | 无| 64 | | vertical | nav方向是否vertical | Boolean | false | 65 | -------------------------------------------------------------------------------- /docs/components/pager.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pager - 页码 3 | --- 4 | 5 | ## Pager - 页码 6 | ### 示例 7 | 8 | 9 | 10 | 11 | ### 代码 12 | ```HTML 13 | 14 | ``` 15 | 16 | 17 | 18 | 19 | ### Attributes 20 | | 参数 | 说明 | 类型 | 默认值 | 21 | | ------ | ------ | ------ | ------ | 22 | | totalPage | 总条数(必填) | Number | 无| 23 | | currentPage | 当前页数(必填) | Number | 无 | 24 | 25 | -------------------------------------------------------------------------------- /docs/components/popover.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Popover - 弹出层 3 | --- 4 | 5 | ## Popover-弹出层 6 | ### 示例 7 | 8 | 9 | 10 | 11 | ### 代码 12 | ```HTML 13 | 14 | 17 | 20 | 21 |
22 | 23 | 26 | 29 | 30 | ``` 31 | 32 | 33 | 34 | 35 | ### Attributes 36 | | 参数 | 说明 | 类型 | 默认值 | 37 | | ------ | ------ | ------ | ------ | 38 | | trigger | 触发方式 | String | click[hover]| 39 | | position | 触发方位 | String | top[left bottom right] | 40 | -------------------------------------------------------------------------------- /docs/components/slides.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Slides - Slides 3 | --- 4 | 5 | ## Slides-Slides 6 | ### 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |
15 | 16 | 17 |
1
18 |
19 | 20 |
2
21 |
22 | 23 |
3
24 |
25 |
26 |
27 | ``` 28 | 29 | ### Attributes 30 | | 参数 | 说明 | 类型 | 默认值 | 31 | | ------ | ------ | ------ | ------ | 32 | | selected | 默认展示第几个,name值 | String | 第一个 | 33 | | autoPlay | 是否自动播放 | Boolean | true | 34 | -------------------------------------------------------------------------------- /docs/components/table.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Table - Table 3 | --- 4 | 5 | ## Table-Table 6 | ### 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |
15 |

基础用法

16 |
17 | 20 |
21 |

排序

22 |
23 | 27 |
28 |

复选框

29 |
30 | 35 |
36 |

loading

37 | Loading 38 |
39 | 43 |
44 |

固定表头

45 |
46 | 51 |
52 |
53 | ``` 54 | 55 | ### Attributes 56 | | 参数 | 说明 | 类型 | 默认值 | 57 | | ------ | ------ | ------ | ------ | 58 | | height | 表格高度 | Number | 无| 59 | | columns | 表头字段(必填,必须指定宽度)| Array | 无 | 60 | | dataSource | 表格数据 | Array | 无 | 61 | | order-by| 是否排序 | Boolean | false| 62 | | checkable | 是否需要复选框 | Boolean | false | 63 | | loading | 是否显示加载loading | Boolean | false| 64 | | fixed-header | 是否固定表头 | Boolean | false | 65 | -------------------------------------------------------------------------------- /docs/components/tabs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Tabs - 选项卡 3 | --- 4 | 5 | # 选项卡 6 | ## 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |

通过selected设置默认选项,disabled设置禁用选项, 默认方向为horizontal

15 | 16 | 17 | 选项一 18 | 选项二 19 | 选项三 20 | 21 | 22 | 第一个选项内容 23 | 第二个选项内容 24 | 第三个选项内容 25 | 26 | 27 |
28 |

vertical方向

29 | 30 | 31 | 选项一 32 | 选项二 33 | 选项三 34 | 35 | 36 | 第一个选项内容 37 | 第二个选项内容 38 | 第三个选项内容 39 | 40 | 41 | ``` 42 | 43 | ### Attributes 44 | | 参数 | 说明 | 类型 | 默认值 | 45 | | ------ | ------ | ------ | ------ | 46 | | selected | 默认选中块(必填) | String | 无 | 47 | | direction | tab方向 | String | horizontal[vertical] | -------------------------------------------------------------------------------- /docs/components/toast.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Toast - Toast 3 | --- 4 | 5 | ## Toast-toast 6 | ### 示例 7 | 8 | 9 | 10 | 11 | 12 | ### 代码 13 | ```HTML 14 |

autoClose属性控制是否自动关闭

15 | 默认 16 | 17 |

closeButton属性控制自定义关闭

18 | 可关闭 19 | 20 |

enableHtml属性控制是否转义html

21 | 展示html 22 | 23 |

position属性控制位置(top,middle,bottom)

24 | 居中 25 | ``` 26 | ```js 27 | showToast() { 28 | this.$toast('这是一段默认2秒自动关闭的提示', { 29 | autoClose: 2 30 | }) 31 | }, 32 | showToast2() { 33 | this.$toast('这是一段手动关闭的提示', { 34 | autoClose: false, 35 | closeButton: { 36 | text: '关闭文字', 37 | callback() { 38 | console.log('关闭成功') 39 | } 40 | } 41 | }) 42 | }, 43 | showToast3() { 44 | this.$toast('

这是一个h3标题

', { 45 | enableHtml: true, 46 | autoClose: 2, 47 | }) 48 | }, 49 | showToast4() { 50 | this.$toast('这是一条居中提示', { 51 | autoClose: 2, 52 | position: 'middle' 53 | }) 54 | } 55 | } 56 | ``` 57 | 58 | 59 | 60 | 61 | ### Attributes 62 | | 参数 | 说明 | 类型 | 默认值 | 63 | | ------ | ------ | ------ | ------ | 64 | | autoClose | 是否自动关闭,若为number值,则为关闭时间 | String, Number | true | 65 | | closeButton | 是否有关闭按钮,可自定义关闭文字和回调 | Object | 关闭(按钮文字) | 66 | | enableHtml |是否允许插入html|Boolean|false| 67 | | position | toasti显示位置 | String | top[middle bottom] 68 | 69 | -------------------------------------------------------------------------------- /docs/get-started/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 介绍 3 | --- 4 | ## 介绍 5 | 此项目为巩固vue用法和深入理解组件化而开发。 6 | 7 | ## 项目特点 8 | * 项目参考了ElementUI,AntdDesign等UI库的UI设计以及API设计; 9 | * 深入使用了Vue,如依赖注入,.sync,单向数据流等; 10 | * 未完待更新...; 11 | -------------------------------------------------------------------------------- /docs/install/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 安装 3 | --- 4 | 5 | # 安装 -------------------------------------------------------------------------------- /karma.config.js: -------------------------------------------------------------------------------- 1 | var webpackConfig = require("@vue/cli-service/webpack.config.js"); 2 | const path = require("path"); 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | frameworks: ["mocha"], 7 | 8 | files: ["tests/**/*.spec.js"], 9 | 10 | preprocessors: { 11 | "**/*.spec.js": ["webpack", "sourcemap"] 12 | }, 13 | 14 | webpack: webpackConfig, 15 | 16 | reporters: ["spec", "coverage"], 17 | coverageReporter: { 18 | dir: "./coverage", 19 | reporters: [{ type: "lcov", subdir: "." }, { type: "text-summary" }] 20 | }, 21 | autoWatch: true, 22 | 23 | browsers: ["ChromeHeadless"] 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-vue-1", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test": "karma start --single-run", 9 | "docs:dev": "vuepress dev docs", 10 | "docs:build": "vuepress build docs", 11 | "test:unit": "karma start", 12 | "test:unit:old": "vue-cli-service test:unit" 13 | }, 14 | "dependencies": { 15 | "core-js": "^2.6.5", 16 | "vue": "^2.6.10" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "^3.0.1", 20 | "@vue/cli-plugin-unit-mocha": "^3.0.1", 21 | "@vue/cli-service": "^3.0.1", 22 | "@vue/test-utils": "1.0.0-beta.29", 23 | "chai": "^4.2.0", 24 | "karma": "^4.2.0", 25 | "karma-chrome-launcher": "^3.1.0", 26 | "karma-mocha": "^1.3.0", 27 | "karma-sourcemap-loader": "^0.3.7", 28 | "karma-spec-reporter": "^0.0.32", 29 | "karma-webpack": "^4.0.2", 30 | "node-sass": "^4.9.0", 31 | "sass-loader": "^7.1.0", 32 | "sinon": "^7.4.1", 33 | "sinon-chai": "^3.3.0", 34 | "vue-template-compiler": "^2.6.10", 35 | "vuepress": "^1.0.3" 36 | }, 37 | "postcss": { 38 | "plugins": { 39 | "autoprefixer": {} 40 | } 41 | }, 42 | "browserslist": [ 43 | "> 1%", 44 | "last 2 versions" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Button from "./button/button"; 3 | import Icon from "./icon"; 4 | import ButtonGroup from "./button/button-group"; 5 | import Input from "./input/input"; 6 | import Row from "./grid/row"; 7 | import Col from "./grid/col"; 8 | import Layout from "./layout/layout"; 9 | import Content from "./layout/content"; 10 | import Header from "./layout/header"; 11 | import Footer from "./layout/footer"; 12 | import Sider from "./layout/sider"; 13 | 14 | import plugin from "./plugin"; 15 | 16 | import Tabs from "./tabs/tabs"; 17 | import TabsHead from "./tabs/tabs-head"; 18 | import TabsBody from "./tabs/tabs-body"; 19 | import TabsItem from "./tabs/tabs-item"; 20 | import TabsPane from "./tabs/tabs-pane"; 21 | 22 | import Popover from "./popover/popover"; 23 | import Collapse from "./collapse/collapse"; 24 | import CollapseItem from "./collapse/collapse-item"; 25 | import Cascader from "./cascader/cascader"; 26 | 27 | Vue.component("g-button", Button); 28 | Vue.component("g-button-group", ButtonGroup); 29 | Vue.component("g-cascader", Cascader); 30 | Vue.component("g-col", Col); 31 | Vue.component("g-collapse", Collapse); 32 | Vue.component("g-collapse-item", CollapseItem); 33 | Vue.component("g-content", Content); 34 | Vue.component("g-footer", Footer); 35 | Vue.component("g-header", Header); 36 | Vue.component("g-icon", Icon); 37 | Vue.component("g-input", Input); 38 | Vue.component("g-layout", Layout); 39 | Vue.component("g-popover", Popover); 40 | Vue.component("g-row", Row); 41 | Vue.component("g-sider", Sider); 42 | Vue.component("g-tabs", Tabs); 43 | Vue.component("g-tabs-body", TabsBody); 44 | Vue.component("g-tabs-head", TabsHead); 45 | Vue.component("g-tabs-item", TabsItem); 46 | Vue.component("g-tabs-pane", TabsPane); 47 | Vue.use(plugin); 48 | 49 | new Vue({ 50 | el: "#app", 51 | data: { 52 | selectedTab: "first", 53 | source: [ 54 | { 55 | name: "江苏省", 56 | children: [ 57 | { 58 | name: "南通市", 59 | children: [{ name: "开发区" }, { name: "崇川区" }] 60 | }, 61 | { 62 | name: "宿迁市", 63 | children: [{ name: "宿迁区" }] 64 | } 65 | ] 66 | }, 67 | { 68 | name: "浙江省", 69 | children: [ 70 | { 71 | name: "湖州市", 72 | children: [{ name: "湖州区" }, { name: "湖州2区" }] 73 | }, 74 | { 75 | name: "杭州市", 76 | children: [{ name: "杭州一区" }, { name: "杭州二区" }] 77 | } 78 | ] 79 | } 80 | ] 81 | }, 82 | methods: { 83 | yyy(data) { 84 | console.log(data); 85 | }, 86 | showToast() { 87 | this.$toast("加油", { 88 | enableHtml: true, 89 | position: "top", 90 | autoClose: false 91 | }); 92 | }, 93 | showToast2() { 94 | this.$toast("加油", { 95 | enableHtml: true, 96 | position: "middle", 97 | autoClose: false 98 | }); 99 | }, 100 | showToast3() { 101 | this.$toast("加油", { 102 | enableHtml: true, 103 | position: "bottom", 104 | autoClose: 2 105 | }); 106 | } 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /src/button/button-group.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | -------------------------------------------------------------------------------- /src/button/button.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | 44 | 95 | -------------------------------------------------------------------------------- /src/cascader/cascader-items.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 97 | 98 | 139 | -------------------------------------------------------------------------------- /src/cascader/cascader.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 122 | 123 | 150 | -------------------------------------------------------------------------------- /src/cascader/demo.vue: -------------------------------------------------------------------------------- 1 | 9 | 52 | -------------------------------------------------------------------------------- /src/click-outside.js: -------------------------------------------------------------------------------- 1 | let onClickDocument = e => { 2 | let { target } = e; 3 | callbacks.forEach(item => { 4 | if (target === item.el || item.el.contains(target)) { 5 | return; 6 | } else { 7 | item.callback(); 8 | } 9 | }); 10 | }; 11 | document.addEventListener("click", onClickDocument); 12 | let callbacks = []; 13 | export default { 14 | bind: function(el, binding, vnode) { 15 | callbacks.push({ el, callback: binding.value }); 16 | } 17 | }; 18 | 19 | let removeListener = () => { 20 | document.removeEventListener("click", onClickDocument); 21 | }; 22 | 23 | export { removeListener }; 24 | -------------------------------------------------------------------------------- /src/collapse/collapse-item.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 51 | 52 | -------------------------------------------------------------------------------- /src/collapse/collapse.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 53 | 54 | -------------------------------------------------------------------------------- /src/demo.vue: -------------------------------------------------------------------------------- 1 | 65 | 78 | 85 | -------------------------------------------------------------------------------- /src/grid/col.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 90 | 91 | -------------------------------------------------------------------------------- /src/grid/row.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 42 | 43 | -------------------------------------------------------------------------------- /src/icon.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | 19 | -------------------------------------------------------------------------------- /src/icon/icon.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | 16 | -------------------------------------------------------------------------------- /src/input/input.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 43 | 44 | 84 | -------------------------------------------------------------------------------- /src/layout/content.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/layout/footer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/layout/header.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/layout/layout.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 26 | 27 | -------------------------------------------------------------------------------- /src/layout/sider.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | // import Demo from "./demo.vue"; 3 | // import PagerDemo from "./pager/demo"; 4 | // import TableDemo from "./table/demo"; 5 | import CascaderDemo from './cascader/demo' 6 | 7 | Vue.config.productionTip = false; 8 | 9 | new Vue({ 10 | render: h => h(CascaderDemo) 11 | }).$mount("#app"); 12 | -------------------------------------------------------------------------------- /src/nav/nav-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 34 | 35 | 68 | -------------------------------------------------------------------------------- /src/nav/nav.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 75 | 76 | 90 | -------------------------------------------------------------------------------- /src/nav/sub-nav.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 99 | 100 | 181 | -------------------------------------------------------------------------------- /src/pager/demo.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/pager/pager.vue: -------------------------------------------------------------------------------- 1 | 34 | 97 | 157 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | import Toast from './toast/toast' 2 | 3 | 4 | // 提取函数 5 | function createToast({Vue, message, propsData}) { 6 | // 生成一个toast组件放到body里面 7 | let Construstor = Vue.extend(Toast) 8 | let toast = new Construstor({propsData}) 9 | // 把信息给到插槽 10 | toast.$slots.default = [message] 11 | toast.$mount() 12 | document.body.appendChild(toast.$el) 13 | return toast 14 | } 15 | // 设置标记变量 16 | let currentToast 17 | export default { 18 | install (Vue, options) { 19 | Vue.prototype.$toast = function (message, toastOptions) { 20 | // 如果存在,把之前的销毁 21 | if (currentToast) { 22 | currentToast.close() 23 | } 24 | currentToast = createToast({Vue, message, propsData: toastOptions}) 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/popover/popover.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 142 | 143 | 243 | -------------------------------------------------------------------------------- /src/slides/slides-item.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 44 | 45 | 72 | -------------------------------------------------------------------------------- /src/slides/slides.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 177 | 178 | 216 | -------------------------------------------------------------------------------- /src/svg.js: -------------------------------------------------------------------------------- 1 | !function (s) { 2 | var script = document.getElementsByTagName("script"); 3 | if (!script || Object.keys(script).length === 0) { 4 | return; 5 | } 6 | var t, 7 | e = '', 8 | c = (t = document.getElementsByTagName("script"))[t.length - 1].getAttribute("data-injectcss"); 9 | if (c && !s.__iconfont__svg__cssinject__) { 10 | s.__iconfont__svg__cssinject__ = !0; 11 | try { 12 | document.write("") 13 | } catch (t) { 14 | console && console.log(t) 15 | } 16 | } 17 | !function (t) { 18 | if (document.addEventListener) if (~["complete", "loaded", "interactive"].indexOf(document.readyState)) setTimeout(t, 0); else { 19 | var c = function () { 20 | document.removeEventListener("DOMContentLoaded", c, !1), t() 21 | }; 22 | document.addEventListener("DOMContentLoaded", c, !1) 23 | } else document.attachEvent && (o = t, l = s.document, i = !1, (a = function () { 24 | try { 25 | l.documentElement.doScroll("left") 26 | } catch (t) { 27 | return void setTimeout(a, 50) 28 | } 29 | e() 30 | })(), l.onreadystatechange = function () { 31 | "complete" == l.readyState && (l.onreadystatechange = null, e()) 32 | }); 33 | 34 | function e() { 35 | i || (i = !0, o()) 36 | } 37 | 38 | var o, l, i, a 39 | }(function () { 40 | var t, c; 41 | (t = document.createElement("div")).innerHTML = e, e = null, (c = t.getElementsByTagName("svg")[0]) && (c.setAttribute("aria-hidden", "true"), c.style.position = "absolute", c.style.width = 0, c.style.height = 0, c.style.overflow = "hidden", function (t, c) { 42 | c.firstChild ? function (t, c) { 43 | c.parentNode.insertBefore(t, c) 44 | }(t, c.firstChild) : c.appendChild(t) 45 | }(c, document.body)) 46 | }) 47 | }(window); -------------------------------------------------------------------------------- /src/table/demo.vue: -------------------------------------------------------------------------------- 1 | 18 | 58 | 66 | -------------------------------------------------------------------------------- /src/table/table.vue: -------------------------------------------------------------------------------- 1 | 48 | 203 | -------------------------------------------------------------------------------- /src/tabs/tabs-body.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | -------------------------------------------------------------------------------- /src/tabs/tabs-head.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 34 | 35 | -------------------------------------------------------------------------------- /src/tabs/tabs-item.vue: -------------------------------------------------------------------------------- 1 | 6 | 50 | -------------------------------------------------------------------------------- /src/tabs/tabs-pane.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 38 | 39 | -------------------------------------------------------------------------------- /src/tabs/tabs.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 60 | 61 | -------------------------------------------------------------------------------- /src/toast/toast.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 85 | 86 | 158 | -------------------------------------------------------------------------------- /src/validate.js: -------------------------------------------------------------------------------- 1 | class Validator { 2 | // 自行添加 3 | static add(name, fn) { 4 | Validator.prototype[name] = fn; 5 | } 6 | 7 | constructor() {} 8 | validate(data, rules) { 9 | let errors = {}; 10 | rules.forEach(rule => { 11 | let value = data[rule.key]; 12 | if (rule.required) { 13 | let error = this.required(value); 14 | if (error) { 15 | ensureObject(errors, rule.key); 16 | errors[rule.key].required = error; 17 | return; 18 | } 19 | } 20 | let validators = Object.keys(rule).filter( 21 | key => key !== "key" && key !== "required" 22 | ); 23 | validators.forEach(validatorKey => { 24 | if (this[validatorKey]) { 25 | let error = this[validatorKey](value, rule[validatorKey]); 26 | if (error) { 27 | ensureObject(errors, rule.key); 28 | errors[rule.key][validatorKey] = error; 29 | } 30 | } else { 31 | throw `不存在的校验器: ${validatorKey}`; 32 | } 33 | }); 34 | }); 35 | return errors; 36 | } 37 | 38 | required(value) { 39 | if (value !== 0 && !value) { 40 | return "必填"; 41 | } 42 | } 43 | 44 | pattern(value, pattern) { 45 | if (pattern === "email") { 46 | pattern = /^.+@.+$/; 47 | } 48 | if (pattern.test(value) === false) { 49 | return "格式不正确"; 50 | } 51 | } 52 | 53 | minLength(value, minLength) { 54 | if (value.length < minLength) { 55 | return "太短"; 56 | } 57 | } 58 | 59 | maxLength(value, maxLength) { 60 | if (value.length > maxLength) { 61 | return "太长"; 62 | } 63 | } 64 | } 65 | 66 | function ensureObject(obj, key) { 67 | if (typeof obj[key] !== "object") { 68 | obj[key] = {}; 69 | } 70 | } 71 | 72 | export default Validator; 73 | -------------------------------------------------------------------------------- /styles/var.scss: -------------------------------------------------------------------------------- 1 | $border-color-hover: #666; 2 | $border-color: #999; 3 | $border-color-light: lighten($border-color, 30%); 4 | $border-radius: 4px; 5 | $box-shadow-color: rgba(0, 0, 0, 0.5); 6 | $button-active-bg: #eee; 7 | $button-bg: white; 8 | $button-height: 32px; 9 | $color: #333; 10 | $light-color: #666; 11 | $font-size: 14px; 12 | $small-font-size: 12px; 13 | $input-height: 32px; 14 | $red: #f1453d; 15 | $grey: #eee; 16 | $blue: #4a90e2; 17 | .box-shadow { 18 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.15); 19 | } 20 | @keyframes spin { 21 | 0% { 22 | transform: rotate(0deg); 23 | } 24 | 100% { 25 | transform: rotate(360deg); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/unit/button.spec.js: -------------------------------------------------------------------------------- 1 | import chai, { expect } from "chai"; 2 | import sinon from "sinon"; 3 | import sinonChai from "sinon-chai"; 4 | import { shallowMount, mount } from "@vue/test-utils"; 5 | import Button from "../../src/button/button.vue"; 6 | chai.use(sinonChai); 7 | 8 | describe("Button.vue", () => { 9 | it("存在.", () => { 10 | expect(Button).to.exist; 11 | }); 12 | it("可以设置icon.", () => { 13 | const wrapper = mount(Button, { 14 | propsData: { 15 | icon: "settings" 16 | } 17 | }); 18 | const useElement = wrapper.find("use"); 19 | expect(useElement.attributes()["href"]).to.equal("#i-settings"); 20 | }); 21 | it("可以设置loading.", () => { 22 | const wrapper = mount(Button, { 23 | propsData: { 24 | icon: "settings", 25 | loading: true 26 | } 27 | }); 28 | const vm = wrapper.vm; 29 | const useElements = vm.$el.querySelectorAll("use"); 30 | expect(useElements.length).to.equal(1); 31 | expect(useElements[0].getAttribute("xlink:href")).to.equal("#i-loading"); 32 | }); 33 | 34 | xit("icon 默认的 order 是 1", () => { 35 | const wrapper = mount(Button, { 36 | propsData: { 37 | icon: "settings" 38 | } 39 | }); 40 | const vm = wrapper.vm; 41 | const icon = vm.$el.querySelector("svg"); 42 | expect(getComputedStyle(icon).order).to.eq("1"); 43 | }); 44 | 45 | xit("设置 iconPosition 可以改变 order", () => { 46 | const wrapper = mount(Butotn, { 47 | propsData: { 48 | icon: "settings", 49 | iconPosition: "right" 50 | } 51 | }); 52 | const vm = wrapper.vm; 53 | const icon = vm.$el.querySelector("svg"); 54 | expect(getComputedStyle(icon).order).to.eq("2"); 55 | }); 56 | it("点击 button 触发 click 事件", () => { 57 | const wrapper = mount(Button, { 58 | propsData: { 59 | icon: "settings" 60 | } 61 | }); 62 | const vm = wrapper.vm; 63 | 64 | const callback = sinon.fake(); 65 | vm.$on("click", callback); 66 | vm.$el.click(); 67 | expect(callback).to.have.been.called; 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /tests/unit/button.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Button from '../../src/button/button' 4 | 5 | Vue.config.productionTip = false 6 | Vue.config.devtools = false 7 | 8 | describe('Button', () => { 9 | it('存在.', () => { 10 | expect(Button).to.be.ok 11 | }) 12 | it('可以设置icon.', () => { 13 | const Constructor = Vue.extend(Button) 14 | // 给它传一个数据 15 | const vm = new Constructor({ 16 | propsData: { 17 | icon: 'settings' 18 | } 19 | }).$mount() 20 | const useElement = vm.$el.querySelector('use') 21 | // 查看这个属性,期待它等于这个值 22 | expect(useElement.getAttribute('xlink:href')).to.equal('#i-settings') 23 | vm.$destroy() 24 | }) 25 | it('可以设置loading.', () => { 26 | const Constructor = Vue.extend(Button) 27 | const vm = new Constructor({ 28 | propsData: { 29 | icon: 'settings', 30 | loading: true 31 | } 32 | }).$mount() 33 | const useElements = vm.$el.querySelectorAll('use') 34 | expect(useElements.length).to.equal(1) 35 | expect(useElements[0].getAttribute('xlink:href')).to.equal('#i-loading') 36 | vm.$destroy() 37 | }) 38 | it('icon 默认的 order 是 1', () => { 39 | const div = document.createElement('div') 40 | document.body.appendChild(div) 41 | const Constructor = Vue.extend(Button) 42 | const vm = new Constructor({ 43 | propsData: { 44 | icon: 'settings', 45 | } 46 | }).$mount(div) 47 | const icon = vm.$el.querySelector('svg') 48 | expect(getComputedStyle(icon).order).to.eq('1') 49 | vm.$el.remove() 50 | vm.$destroy() 51 | }) 52 | it('设置 iconPosition 可以改变 order', () => { 53 | const div = document.createElement('div') 54 | document.body.appendChild(div) 55 | const Constructor = Vue.extend(Button) 56 | const vm = new Constructor({ 57 | propsData: { 58 | icon: 'settings', 59 | iconPosition: 'right' 60 | } 61 | }).$mount(div) 62 | const icon = vm.$el.querySelector('svg') 63 | expect(getComputedStyle(icon).order).to.eq('2') 64 | vm.$el.remove() 65 | vm.$destroy() 66 | }) 67 | it('点击 button 触发 click 事件', () => { 68 | const Constructor = Vue.extend(Button) 69 | const vm = new Constructor({ 70 | propsData: { 71 | icon: 'settings', 72 | } 73 | }).$mount() 74 | const callback = sinon.fake(); 75 | vm.$on('click', callback) 76 | // 找到这个元素,调用它的点击事件 77 | vm.$el.click() 78 | // 期待回调 79 | expect(callback).to.have.been.called 80 | }) 81 | }) 82 | -------------------------------------------------------------------------------- /tests/unit/col.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Row from '../../src/grid/row' 4 | import Col from '../../src/grid/col' 5 | 6 | 7 | Vue.config.productionTip = false 8 | Vue.config.devtools = false 9 | 10 | describe('Col', () => { 11 | it('存在.', () => { 12 | expect(Col).to.be.ok 13 | }) 14 | it('接受span属性', () => { 15 | // 必须放到页面上 16 | const div = document.createElement('div') 17 | document.body.appendChild(div) 18 | const Constructor = Vue.extend(Col) 19 | const vm = new Constructor({ 20 | propsData: { 21 | span: 1 22 | } 23 | }).$mount(div) 24 | // console.log(vm.$el) 25 | expect(vm.$el.classList.contains('col-1')).to.eq(true) 26 | div.remove() 27 | vm.$destroy() 28 | }) 29 | it('接受offset属性', () => { 30 | const div = document.createElement('div') 31 | document.body.appendChild(div) 32 | const Constructor = Vue.extend(Col) 33 | const vm = new Constructor({ 34 | propsData: { 35 | offset: 1 36 | } 37 | }).$mount(div) 38 | console.log(vm.$el) 39 | expect(vm.$el.classList.contains('offset-1')).to.eq(true) 40 | div.remove() 41 | vm.$destroy() 42 | }) 43 | it('接受ipad属性', () => { 44 | const div = document.createElement('div') 45 | document.body.appendChild(div) 46 | const Constructor = Vue.extend(Col) 47 | const vm = new Constructor({ 48 | propsData: { 49 | ipad: {span: 1, offset: 2} 50 | } 51 | }).$mount(div) 52 | console.log(vm.$el) 53 | expect(vm.$el.classList.contains('col-ipad-1')).to.eq(true) 54 | expect(vm.$el.classList.contains('offset-ipad-2')).to.eq(true) 55 | div.remove() 56 | vm.$destroy() 57 | }) 58 | it('接受narrowPc属性', () => { 59 | const div = document.createElement('div') 60 | document.body.appendChild(div) 61 | const Constructor = Vue.extend(Col) 62 | const vm = new Constructor({ 63 | propsData: { 64 | narrowPc: {span: 1, offset: 2} 65 | } 66 | }).$mount(div) 67 | console.log(vm.$el) 68 | expect(vm.$el.classList.contains('col-narrow-pc-1')).to.eq(true) 69 | expect(vm.$el.classList.contains('offset-narrow-pc-2')).to.eq(true) 70 | div.remove() 71 | vm.$destroy() 72 | }) 73 | it('接受pc属性', () => { 74 | const div = document.createElement('div') 75 | document.body.appendChild(div) 76 | const Constructor = Vue.extend(Col) 77 | const vm = new Constructor({ 78 | propsData: { 79 | pc: {span: 1, offset: 2} 80 | } 81 | }).$mount(div) 82 | console.log(vm.$el) 83 | expect(vm.$el.classList.contains('col-pc-1')).to.eq(true) 84 | expect(vm.$el.classList.contains('offset-pc-2')).to.eq(true) 85 | div.remove() 86 | vm.$destroy() 87 | }) 88 | it('接受widePc属性', () => { 89 | const div = document.createElement('div') 90 | document.body.appendChild(div) 91 | const Constructor = Vue.extend(Col) 92 | const vm = new Constructor({ 93 | propsData: { 94 | widePc: {span: 1, offset: 2} 95 | } 96 | }).$mount(div) 97 | console.log(vm.$el) 98 | expect(vm.$el.classList.contains('col-wide-pc-1')).to.eq(true) 99 | expect(vm.$el.classList.contains('offset-wide-pc-2')).to.eq(true) 100 | div.remove() 101 | vm.$destroy() 102 | }) 103 | }) 104 | -------------------------------------------------------------------------------- /tests/unit/input.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Input from '../../src/input/input' 4 | 5 | Vue.config.productionTip = false 6 | Vue.config.devtools = false 7 | 8 | describe('Input', () => { 9 | it('存在.', () => { 10 | expect(Input).to.be.ok 11 | }) 12 | // 测试props 13 | describe('props', () => { 14 | const Constructor = Vue.extend(Input) 15 | let vm 16 | afterEach(function () { 17 | vm.$destroy() 18 | }) 19 | it('可以接受value', () => { 20 | // 给它传一个数据 21 | vm = new Constructor({ 22 | propsData: { 23 | value: '1234' 24 | } 25 | }).$mount() 26 | const inputElement = vm.$el.querySelector('input') 27 | // 查看这个属性,期待它等于这个值 28 | expect(inputElement.value).to.equal('1234') 29 | }) 30 | it('可以接受disabled', () => { 31 | // 给它传一个数据 32 | vm = new Constructor({ 33 | propsData: { 34 | disabled: true 35 | } 36 | }).$mount() 37 | const inputElement = vm.$el.querySelector('input') 38 | // 查看这个属性,期待它等于这个值 39 | expect(inputElement.disabled).to.equal(true) 40 | }) 41 | it('可以接受readonly', () => { 42 | // 给它传一个数据 43 | vm = new Constructor({ 44 | propsData: { 45 | readonly: true 46 | } 47 | }).$mount() 48 | const inputElement = vm.$el.querySelector('input') 49 | // 查看这个属性,期待它等于这个值 50 | expect(inputElement.readOnly).to.equal(true) 51 | }) 52 | it('可以接受error', () => { 53 | // 给它传一个数据 54 | vm = new Constructor({ 55 | propsData: { 56 | error: '最大值超过2' 57 | } 58 | }).$mount() 59 | const useElement = vm.$el.querySelector('use') 60 | // 查看这个属性,期待它等于这个值 61 | expect(useElement.getAttribute('xlink:href')).to.equal('#i-error') 62 | const errorMesage = vm.$el.querySelector('.error-message') 63 | // 查看这个属性,期待它等于这个值 64 | expect(errorMesage.innerText).to.equal("最大值超过2") 65 | }) 66 | }) 67 | // 测试input事件 68 | describe('事件', () => { 69 | const Constructor = Vue.extend(Input) 70 | let vm 71 | afterEach(function () { 72 | vm.$destroy() 73 | }) 74 | it('支持change/input/focus/blur事件', () => { 75 | ['change','input','focus','blur'].forEach((eventName) => { 76 | vm = new Constructor({}).$mount() 77 | const callback = sinon.fake(); 78 | vm.$on(eventName, callback) 79 | // 触发input的change事件 80 | let event = new Event(eventName) 81 | Object.defineProperty( 82 | event, 'target', { 83 | value: {value: 'hi'}, enumerable: true 84 | } 85 | ) 86 | let inputElement = vm.$el.querySelector('input') 87 | inputElement.dispatchEvent(event) 88 | expect(callback).to.have.been.calledWith('hi') 89 | }) 90 | }) 91 | // it('支持input事件', () => { 92 | // vm = new Constructor({}).$mount() 93 | // const callback = sinon.fake(); 94 | // vm.$on('input', callback) 95 | // // 触发input的change事件 96 | // let event = new Event('input') 97 | // let inputElement = vm.$el.querySelector('input') 98 | // inputElement.dispatchEvent(event) 99 | // expect(callback).to.have.been.calledWith(event) 100 | // }) 101 | // it('支持focus事件', () => { 102 | // vm = new Constructor({}).$mount() 103 | // const callback = sinon.fake(); 104 | // vm.$on('focus', callback) 105 | // // 触发input的change事件 106 | // let event = new Event('focus') 107 | // let inputElement = vm.$el.querySelector('input') 108 | // inputElement.dispatchEvent(event) 109 | // expect(callback).to.have.been.calledWith(event) 110 | // }) 111 | // it('支持blur事件', () => { 112 | // vm = new Constructor({}).$mount() 113 | // const callback = sinon.fake(); 114 | // vm.$on('blur', callback) 115 | // // 触发input的change事件 116 | // let event = new Event('blur') 117 | // let inputElement = vm.$el.querySelector('input') 118 | // inputElement.dispatchEvent(event) 119 | // expect(callback).to.have.been.calledWith(event) 120 | // }) 121 | }) 122 | }) 123 | -------------------------------------------------------------------------------- /tests/unit/nav.spec.js: -------------------------------------------------------------------------------- 1 | import chai, { expect } from "chai"; 2 | import sinon from "sinon"; 3 | import sinonChai from "sinon-chai"; 4 | import { shallowMount, mount } from "@vue/test-utils"; 5 | import Nav from "../../src/nav/nav"; 6 | import NavItem from "../../src/nav/nav-item"; 7 | import SubNav from "../../src/nav/sub-nav"; 8 | import Vue from "vue"; 9 | 10 | chai.use(sinonChai); 11 | 12 | describe("nav.vue", () => { 13 | it("存在.", () => { 14 | expect(Nav).to.exist; 15 | }); 16 | it("支持selected属性", () => { 17 | Vue.component("g-nav-item", NavItem); 18 | Vue.component("g-sub-nav", SubNav); 19 | const wrapper = mount(Nav, { 20 | propsData: { 21 | selected: "home" 22 | }, 23 | slots: { 24 | default: ` 25 | 首页 26 | 27 | 30 | 企业文化 31 | 开发团队 32 | 33 | 36 | 微信 37 | QQ 38 | 39 | 40 | 43 | 联通 44 | 移动 45 | 46 | 47 | 48 | 招聘` 49 | } 50 | }); 51 | setTimeout(() => { 52 | expect(wrapper.find('[data-name="home"].selected').exists()).to.be.true; 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /tests/unit/pager.spec.js: -------------------------------------------------------------------------------- 1 | import chai, {expect} from 'chai' 2 | import sinon from 'sinon' 3 | import sinonChai from 'sinon-chai' 4 | import {shallowMount, mount} from '@vue/test-utils' 5 | import Pager from '../../src/pager/pager.vue' 6 | chai.use(sinonChai) 7 | 8 | describe('Pager.vue', () => { 9 | it('存在.', () => { 10 | expect(Pager).to.exist 11 | }) 12 | it('它可以设置currentPage', () => { 13 | const wrapper = mount(Pager, { 14 | propsData: { 15 | totalPage: 8, 16 | currentPage:3, 17 | } 18 | }) 19 | expect(wrapper.find('[data-number="3"].current').exists()).to.be.true 20 | }) 21 | 22 | it('它可以设置totalPage', () => { 23 | const wrapper = mount(Pager, { 24 | propsData: { 25 | totalPage: 8, 26 | currentPage:8, 27 | } 28 | }) 29 | expect(wrapper.find('.disabled').exists()).to.be.true 30 | }) 31 | 32 | it('它可以设置hide pager', () => { 33 | const wrapper = mount(Pager, { 34 | propsData: { 35 | totalPage: 1, 36 | currentPage:1, 37 | hideIfOnePage:false 38 | } 39 | }) 40 | expect(wrapper.find('.g-pager')).to.be.not.true 41 | }) 42 | 43 | }) -------------------------------------------------------------------------------- /tests/unit/popover.spec.js: -------------------------------------------------------------------------------- 1 | import chai, {expect} from 'chai' 2 | import sinon from 'sinon' 3 | import sinonChai from 'sinon-chai' 4 | import {shallowMount, mount} from '@vue/test-utils' 5 | import Popover from '../../src/popover/popover' 6 | 7 | chai.use(sinonChai) 8 | 9 | describe('Popover', () => { 10 | 11 | it('存在.', () => { 12 | expect(Popover).to.exist 13 | }) 14 | 15 | it('可以设置position.', () => { 16 | const wrapper = mount(Popover, { 17 | slots: { 18 | default: {template: ``}, 19 | content: '
弹出内容
' 20 | }, 21 | propsData: { 22 | position: 'bottom' 23 | } 24 | }) 25 | wrapper.find('button').trigger('click') 26 | let classes = wrapper.find('.content-wrapper').classes() 27 | expect(classes).to.include('position-bottom') 28 | }) 29 | it('可以设置 trigger', () => { 30 | const wrapper = mount(Popover, { 31 | slots: { 32 | default: {template: ``}, 33 | content: '
弹出内容
' 34 | }, 35 | propsData: { 36 | position: 'bottom', 37 | trigger: 'hover' 38 | } 39 | }) 40 | expect(wrapper.find('.content-wrapper').element).to.not.exist 41 | wrapper.find('.popover').trigger('mouseenter') 42 | expect(wrapper.find('.content-wrapper').element).to.exist 43 | 44 | }) 45 | 46 | }) -------------------------------------------------------------------------------- /tests/unit/row.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Row from '../../src/grid/row' 4 | import Col from '../../src/grid/col' 5 | 6 | 7 | Vue.config.productionTip = false 8 | Vue.config.devtools = false 9 | 10 | describe('Row', () => { 11 | it('存在.', () => { 12 | expect(Row).to.be.ok 13 | }) 14 | it('接受gutter属性', (done) => { 15 | Vue.component('g-row', Row) 16 | Vue.component('g-col', Col) 17 | const div = document.createElement('div') 18 | document.body.appendChild(div) 19 | div.innerHTML = ` 20 | 21 | 22 | 23 | > 24 | ` 25 | const vm = new Vue({ 26 | el: div 27 | }) 28 | setTimeout(() => { 29 | const row = vm.$el.querySelector('.row') 30 | expect(getComputedStyle(row).marginLeft).to.eq('-10px') 31 | expect(getComputedStyle(row).marginRight).to.eq('-10px') 32 | const cols = vm.$el.querySelectorAll('.col') 33 | expect(getComputedStyle(cols[0]).paddingRight).to.eq('10px') 34 | expect(getComputedStyle(cols[1]).paddingLeft).to.eq('10px') 35 | done() 36 | vm.$el.remove() 37 | vm.$destroy() 38 | }) 39 | }) 40 | it('接受align属性', () => { 41 | // 必须放到页面上 42 | const div = document.createElement('div') 43 | document.body.appendChild(div) 44 | const Constructor = Vue.extend(Row) 45 | const vm = new Constructor({ 46 | propsData: { 47 | align: 'right' 48 | } 49 | }).$mount(div) 50 | const element = vm.$el 51 | expect(getComputedStyle(element).justifyContent).to.eq('flex-end') 52 | div.remove() 53 | vm.$destroy() 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /tests/unit/slides.spec.js: -------------------------------------------------------------------------------- 1 | import chai, { expect } from "chai"; 2 | import sinon from "sinon"; 3 | import sinonChai from "sinon-chai"; 4 | import { shallowMount, mount } from "@vue/test-utils"; 5 | import Slides from "../../src/slides/slides.vue"; 6 | import SlidesItem from "../../src/slides/slides-item.vue"; 7 | import Vue from "vue"; 8 | 9 | chai.use(sinonChai); 10 | 11 | describe("Slides.vue", () => { 12 | it("存在.", () => { 13 | expect(Slides).to.exist; 14 | }); 15 | 16 | it("接受 GoSlidesItem,默认展示第一个", done => { 17 | Vue.component("GSlidesItem", SlidesItem); 18 | const wrapper = mount(Slides, { 19 | propsData: { 20 | autoPlay: false 21 | }, 22 | slots: { 23 | default: ` 24 | 25 |
1
26 |
27 | 28 |
2
29 |
30 | 31 |
3
32 |
33 | ` 34 | } 35 | }); 36 | setTimeout(() => { 37 | expect(wrapper.find(".box1").exists()).to.be.true; 38 | done(); 39 | }); 40 | }); 41 | it("selected 是几,选中的就是几", done => { 42 | Vue.component("GSlidesItem", SlidesItem); 43 | const wrapper = mount(Slides, { 44 | propsData: { 45 | autoPlay: false, 46 | selected: "2" 47 | }, 48 | slots: { 49 | default: ` 50 | 51 |
1
52 |
53 | 54 |
2
55 |
56 | 57 |
3
58 |
59 | ` 60 | } 61 | }); 62 | setTimeout(() => { 63 | expect(wrapper.find(".box2").exists()).to.be.true; 64 | done(); 65 | }); 66 | }); 67 | 68 | it("点击第二个就展示第二个", done => { 69 | Vue.component("GSlidesItem", SlidesItem); 70 | const wrapper = mount(Slides, { 71 | propsData: { 72 | autoPlay: false, 73 | selected: "1" 74 | }, 75 | slots: { 76 | default: ` 77 | 78 |
1
79 |
80 | 81 |
2
82 |
83 | 84 |
3
85 |
86 | ` 87 | }, 88 | listeners: { 89 | "update:selected": x => { 90 | expect(x).to.eq("2"); 91 | done(); 92 | } 93 | } 94 | }); 95 | setTimeout(() => { 96 | wrapper.find('[data-index="1"]').trigger("click"); 97 | }); 98 | }); 99 | 100 | it("会自动播放", done => { 101 | Vue.component("GSlidesItem", SlidesItem); 102 | const callback = sinon.fake(); 103 | const wrapper = mount(Slides, { 104 | propsData: { 105 | autoPlay: true, 106 | autoPlayDelay: 20, 107 | selected: "1" 108 | }, 109 | slots: { 110 | default: ` 111 | 112 |
1
113 |
114 | 115 |
2
116 |
117 | 118 |
3
119 |
120 | ` 121 | }, 122 | listeners: { 123 | "update:selected": callback 124 | } 125 | }); 126 | setTimeout(() => { 127 | expect(callback).to.have.been.calledWith("2"); 128 | done(); 129 | }, 21); 130 | }); 131 | it("可以点击上一张", done => { 132 | Vue.component("GSlidesItem", SlidesItem); 133 | const callback = sinon.fake(); 134 | const wrapper = mount(Slides, { 135 | propsData: { 136 | autoPlay: true, 137 | autoPlayDelay: 20, 138 | selected: "1" 139 | }, 140 | slots: { 141 | default: ` 142 | 143 |
1
144 |
145 | 146 |
2
147 |
148 | 149 |
3
150 |
151 | ` 152 | }, 153 | listeners: { 154 | "update:selected": callback 155 | } 156 | }); 157 | setTimeout(() => { 158 | wrapper.find('[data-action="prev"]').trigger("click"); 159 | expect(callback).to.have.been.calledWith("3"); 160 | done(); 161 | }, 21); 162 | }); 163 | it("可以点击下一张", done => { 164 | Vue.component("GSlidesItem", SlidesItem); 165 | const callback = sinon.fake(); 166 | const wrapper = mount(Slides, { 167 | propsData: { 168 | autoPlay: true, 169 | autoPlayDelay: 20, 170 | selected: "1" 171 | }, 172 | slots: { 173 | default: ` 174 | 175 |
1
176 |
177 | 178 |
2
179 |
180 | 181 |
3
182 |
183 | ` 184 | }, 185 | listeners: { 186 | "update:selected": callback 187 | } 188 | }); 189 | setTimeout(() => { 190 | wrapper.find('[data-action="next"]').trigger("click"); 191 | expect(callback).to.have.been.calledWith("2"); 192 | done(); 193 | }, 21); 194 | }); 195 | }); 196 | -------------------------------------------------------------------------------- /tests/unit/tabs-body.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Tabs from '../../src/tabs/tabs' 4 | import TabsHead from '../../src/tabs/tabs-head' 5 | import TabsBody from '../../src/tabs/tabs-body' 6 | import TabsItem from '../../src/tabs/tabs-item' 7 | import TabsPane from '../../src/tabs/tabs-pane' 8 | 9 | Vue.component('g-tabs', Tabs) 10 | Vue.component('g-tabs-head', TabsHead) 11 | Vue.component('g-tabs-body', TabsBody) 12 | Vue.component('g-tabs-item', TabsItem) 13 | Vue.component('g-tabs-pane', TabsPane) 14 | 15 | Vue.config.productionTip = false 16 | Vue.config.devtools = false 17 | 18 | describe('TabsBody', () => { 19 | it('存在.', () => { 20 | expect(TabsBody).to.exist 21 | }) 22 | }) -------------------------------------------------------------------------------- /tests/unit/tabs-head.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Tabs from '../../src/tabs/tabs' 4 | import TabsHead from '../../src/tabs/tabs-head' 5 | import TabsBody from '../../src/tabs/tabs-body' 6 | import TabsItem from '../../src/tabs/tabs-item' 7 | import TabsPane from '../../src/tabs/tabs-pane' 8 | 9 | Vue.component('g-tabs', Tabs) 10 | Vue.component('g-tabs-head', TabsHead) 11 | Vue.component('g-tabs-body', TabsBody) 12 | Vue.component('g-tabs-item', TabsItem) 13 | Vue.component('g-tabs-pane', TabsPane) 14 | 15 | Vue.config.productionTip = false 16 | Vue.config.devtools = false 17 | 18 | describe('TabsHead', () => { 19 | it('存在.', () => { 20 | expect(TabsHead).to.exist 21 | }) 22 | }) -------------------------------------------------------------------------------- /tests/unit/tabs-item.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Tabs from '../../src/tabs/tabs' 4 | import TabsHead from '../../src/tabs/tabs-head' 5 | import TabsBody from '../../src/tabs/tabs-body' 6 | import TabsItem from '../../src/tabs/tabs-item' 7 | import TabsPane from '../../src/tabs/tabs-pane' 8 | 9 | Vue.component('g-tabs', Tabs) 10 | Vue.component('g-tabs-head', TabsHead) 11 | Vue.component('g-tabs-body', TabsBody) 12 | Vue.component('g-tabs-item', TabsItem) 13 | Vue.component('g-tabs-pane', TabsPane) 14 | 15 | Vue.config.productionTip = false 16 | Vue.config.devtools = false 17 | 18 | describe('TabsItem', () => { 19 | 20 | it('存在.', () => { 21 | expect(TabsItem).to.exist 22 | }) 23 | 24 | // it('接受 name 属性', () => { 25 | // const Constructor = Vue.extend(TabsItem) 26 | // const vm = new Constructor({ 27 | // propsData: { 28 | // name: 'xxx', 29 | // } 30 | // }).$mount() 31 | // expect(vm.$el.getAttribute('data-name')).to.eq('xxx') 32 | // }) 33 | // it('接受 disabled 属性', () => { 34 | // const Constructor = Vue.extend(TabsItem) 35 | // const vm = new Constructor({ 36 | // propsData: { 37 | // disabled: true, 38 | // } 39 | // }).$mount() 40 | // expect(vm.$el.classList.contains('disabled')).to.be.true 41 | // const callback = sinon.fake(); 42 | // vm.$on('click', callback) 43 | // vm.$el.click() 44 | // expect(callback).to.have.not.been.called 45 | // }) 46 | }) -------------------------------------------------------------------------------- /tests/unit/tabs-pane.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Tabs from '../../src/tabs/tabs' 4 | import TabsHead from '../../src/tabs/tabs-head' 5 | import TabsBody from '../../src/tabs/tabs-body' 6 | import TabsItem from '../../src/tabs/tabs-item' 7 | import TabsPane from '../../src/tabs/tabs-pane' 8 | 9 | Vue.component('g-tabs', Tabs) 10 | Vue.component('g-tabs-head', TabsHead) 11 | Vue.component('g-tabs-body', TabsBody) 12 | Vue.component('g-tabs-item', TabsItem) 13 | Vue.component('g-tabs-pane', TabsPane) 14 | 15 | Vue.config.productionTip = false 16 | Vue.config.devtools = false 17 | 18 | describe('TabsPane', () => { 19 | it('存在.', () => { 20 | expect(TabsPane).to.exist 21 | }) 22 | }) -------------------------------------------------------------------------------- /tests/unit/tabs.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Tabs from '../../src/tabs/tabs' 4 | import TabsHead from '../../src/tabs/tabs-head' 5 | import TabsBody from '../../src/tabs/tabs-body' 6 | import TabsItem from '../../src/tabs/tabs-item' 7 | import TabsPane from '../../src/tabs/tabs-pane' 8 | 9 | Vue.component('g-tabs', Tabs) 10 | Vue.component('g-tabs-head', TabsHead) 11 | Vue.component('g-tabs-body', TabsBody) 12 | Vue.component('g-tabs-item', TabsItem) 13 | Vue.component('g-tabs-pane', TabsPane) 14 | 15 | Vue.config.productionTip = false 16 | Vue.config.devtools = false 17 | 18 | describe('Tabs', () => { 19 | it('存在.', () => { 20 | expect(Tabs).to.exist 21 | }) 22 | }) -------------------------------------------------------------------------------- /tests/unit/toast.test.js: -------------------------------------------------------------------------------- 1 | const expect = chai.expect; 2 | import Vue from 'vue' 3 | import Toast from '../../src/toast/toast' 4 | 5 | Vue.config.productionTip = false 6 | Vue.config.devtools = false 7 | 8 | describe('Toast', () => { 9 | 10 | it('存在.', () => { 11 | expect(Toast).to.exist 12 | }) 13 | 14 | describe('props', function () { 15 | this.timeout(15000) 16 | it('接受 autoClose', (done) => { 17 | let div = document.createElement('div') 18 | document.body.appendChild(div) 19 | const Constructor = Vue.extend(Toast) 20 | const vm = new Constructor({ 21 | propsData: { 22 | autoClose: 1, 23 | } 24 | }).$mount(div) 25 | // vm.$on('close', () => { 26 | // expect(document.body.contains(vm.$el)).to.eq(false) 27 | // done() 28 | // }) 29 | setTimeout(() => { 30 | expect(document.body.contains(vm.$el)).to.eq(false) 31 | done() 32 | },2000) 33 | }) 34 | it('接受 closeButton', (done) => { 35 | const callback = sinon.fake(); 36 | const Constructor = Vue.extend(Toast) 37 | const vm = new Constructor({ 38 | propsData: { 39 | closeButton: { 40 | text: '关闭吧', 41 | callback, 42 | }, 43 | } 44 | }).$mount() 45 | let closeButton = vm.$el.querySelector('.close') 46 | expect(closeButton.textContent.trim()).to.eq('关闭吧') 47 | setTimeout(() => { 48 | closeButton.click() 49 | expect(callback).to.have.been.called 50 | done() 51 | },200) 52 | }) 53 | it('接受 enableHtml', () => { 54 | const Constructor = Vue.extend(Toast) 55 | const vm = new Constructor({ 56 | propsData: {enableHtml: true} 57 | }) 58 | vm.$slots.default = ['hi'] 59 | vm.$mount() 60 | let strong = vm.$el.querySelector('#test') 61 | expect(strong).to.exist 62 | }) 63 | it('接受 position', () => { 64 | const Constructor = Vue.extend(Toast) 65 | const vm = new Constructor({ 66 | propsData: { 67 | position: 'bottom' 68 | } 69 | }).$mount() 70 | expect(vm.$el.classList.contains('position-bottom')).to.eq(true) 71 | }) 72 | }) 73 | }) -------------------------------------------------------------------------------- /tests/unit/validate.spec.js: -------------------------------------------------------------------------------- 1 | import chai, { expect } from "chai"; 2 | import sinon from "sinon"; 3 | import sinonChai from "sinon-chai"; 4 | import { shallowMount, mount } from "@vue/test-utils"; 5 | import Validator from "../../src/validate.js"; 6 | 7 | chai.use(sinonChai); 8 | 9 | describe("Validate", () => { 10 | it("存在.", () => { 11 | expect(Validator).to.exist; 12 | }); 13 | 14 | it("required true 报错", () => { 15 | let data = { 16 | email: "" 17 | }; 18 | let rules = [{ key: "email", required: true }]; 19 | let validator = new Validator(); 20 | let errors = validator.validate(data, rules); 21 | expect(errors.email.required).to.eq("必填"); 22 | }); 23 | 24 | it("required true 通过", () => { 25 | let data = { 26 | email: 0 27 | }; 28 | let rules = [{ key: "email", required: true }]; 29 | let validator = new Validator(); 30 | let errors = validator.validate(data, rules); 31 | expect(errors.email).to.not.exist; 32 | }); 33 | 34 | it("pattern 报错", () => { 35 | let data = { 36 | email: "@frank.com" 37 | }; 38 | let rules = [{ key: "email", pattern: /^.+@.+$/ }]; 39 | let validator = new Validator(); 40 | let errors = validator.validate(data, rules); 41 | expect(errors.email.pattern).to.eq("格式不正确"); 42 | }); 43 | 44 | it("pattern 通过", () => { 45 | let data = { 46 | email: "1@frank.com" 47 | }; 48 | let rules = [{ key: "email", pattern: /^.+@.+$/ }]; 49 | let validator = new Validator(); 50 | let errors = validator.validate(data, rules); 51 | expect(errors.email).to.not.exist; 52 | }); 53 | 54 | it("pattern email 报错", () => { 55 | let data = { 56 | email: "@frank.com" 57 | }; 58 | let rules = [{ key: "email", pattern: "email" }]; 59 | let validator = new Validator(); 60 | let errors = validator.validate(data, rules); 61 | expect(errors.email.pattern).to.eq("格式不正确"); 62 | }); 63 | it("pattern email 通过", () => { 64 | let data = { 65 | email: "1@frank.com" 66 | }; 67 | let rules = [{ key: "email", pattern: "email" }]; 68 | let validator = new Validator(); 69 | let errors = validator.validate(data, rules); 70 | expect(errors.email).to.not.exist; 71 | }); 72 | 73 | it("require & pattern", () => { 74 | let data = { 75 | email: "" 76 | }; 77 | let rules = [{ key: "email", pattern: "email", required: true }]; 78 | let validator = new Validator(); 79 | let errors = validator.validate(data, rules); 80 | expect(errors.email.required).to.exist; 81 | expect(errors.email.pattern).to.not.exist; 82 | }); 83 | it("pattern & minLength", () => { 84 | let data = { 85 | email: "" 86 | }; 87 | let rules = [{ key: "email", pattern: "email", minLength: 6 }]; 88 | let validator = new Validator(); 89 | let errors = validator.validate(data, rules); 90 | expect(errors.email.minLength).to.exist; 91 | expect(errors.email.pattern).to.exist; 92 | }); 93 | it("maxLength", () => { 94 | let data = { 95 | email: "123123123123" 96 | }; 97 | let rules = [{ key: "email", pattern: "email", maxLength: 10 }]; 98 | let validator = new Validator(); 99 | let errors = validator.validate(data, rules); 100 | expect(errors.email.maxLength).to.exist; 101 | }); 102 | it("many keys", () => { 103 | let data = { 104 | email: "123123123123" 105 | }; 106 | let rules = [ 107 | { 108 | key: "email", 109 | required: true, 110 | minLength: 5, 111 | maxLength: 10, 112 | hasNumber: true, 113 | hasLowerCaseAndUpperCase: true, 114 | hasDot: true, 115 | hasUnderscore: true, 116 | hasFrank: true 117 | } 118 | ]; 119 | let fn = () => { 120 | let validator = new Validator(); 121 | validator.validate(data, rules); 122 | }; 123 | expect(fn).to.throw(); 124 | }); 125 | it("自定义测试规则 hasNumber", () => { 126 | let data = { 127 | email: "abcabcabcabc" 128 | }; 129 | let validator = new Validator(); 130 | validator.hasNumber = value => { 131 | if (!/\d/.test(value)) { 132 | return "必须含有数字"; 133 | } 134 | }; 135 | let rules = [{ key: "email", required: true, hasNumber: true }]; 136 | let errors; 137 | let fn = () => { 138 | errors = validator.validate(data, rules); 139 | }; 140 | expect(fn).to.not.throw(); 141 | expect(errors.email.hasNumber).to.eq("必须含有数字"); 142 | }); 143 | it("两个 validator 互相不影响", () => { 144 | let data = { 145 | email: "abcabcabcabc" 146 | }; 147 | let validator1 = new Validator(); 148 | let validator2 = new Validator(); 149 | validator1.hasNumber = value => { 150 | if (!/\d/.test(value)) { 151 | return "必须含有数字"; 152 | } 153 | }; 154 | let rules = [{ key: "email", required: true, hasNumber: true }]; 155 | let errors; 156 | expect(() => { 157 | validator1.validate(data, rules); 158 | }).to.not.throw(); 159 | expect(() => { 160 | validator2.validate(data, rules); 161 | }).to.throw(); 162 | }); 163 | 164 | it("可以全局添加新规则", () => { 165 | let data = { 166 | email: "abcabcabcabc" 167 | }; 168 | let validator1 = new Validator(); 169 | let validator2 = new Validator(); 170 | Validator.add("hasNumber", value => { 171 | if (!/\d/.test(value)) { 172 | return "必须含有数字"; 173 | } 174 | }); 175 | let rules = [{ key: "email", required: true, hasNumber: true }]; 176 | let errors; 177 | expect(() => { 178 | validator1.validate(data, rules); 179 | }).to.not.throw(); 180 | expect(() => { 181 | validator2.validate(data, rules); 182 | }).to.not.throw(); 183 | }); 184 | }); 185 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | module.exports = { 3 | css: { 4 | loaderOptions: { 5 | sass: { 6 | includePaths: [path.join(__dirname, 'styles')] 7 | }, 8 | } 9 | } 10 | } --------------------------------------------------------------------------------