├── .browserslistrc
├── .eslintrc.js
├── .gitignore
├── README.md
├── babel.config.js
├── jest.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
└── index.html
├── src
├── (不会被打包)App
│ ├── popover.vue
│ ├── tab.vue
│ ├── tree.vue
│ ├── 分页器.vue
│ ├── 啊!声明.md
│ ├── 小星星.vue
│ ├── 弹出框.vue
│ ├── 懒加载.vue
│ ├── 按钮输入.vue
│ ├── 日期选择器.vue
│ ├── 计数器.vue
│ └── 骨架.vue
├── assets
│ └── js
│ │ ├── Clickoutside.js
│ │ ├── handelDate.js
│ │ ├── prevent.js
│ │ ├── utils.js
│ │ └── vuePopper.js
├── components
│ ├── Alert
│ │ ├── index.js
│ │ └── main
│ │ │ ├── alert.js
│ │ │ └── alert.vue
│ ├── Button
│ │ ├── index.js
│ │ └── main
│ │ │ └── button.vue
│ ├── DatePicker
│ │ ├── index.js
│ │ └── main
│ │ │ └── datePicker.vue
│ ├── Icon
│ │ ├── index.js
│ │ └── main
│ │ │ └── icon.vue
│ ├── Input
│ │ ├── index.js
│ │ └── main
│ │ │ └── input.vue
│ ├── InputNumber
│ │ ├── index.js
│ │ └── main
│ │ │ └── input-number.vue
│ ├── Lazy
│ │ ├── index.js
│ │ └── main
│ │ │ └── lazy.js
│ ├── Pagination
│ │ ├── index.js
│ │ └── main
│ │ │ └── pagination.vue
│ ├── Popover
│ │ ├── index.js
│ │ └── main
│ │ │ ├── index.js
│ │ │ └── popover.vue
│ ├── Rate
│ │ ├── index.js
│ │ └── main
│ │ │ └── rate.vue
│ ├── Ske
│ │ ├── index.js
│ │ └── main
│ │ │ └── ske.vue
│ ├── Tab
│ │ ├── index.js
│ │ └── main
│ │ │ ├── tab-pane.vue
│ │ │ └── tab.vue
│ ├── Toast
│ │ ├── index.js
│ │ └── main
│ │ │ ├── toast.js
│ │ │ └── toast.vue
│ ├── Tree
│ │ ├── index.js
│ │ └── main
│ │ │ └── tree.vue
│ ├── index.js
│ └── loading
│ │ ├── index.js
│ │ └── main
│ │ ├── loading.js
│ │ └── loading.vue
├── main.js
└── style
│ ├── Alert.scss
│ ├── Button.scss
│ ├── DatePicker.scss
│ ├── Icon.scss
│ ├── Input.scss
│ ├── Loading.scss
│ ├── Pagination.scss
│ ├── Popover.scss
│ ├── Rate.scss
│ ├── Ske.scss
│ ├── Tab.scss
│ ├── Toast.scss
│ ├── Tree.scss
│ ├── common
│ ├── animation.scss
│ ├── extend.scss
│ ├── mixin.scss
│ ├── reset.scss
│ └── var.scss
│ ├── config
│ └── index.scss
│ ├── fonts
│ └── iconfont.js
│ ├── index.scss
│ └── inputNumber.scss
└── tests
├── unit
├── .eslintrc.js
├── Button.test.js
├── Input.test.js
├── Loading.test.js
├── Pagination.test.js
└── Toast.test.js
└── utils
└── util.js
/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not ie <= 8
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | 'extends': [
7 | 'plugin:vue/essential',
8 | 'eslint:recommended'
9 | ],
10 | rules: {
11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
13 | },
14 | parserOptions: {
15 | parser: 'babel-eslint'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw*
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # cc-ui 是一个 vue 的 ui 组件库
2 |
3 | ## 介绍
4 |
5 |
关于作者
6 | 自信自律的终身学习者: lulu
7 | 工作方面: 本人更喜欢稳健的作风, 代码的编写要守规矩, 插件要使用网上成熟的开源库, 采取的解决方案以稳妥为主.
8 | 学习方面: 本人更喜欢天马行空的想法, 不拘一格的做派, 可以中文编程, 可以各种各样的效果, 遇到问题都要自己解决, 我可以学别人的方法, 但我要自己实现自己写, 所以有时候会做出一些样子乱七八糟的组件, 还请大家包涵😁
9 |
10 | 关于ui
11 | 'cc'是本人养的大金毛,很可爱的!
12 | 本组件适用于pc端, 因为本人从事过大型后台管理系统的开发, 感觉很多组件库写的太棒了, 当然这些库也存在一些不足之处, 所以才产生了编写一套自己的组件库的想法.
13 | 生活不止代码, 所以也想通过本套工程与大家一起学习一起交流😁共同实现人生价值
14 |
15 | ## 安装
16 |
17 | npm i vue-cc-ui -S
18 |
19 | ## 详细的相关文章请查看:
20 |
21 | https://segmentfault.com/u/lulu_up/articles
22 |
23 | ## ui 详情请打开 lulu 个人开发网站
24 |
25 | https://zhangzhaosong.com
26 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
3 | transform: {
4 | '^.+\\.vue$': 'vue-jest',
5 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$':
6 | 'jest-transform-stub',
7 | '^.+\\.jsx?$': 'babel-jest'
8 | },
9 | transformIgnorePatterns: ['/node_modules/','/.eslintrc.js/','/dist/'],
10 | moduleNameMapper: {
11 | '^@/(.*)$': '/src/$1'
12 | },
13 | snapshotSerializers: ['jest-serializer-vue'],
14 | testMatch: [
15 | '**/tests/unit/**/*.(spec|test).(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
16 | ],
17 | testURL: 'http://localhost/',
18 | // watchPlugins: [
19 | // 'jest-watch-typeahead/filename',
20 | // 'jest-watch-typeahead/testname'
21 | // ]
22 | };
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-cc-ui",
3 | "version": "0.0.17",
4 | "main": "./dist/cc-ui.umd.min",
5 | "files": [
6 | "dist/*",
7 | "src/*",
8 | "*.json",
9 | "*.js"
10 | ],
11 | "scripts": {
12 | "serve": "vue-cli-service serve",
13 | "build": "vue-cli-service build",
14 | "lint": "vue-cli-service lint",
15 | "test:unit": "vue-cli-service test:unit",
16 | "build-bundle": "vue-cli-service build --target lib --name cc-ui ./src/components/index.js"
17 | },
18 | "dependencies": {
19 | "vue": "^2.6.6"
20 | },
21 | "devDependencies": {
22 | "@vue/cli-plugin-babel": "^3.5.0",
23 | "@vue/cli-plugin-eslint": "^3.5.0",
24 | "@vue/cli-plugin-unit-jest": "^3.10.0",
25 | "@vue/cli-service": "^3.5.0",
26 | "@vue/test-utils": "1.0.0-beta.29",
27 | "babel-core": "7.0.0-bridge.0",
28 | "babel-eslint": "^10.0.1",
29 | "babel-jest": "^23.6.0",
30 | "eslint": "^5.8.0",
31 | "eslint-plugin-vue": "^5.0.0",
32 | "node-sass": "^4.12.0",
33 | "sass-loader": "^7.1.0",
34 | "speed-measure-webpack-plugin": "^1.3.1",
35 | "vue-template-compiler": "^2.5.21"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | cc_ui
9 |
10 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/popover.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 容是我内容是我内容
9 |
10 |
11 | 出现 弹框
12 |
13 |
14 |
15 |
16 |
17 | 容是我内容是我内容
18 |
19 |
20 | 出现 弹框
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/tab.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
labcc: 显示的名称
6 |
name: 被选中的名称
7 |
内容自不必说
8 |
tab:{{activeName}}
9 |
里面的内容:{{context}}
10 |
11 |
12 |
14 |
17 |
18 |
19 | {{context}}
20 |
21 |
22 |
23 |
24 | 用户管理2
25 |
26 |
27 |
28 |
30 | 配置管理
31 |
32 | 角色管理
35 | 定时任务补偿
37 |
38 |
39 |
40 |
42 | 111
44 | 222
46 | 333
48 | 444
50 |
51 |
52 |
54 | 111
56 |
57 |
58 |
59 |
60 |
81 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/tree.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
15 |
16 |
{{choiceLists}}
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/分页器.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
10 |
11 |
12 |
19 |
20 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/啊!声明.md:
--------------------------------------------------------------------------------
1 | ### 特别声明
2 | 1. 此处为示例文件夹, 不会被打包到项目中, 所以中文命名也无妨.
--------------------------------------------------------------------------------
/src/(不会被打包)App/小星星.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 | 滑动
6 |
12 |
13 |
14 | -
15 |
禁选:
16 |
19 |
20 | -
21 |
-
22 |
禁选: 变大
23 |
27 |
28 | -
29 |
禁选: 表白专用
30 |
38 |
39 | -
40 |
禁选: 心
41 |
49 |
50 | -
51 |
59 |
60 | -
61 |
68 |
69 | -
70 |
12 vs 10
71 |
79 |
80 |
81 | -
82 |
可选:
83 |
84 |
88 |
89 | -
90 |
可选: one
91 |
92 |
95 |
96 | -
97 |
可选: one
98 |
99 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
129 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/弹出框.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 基本造型
6 | 可以验证信息
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/懒加载.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
![]()
7 |
8 |
11 |
![]()
15 |
16 |
17 |
18 |
19 |
46 |
63 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/按钮输入.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
按钮正常
4 |
按钮禁用
5 |
6 |
左边按钮
7 |
中间正常
8 |
中间正常
9 |
右边正常
10 |
11 |
节流
13 |
节流
15 |
前防抖
17 |
后防抖
19 |
20 |
大按钮
21 |
中按钮
22 |
小按钮
23 |
24 |
bling
25 |
26 |
图标按钮
28 |
图标按钮
30 |
图标按钮
32 |
图标按钮
34 |
图标按钮
36 |
图标按钮
38 |
图标按钮
40 |
41 |
图标按钮
44 |
图标按钮
46 |
图标按钮
48 |
49 |
type按钮
50 |
type按钮
51 |
type按钮
52 |
53 | 字体图标:
54 |
55 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
弹出Taost
76 |
弹出Taost
78 |
弹出Taost
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
js方式触发
89 |
90 |
91 |
97 |
98 |
99 |
100 | 6666
101 |
102 |
103 |
104 |
105 |
106 |
107 |
文字信息玩好玩
108 |
109 |
110 |
自动获取焦点
111 |
文s信息
112 |
113 |
114 |
好
115 |
116 |
117 |
118 |
119 |
{{bangding}}
120 |
文本框
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | 自适应高度
129 |
130 |
134 |
135 |
136 |
137 |
138 |
179 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/日期选择器.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{showValues}}
4 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/计数器.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 1:
5 |
6 |
7 | - 2:
8 |
9 |
10 | - 3:
11 |
12 |
13 | -
14 | 4:自定义
15 |
16 |
17 |
点我😁
18 |
19 |
20 | 😁点我
21 |
22 |
23 |
24 | - 5:
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
49 |
--------------------------------------------------------------------------------
/src/(不会被打包)App/骨架.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 请问\
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/assets/js/Clickoutside.js:
--------------------------------------------------------------------------------
1 | // 判断点击是不是自己
2 | const clickoutside = {
3 | bind(el, bindings, vnode) {
4 | const handleClick = function(e) {
5 | // 如果当前的不包含目标元素, 也就是隐藏日期组件
6 | // 这个vnode就是组件的虚拟dom
7 | if (!el.contains(e.target)) {
8 | // 拿到日历组件的实例, expression就是我们指令传进来的操作;
9 | vnode.context[bindings.expression]();
10 | }
11 | };
12 | // 为了方便移除
13 | el.handleClick = handleClick;
14 | document.addEventListener('click', handleClick);
15 | },
16 | unbind(el) {
17 | document.removeEventListener('click', el.handleClick);
18 | }
19 | };
20 |
21 | export default clickoutside;
22 |
--------------------------------------------------------------------------------
/src/assets/js/handelDate.js:
--------------------------------------------------------------------------------
1 | export function getYMD(date){
2 | let day = date.getDate();
3 | let month = date.getMonth();
4 | let year = date.getFullYear();
5 | return {
6 | year, month, day
7 | }
8 | }
9 |
10 | export const getDayCountOfMonth = function(year, month) {
11 | if (month === 3 || month === 5 || month === 8 || month === 10) {
12 | return 30;
13 | }
14 | if (month === 1) {
15 | if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
16 | return 29;
17 | } else {
18 | return 28;
19 | }
20 | }
21 |
22 | return 31;
23 | };
--------------------------------------------------------------------------------
/src/assets/js/prevent.js:
--------------------------------------------------------------------------------
1 | // 数据节流函数
2 | // id: 唯一标识
3 | // obj: 要执行的函数
4 | // time: 多久之内节流
5 | // model: 哪种模式
6 | // 需要节流的对象
7 | const preventList = {};
8 | const config = ['', throttle, shakeBefour, shakeAfter];
9 | const prevent = function(id, obj, time, model = 1) {
10 | config[model](id, obj, time);
11 | };
12 |
13 | // 模式1 不管点多少下每隔time秒,触发一次
14 | function throttle(id, obj, time) {
15 | if (preventList['can' + id]) return;
16 | obj();
17 | preventList['can' + id] = true;
18 | preventList['time' + id] = setTimeout(() => {
19 | preventList['can' + id] = false;
20 | }, time);
21 | }
22 |
23 | // 模式2 每次动作都有time的延时再执行,也就是所有连续点击完事的时候执行一个
24 | function shakeBefour(id, obj, time) {
25 | clearTimeout(preventList['time' + id]);
26 | preventList['time' + id] = setTimeout(() => obj(), time);
27 | }
28 |
29 | // 默认的模式, 模式3, 第一下点击触发, 之后时间内不触发
30 | function shakeAfter(id, obj, time) {
31 | if (preventList['can' + id]) {
32 | clearTimeout(preventList['time' + id]);
33 | } else {
34 | obj();
35 | preventList['can' + id] = true;
36 | }
37 | preventList['time' + id] = setTimeout(() => {
38 | preventList['can' + id] = false;
39 | }, time);
40 | }
41 |
42 | export default prevent;
43 |
--------------------------------------------------------------------------------
/src/assets/js/utils.js:
--------------------------------------------------------------------------------
1 | // 加.0 toFixed会四舍五入的
2 | export const myToFixed = value => {
3 | value = value + '';
4 | if (value.includes('.')) {
5 | let sp = value.split('.');
6 | return sp[0] + '.' + sp[1].slice(0, 1);
7 | } else {
8 | return value + '.0';
9 | }
10 | };
11 |
12 | // 算元素距离body的距离
13 | export function getHTMLScroll(node) {
14 | // node.getBoundingClientRect() 其实这一个函数就行了
15 | if (!node) return;
16 | let result = { top: 0, left: 0 },
17 | parent = node.offsetParent || node.parentNode,
18 | children = node; // 获取定位父级
19 | let task = son => {
20 | let dom = son.parentNode;
21 | if (!dom) return;
22 | if (parent === dom) {
23 | let domScrollTop = dom.scrollTop || 0,
24 | domScrollLeft = dom.scrollLeft || 0;
25 | result.top += children.offsetTop - domScrollTop;
26 | result.left += children.offsetLeft - domScrollLeft;
27 | children = parent;
28 | parent = dom.offsetParent; // 下一个父级
29 | } else {
30 | let domScrollTop = dom.scrollTop || 0,
31 | domScrollLeft = dom.scrollLeft || 0;
32 | result.top -= domScrollTop;
33 | result.left -= domScrollLeft;
34 | }
35 |
36 | let pos = window.getComputedStyle(dom, null).position;
37 | if (pos === 'fixed') {
38 | result.top += dom.offsetTop;
39 | result.left += dom.offsetLeft;
40 | return;
41 | }
42 | if (dom.nodeName !== 'BODY') {
43 | task(dom);
44 | }
45 | };
46 | task(node);
47 | return result;
48 | }
49 |
50 | export function getViewportSize() {
51 | if (window.innerHeight) {
52 | return {
53 | width: window.innerWidth,
54 | height: window.innerHeight
55 | };
56 | } else {
57 | if (document.compatMode === 'BackCompat') {
58 | return {
59 | width: document.body.clientWidth,
60 | height: document.body.clientHeight
61 | };
62 | } else {
63 | return {
64 | width: document.documentElement.clientWidth,
65 | height: document.documentElement.clientHeight
66 | };
67 | }
68 | }
69 | }
70 |
71 | export function getScrollOffset() {
72 | if (window.pageXOffset) {
73 | return {
74 | left: window.pageXOffset,
75 | top: window.pageYOffset
76 | };
77 | } else {
78 | // 问题: 为什么要相加
79 | // 因为这两个属性只有一个有用, 另一个肯定是0, 索性直接相加
80 | return {
81 | left: document.body.scrollLeft + document.documentElement.scrollLeft,
82 | top: document.body.scrollTop + document.documentElement.scrollTop
83 | };
84 | }
85 | }
86 |
87 | export function inspect(min) {
88 | return function(value) {
89 | if (value < min || value !== ~~value) {
90 | throw new Error(`最小为${min}的整数`);
91 | }
92 | return true;
93 | };
94 | }
95 |
96 | // 添加事件, element-ui判断是不是服务器环境
97 | export function on(element, event, handler) {
98 | if (element && event && handler) {
99 | element.addEventListener(event, handler, false);
100 | }
101 | }
102 | // 移除事件
103 | export function off(element, event, handler) {
104 | if (element && event) {
105 | element.removeEventListener(event, handler, false);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/assets/js/vuePopper.js:
--------------------------------------------------------------------------------
1 | // 流体bug未解决, 需要多次获取dom, 感觉完全没必要
2 | import { getScrollOffset } from './utils';
3 | export function getPopoverPosition(popover, content, direction,CONTANT ) {
4 | // 只负责启动, 自己去检测
5 | // 优化一些, 参数获取函数
6 | let result = { show: true };
7 | getOptions(result, popover, content, direction,CONTANT );
8 | let { left, top } = getScrollOffset();
9 | result.left += left;
10 | result.top += top;
11 | return result;
12 | }
13 | // 手打比 循环省性能
14 | const list = [
15 | 'top-end',
16 | 'left-end',
17 | 'top-start',
18 | 'right-end',
19 | 'top-middle',
20 | 'bottom-end',
21 | 'left-start',
22 | 'right-start',
23 | 'left-middle',
24 | 'right-middle',
25 | 'bottom-start',
26 | 'bottom-middle'
27 | ];
28 | // 只要获取一次就行, 不要浪费性能
29 | function getOptions(result, popover, content, direction,CONTANT = 10) {
30 | let myList = list.concat(),
31 | client = popover.getBoundingClientRect();
32 | myList.splice(list.indexOf(direction), 1);
33 | getDirection(result, {
34 | myList,
35 | direction,
36 | CONTANT,
37 | top: client.top,
38 | left: client.left,
39 | popoverWidth: popover.offsetWidth,
40 | contentWidth: content.offsetWidth,
41 | popoverHeight: popover.offsetHeight,
42 | contentHeight: content.offsetHeight
43 | });
44 | }
45 | function getDirection(result, options) {
46 | let {
47 | top,
48 | left,
49 | CONTANT,
50 | direction,
51 | contentWidth,
52 | popoverWidth,
53 | contentHeight,
54 | popoverHeight
55 | } = options;
56 | result.options = options;
57 | let main = direction.split('-')[0],
58 | around = direction.split('-')[1];
59 | if (main === 'top' || main === 'bottom') {
60 | if (around === 'start') {
61 | result.left = left;
62 | } else if (around === 'end') {
63 | result.left = left + popoverWidth - contentWidth;
64 | } else if (around === 'middle') {
65 | result.left = left + popoverWidth / 2 - contentWidth / 2;
66 | }
67 | if (main === 'top') {
68 | result.top = top - contentHeight - CONTANT;
69 | } else {
70 | result.top = top + popoverHeight + CONTANT;
71 | }
72 | } else if (main === 'left' || main === 'right') {
73 | if (around === 'start') {
74 | result.top = top;
75 | } else if (around === 'end') {
76 | result.top = top + popoverHeight - contentHeight;
77 | } else if (around === 'middle') {
78 | result.top = top + popoverHeight / 2 - contentHeight / 2;
79 | }
80 | if (main === 'left') {
81 | result.left = left - contentWidth - CONTANT;
82 | } else {
83 | result.left = left + popoverWidth + CONTANT;
84 | }
85 | }
86 |
87 | testDirection(result, options);
88 | }
89 |
90 | function testDirection(result, options) {
91 | let { left, top } = result,
92 | width = document.documentElement.clientWidth,
93 | height = document.documentElement.clientHeight;
94 | if (
95 | top < 0 ||
96 | left < 0 ||
97 | top + options.contentHeight > height ||
98 | left + options.contentWidth > width
99 | ) {
100 | // 还有可以循环的
101 | if (options.myList.length) {
102 | options.direction = options.myList.shift();
103 | getDirection(result, options);
104 | } else {
105 | // 实在不行就在父级身上
106 | result.left = options.left;
107 | result.right = options.right;
108 | }
109 | } else {
110 | result.show = true;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/src/components/Alert/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './main/alert';
2 |
--------------------------------------------------------------------------------
/src/components/Alert/main/alert.js:
--------------------------------------------------------------------------------
1 | import alert from './alert.vue';
2 | export default {
3 | install(Vue) {
4 | // 显示弹框
5 | Vue.prototype.$ccAlert = function(options) {
6 | let Constructor = Vue.extend(alert), node;
7 | if (typeof options === 'object' && options instanceof Object) {
8 | node = new Constructor({
9 | propsData: options
10 | });
11 | node.$slots.default = [options.message];
12 | } else if (typeof options === 'string') {
13 | node = new Constructor();
14 | node.$slots.default = [options];
15 | }
16 | node.vm = node.$mount();
17 | node.vm.visible = true
18 | document.body.appendChild(node.$el);
19 | };
20 | // 移除弹框
21 | Vue.prototype.$ccHiddenAlert = function() {
22 | document.body.childNodes.forEach(item => {
23 | if (item.className === 'cc-alert') {
24 | document.body.removeChild(item);
25 | }
26 | });
27 | };
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/src/components/Alert/main/alert.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
15 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
103 |
--------------------------------------------------------------------------------
/src/components/Button/index.js:
--------------------------------------------------------------------------------
1 | import Button from './main/button.vue'
2 |
3 | Button.install = function(Vue) {
4 | Vue.component(Button.name, Button);
5 | };
6 |
7 | export default Button
8 |
--------------------------------------------------------------------------------
/src/components/Button/main/button.vue:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
90 |
--------------------------------------------------------------------------------
/src/components/DatePicker/index.js:
--------------------------------------------------------------------------------
1 | import DatePicker from './main/datePicker.vue'
2 |
3 | DatePicker.install = function(Vue) {
4 | Vue.component(DatePicker.name, DatePicker);
5 | };
6 |
7 | export default DatePicker
8 |
--------------------------------------------------------------------------------
/src/components/DatePicker/main/datePicker.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
11 |
19 |
20 |
<
21 |
←
22 |
23 | {{formatDare.split('-')[0]}}年
24 | {{formatDare.split('-')[1]}}月
25 | {{formatDare.split('-')[2]}}日
26 |
27 |
→
28 |
>
29 |
30 |
31 |
35 |
38 | - {{getVisibeDaysIndex(i,j).day}}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
182 |
--------------------------------------------------------------------------------
/src/components/Icon/index.js:
--------------------------------------------------------------------------------
1 | import Icon from './main/icon.vue'
2 |
3 | Icon.install = function(Vue) {
4 | Vue.component(Icon.name, Icon);
5 | };
6 |
7 | export default Icon
--------------------------------------------------------------------------------
/src/components/Icon/main/icon.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/Input/index.js:
--------------------------------------------------------------------------------
1 | import Input from './main/input.vue'
2 |
3 | Input.install = function(Vue) {
4 | Vue.component(Input.name, Input);
5 | };
6 |
7 | export default Input
8 |
--------------------------------------------------------------------------------
/src/components/Input/main/input.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
22 |
36 |
45 |
55 |
56 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/src/components/InputNumber/index.js:
--------------------------------------------------------------------------------
1 | import inputNumber from './main/input-number.vue'
2 |
3 | inputNumber.install = function(Vue) {
4 | Vue.component(inputNumber.name, inputNumber);
5 | };
6 |
7 | export default inputNumber
--------------------------------------------------------------------------------
/src/components/InputNumber/main/input-number.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
27 |
28 |
29 |
111 |
--------------------------------------------------------------------------------
/src/components/Lazy/index.js:
--------------------------------------------------------------------------------
1 |
2 | export { default} from './main/lazy'
--------------------------------------------------------------------------------
/src/components/Lazy/main/lazy.js:
--------------------------------------------------------------------------------
1 | import {
2 | getScrollOffset,
3 | getViewportSize,
4 | getHTMLScroll
5 | } from '@/assets/js/utils';
6 | class Lazy {
7 | install(Vue, options) {
8 | this.vm = Vue;
9 | this.timeEl = '';
10 | this.list = new Set();
11 | this.time = options.time;
12 | this.error = options.error;
13 | this.loadingImg = options.loadingImg;
14 | this.initDirective();
15 | this.initScroll();
16 | }
17 | initDirective() {
18 | this.vm.directive('lazy', {
19 | bind: (el, data) => {
20 | if(this.loadingImg){
21 | el.setAttribute('src', this.loadingImg);
22 | }
23 | this.list.add({ oImg: el, path: data.value });
24 | }
25 | });
26 | this.vm.directive('lazy-box', {
27 | bind: el => {
28 | this.whetherHandle();
29 | el.addEventListener('scroll', this.whetherHandle.bind(this), false);
30 | }
31 | });
32 | }
33 | initScroll() {
34 | // 不管怎么样, 默认先把body监控起来把
35 | // 先触发一次, 第一屏
36 | this.whetherHandle();
37 | window.addEventListener('scroll', this.whetherHandle.bind(this), false);
38 | }
39 | whetherHandle() {
40 | // 完全停下的一瞬间开始计时
41 | clearTimeout(this.timeEl);
42 | this.timeEl = setTimeout(() => {
43 | this.handleScroll();
44 | }, this.time);
45 | }
46 | handleScroll() {
47 | for (let item of this.list) {
48 | if (this.isNoLoading(item.oImg)) {
49 | this.list.delete(item);
50 | } else {
51 | this.handleSrc(item);
52 | }
53 | }
54 | }
55 | // 处理该不该显示的问题
56 | handleSrc(item) {
57 | let { oImg, path } = item,
58 | { top: top1, left: left1 } = getHTMLScroll(oImg),
59 | { top: top2, left: left2 } = getScrollOffset(),
60 | { width, height } = getViewportSize(),
61 | height2 = oImg.offsetHeight / 2,
62 | width2 = oImg.offsetWidth / 2;
63 | if (top1 - top2 + height2 > 0 && top1 - top2 + height2 < height) {
64 | if (left1 - left2 + width2 > 0 && left1 - left2 + width2 < width) {
65 | oImg.onerror = ()=>{
66 | oImg.setAttribute('src', this.error);
67 |
68 | }
69 | oImg.setAttribute('src', path);
70 | }
71 | }
72 | }
73 |
74 | // 工具类
75 | isNoLoading(item){
76 | if(!item)return false
77 | if(item && item.src === this.loadingImg) return false
78 | return true
79 | }
80 | }
81 |
82 | export default new Lazy();
83 |
--------------------------------------------------------------------------------
/src/components/Pagination/index.js:
--------------------------------------------------------------------------------
1 | import Pagination from './main/pagination.vue';
2 |
3 | Pagination.install = function(Vue) {
4 | Vue.component(Pagination.name, Pagination);
5 | };
6 |
7 | export default Pagination;
8 |
--------------------------------------------------------------------------------
/src/components/Pagination/main/pagination.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
37 |
38 |
39 |
129 |
--------------------------------------------------------------------------------
/src/components/Popover/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './main/index';
2 |
--------------------------------------------------------------------------------
/src/components/Popover/main/index.js:
--------------------------------------------------------------------------------
1 | import Popover from './popover.vue';
2 | import prevent from '@/assets/js/prevent';
3 |
4 | Popover.install = function(Vue) {
5 | Vue.component(Popover.name, Popover);
6 | Vue.prototype.$clearPopover = function() {
7 | let ary = document.getElementsByClassName('cc-popover__content');
8 | for (let i = 0; i < ary.length; i++) {
9 | ary[i].style.display = 'none';
10 | }
11 | };
12 | // 监听指令
13 | window.addEventListener('scroll',()=>{
14 | prevent(1,() => {
15 | Vue.prototype.$clearPopover()
16 | },400);
17 | },false)
18 |
19 | Vue.directive('scroll-clear-popover', {
20 | bind: el => {
21 | el.addEventListener('scroll', ()=>{
22 | prevent(1,() => {
23 | Vue.prototype.$clearPopover()
24 | },400);
25 | }, false);
26 | }
27 | });
28 | };
29 |
30 | export default Popover;
31 |
--------------------------------------------------------------------------------
/src/components/Popover/main/popover.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
16 |
17 | 请输入内容
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
155 |
--------------------------------------------------------------------------------
/src/components/Rate/index.js:
--------------------------------------------------------------------------------
1 | import Rate from './main/rate.vue'
2 |
3 | Rate.install = function(Vue) {
4 | Vue.component(Rate.name, Rate);
5 | };
6 |
7 | export default Rate
--------------------------------------------------------------------------------
/src/components/Rate/main/rate.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
12 |
13 |
19 |
20 |
22 |
28 |
29 |
30 |
32 | {{value | fix}}
33 |
34 |
35 |
36 |
37 |
156 |
--------------------------------------------------------------------------------
/src/components/Ske/index.js:
--------------------------------------------------------------------------------
1 | import Ske from './main/ske.vue';
2 |
3 | Ske.install = function(Vue) {
4 | Vue.component(Ske.name, Ske);
5 | };
6 |
7 | export default Ske
8 |
--------------------------------------------------------------------------------
/src/components/Ske/main/ske.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
86 |
--------------------------------------------------------------------------------
/src/components/Tab/index.js:
--------------------------------------------------------------------------------
1 | import Tab from './main/tab.vue'
2 | import TabPane from './main/tab-pane.vue'
3 |
4 | Tab.install = function(Vue) {
5 | Vue.component(Tab.name, Tab);
6 | Vue.component(TabPane.name, TabPane);
7 | };
8 |
9 | export default Tab
--------------------------------------------------------------------------------
/src/components/Tab/main/tab-pane.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
30 |
--------------------------------------------------------------------------------
/src/components/Tab/main/tab.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | -
11 |
15 |
16 | {{item.label}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/Toast/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './main/toast.js';
--------------------------------------------------------------------------------
/src/components/Toast/main/toast.js:
--------------------------------------------------------------------------------
1 | import toast from './toast.vue';
2 | export default {
3 | install(Vue) {
4 | Vue.prototype.$ccToast = function(options) {
5 | let Constructor = Vue.extend(toast), node;
6 | if (typeof options === 'object' && options instanceof Object) {
7 | node = new Constructor({
8 | propsData: options
9 | });
10 | node.$slots.default = [options.message];
11 | } else if (typeof options === 'string') {
12 | node = new Constructor();
13 | node.$slots.default = [options];
14 | }
15 | node.vm = node.$mount();
16 | node.vm.visible = true
17 | document.body.appendChild(node.$el);
18 | };
19 | }
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/Toast/main/toast.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/components/Tree/index.js:
--------------------------------------------------------------------------------
1 | import Tree from './main/tree.vue';
2 |
3 | Tree.install = function(Vue) {
4 | Vue.component(Tree.name, Tree);
5 | };
6 |
7 | export default Tree
8 |
--------------------------------------------------------------------------------
/src/components/Tree/main/tree.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import tab from './Tab/index';
2 | import ske from './Ske/index';
3 | import lazy from './Lazy/index';
4 | import rate from './Rate/index';
5 | import tree from './Tree/index';
6 | import icon from './Icon/index';
7 | import alert from './Alert/index';
8 | import toast from './Toast/index';
9 | import input from './Input/index';
10 | import button from './Button/index';
11 | import loading from './loading/index';
12 | import popover from './Popover/index';
13 | import pagination from './Pagination/index';
14 | import datePicker from './DatePicker/index';
15 | import inputNumber from './InputNumber/index';
16 | // 总的样式
17 | import '../style/index.scss';
18 | // 字体样式
19 | import '../style/fonts/iconfont';
20 |
21 | const components = [
22 | ske,
23 | tab,
24 | // lazy, 因为他要很多配置, 所以需要单独玩一下
25 | icon,
26 | rate,
27 | tree,
28 | alert,
29 | input,
30 | toast,
31 | button,
32 | popover,
33 | loading,
34 | datePicker,
35 | pagination,
36 | inputNumber
37 | ];
38 |
39 | let CC = {
40 | install(Vue) {
41 | components.forEach(component => {
42 | Vue.component(component.name, component);
43 | });
44 | }
45 | };
46 |
47 | export default CC;
48 |
49 |
50 | export {
51 | ske,
52 | tab,
53 | icon,
54 | rate,
55 | tree,
56 | lazy,
57 | alert,
58 | input,
59 | toast,
60 | button,
61 | popover,
62 | loading,
63 | datePicker,
64 | pagination,
65 | inputNumber,
66 | };
67 |
--------------------------------------------------------------------------------
/src/components/loading/index.js:
--------------------------------------------------------------------------------
1 | export { default } from './main/loading.js';
--------------------------------------------------------------------------------
/src/components/loading/main/loading.js:
--------------------------------------------------------------------------------
1 | import Loading from './loading.vue';
2 |
3 | Loading.install = function(Vue) {
4 | Vue.component(Loading.name, Loading);
5 | Vue.prototype.$ccShowLoading = function(options) {
6 | let Constructor = Vue.extend(Loading);
7 | let node = new Constructor({
8 | propsData: options
9 | });
10 | node.vm = node.$mount();
11 | document.body.appendChild(node.$el);
12 | };
13 | Vue.prototype.$ccHiddenLoading = function() {
14 | document.body.childNodes.forEach(item => {
15 | if (item.className === 'cc-loading') {
16 | document.body.removeChild(item);
17 | }
18 | });
19 | };
20 | };
21 |
22 | export default Loading;
--------------------------------------------------------------------------------
/src/components/loading/main/loading.vue:
--------------------------------------------------------------------------------
1 |
2 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | // import App from './(不会被打包)App/tab.vue';
3 | import App from './(不会被打包)App/按钮输入.vue';
4 | // import App from './(不会被打包)App/计数器.vue';
5 | // import App from './(不会被打包)App/小星星.vue';
6 | // import App from './(不会被打包)App/骨架.vue';
7 | // import App from './(不会被打包)App/懒加载.vue';
8 | // import App from './(不会被打包)App/分页器.vue';
9 | // import App from './(不会被打包)App/popover.vue'
10 | // import App from './(不会被打包)App/日期选择器.vue';
11 | // import App from './(不会被打包)App/tree.vue'
12 | // import App from './(不会被打包)App/弹出框.vue';
13 |
14 | import {
15 | tab,
16 | ske,
17 | rate,
18 | lazy,
19 | tree,
20 | icon,
21 | input,
22 | toast,
23 | alert,
24 | button,
25 | popover,
26 | loading,
27 | datePicker,
28 | pagination,
29 | inputNumber,
30 | } from './components/index';
31 | Vue.use(button)
32 | .use(tab)
33 | .use(ske)
34 | .use(icon)
35 | .use(tree)
36 | .use(rate)
37 | .use(alert)
38 | .use(input)
39 | .use(toast)
40 | .use(popover)
41 | .use(loading)
42 | .use(datePicker)
43 | .use(pagination)
44 | .use(inputNumber)
45 | .use(lazy, {
46 | time: 200,
47 | loadingImg:'http://img.mp.sohu.com/upload/20170715/7b3ce9cd49604308bdd2b7755f52cab9_th.png',
48 | error:'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1564597486512&di=9f7ccdadddbd47791ed80eac0890c5e5&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fblog%2F201501%2F03%2F20150103183647_ZxLtT.jpeg'
49 | });
50 |
51 | new Vue({
52 | render: h => h(App)
53 | }).$mount('#app');
54 |
--------------------------------------------------------------------------------
/src/style/Alert.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/mixin.scss';
3 | @import './common/extend.scss';
4 |
5 | @include b(alert) {
6 | &::after {
7 | content: '';
8 | @include position(fixed);
9 | background-color: $--color-black;
10 | opacity: 0.7;
11 | }
12 |
13 | @include e(context) {
14 | position: absolute;
15 | background-color: white;
16 | @include commonShadow($--color-black, 10px);
17 | top: 50%;
18 | left: 50%;
19 | z-index: 2;
20 | min-width: 400px;
21 | border-radius: 5px;
22 | padding: 15px 20px;
23 | transform: translate(-50%, -50%);
24 |
25 | &>header {
26 | display: flex;
27 | align-items: center;
28 | justify-content: space-between;
29 | font-size: 21px;
30 | font-weight: 200;
31 | }
32 |
33 | &>article {
34 | padding: 15px 0;
35 | min-height: 40px;
36 | }
37 |
38 | &>footer {
39 | display: flex;
40 | justify-content: flex-end;
41 |
42 | button:nth-of-type(1) {
43 | margin-right: 15px;
44 | }
45 | }
46 | }
47 |
48 | @include e(icon) {
49 | cursor: pointer;
50 | transition: all .2s;
51 | &:hover{
52 | transform: scale(20)
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/style/Button.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/mixin.scss';
3 | @import './common/extend.scss';
4 |
5 | @include b(button) {
6 | cursor: pointer;
7 | overflow: hidden;
8 | position: relative;
9 | align-items: center;
10 | display: inline-flex;
11 | vertical-align: middle;
12 | justify-content: center;
13 | background-color: white;
14 | outline: 0;
15 | border-radius: 6px;
16 | transition: all 0.1s;
17 |
18 | &.size-big {
19 | font-size: $--size-big;
20 | padding: 6px 11px;
21 | }
22 |
23 | &.size-normal {
24 | font-size: $--size-nomal;
25 | padding: 4px 8px;
26 | }
27 |
28 | &.size-small {
29 | font-size: $--size-small;
30 | padding: 2px 6px;
31 | }
32 |
33 | &:not(.is-disabled) {
34 | &:active {
35 | box-shadow: none;
36 | opacity: 0.7;
37 | transform: translateX(2px) translateY(2px) scale(0.9);
38 | }
39 |
40 | &:hover {
41 | background-color:rgba(0, 0, 0, 0.01)
42 | }
43 | }
44 |
45 | @include commonShadow($--color-black);
46 |
47 | @include when(disabled) {
48 | @include commonShadow(disabled);
49 | }
50 |
51 | @include when(left) {
52 | border-radius: 16px 0 0 16px;
53 | }
54 |
55 | ;
56 |
57 | @include when(right) {
58 | border-radius: 0 16px 16px 0;
59 | }
60 |
61 | ;
62 |
63 | @include when(centre) {
64 | border-radius: 0;
65 | }
66 |
67 | @at-root {
68 | @include commonType(cc-button--);
69 | .is-bling {
70 | &:hover {
71 | @extend .cc-bling;
72 | }
73 | }
74 | };
75 | }
--------------------------------------------------------------------------------
/src/style/DatePicker.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/mixin.scss';
3 | @import './common/extend.scss';
4 |
5 | @include b(date) {
6 | position: relative;
7 | display: inline-block;
8 |
9 | @include b(date-input) {
10 | border: 1px solid $--color-disabled;
11 | outline: 0px;
12 | padding: 8px;
13 | font-size: 16px;
14 | border-radius: 7px;
15 | }
16 |
17 | @include b(date-pannel) {
18 | position: fixed;
19 | background-color: $--color-white;
20 | border: 1px solid $--color-disabled;
21 | width: 280px;
22 | padding: 8px;
23 | border-radius: 7px;
24 |
25 | .pannel-nav {
26 | display: flex;
27 | align-items: center;
28 | justify-content: space-around;
29 | box-shadow: 0px 2px 2px 2px $--color-difference;
30 | padding: 6px 0;
31 | margin-bottom: 10px;
32 |
33 | .pannel-selected {
34 | text-align: center;
35 | width: 160px;
36 | }
37 |
38 | &>span {
39 | &:hover {
40 | cursor: pointer;
41 | color: $--color-nomal
42 | }
43 | }
44 | }
45 |
46 | .pannel-content {
47 | box-shadow: 0px 2px 2px 2px $--color-difference;
48 |
49 | ul {
50 | display: flex;
51 | }
52 |
53 | li {
54 | text-align: center;
55 | flex: 1;
56 | height: 35px;
57 | line-height: 35px;
58 | }
59 |
60 | .read-only {
61 | color: $--color-disabled;
62 | }
63 |
64 | .active-date {
65 | @extend .active-item;
66 | }
67 |
68 | .pannel-content__item {
69 | cursor: pointer;
70 | border: 1px solid $--color-difference;
71 |
72 | li:not(.read-only) {
73 | transition: all .2s;
74 | transform: scale(.8);
75 |
76 | &:hover {
77 | transform: scale(1.3);
78 | @extend .active-item;
79 | }
80 | }
81 | }
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/style/Icon.scss:
--------------------------------------------------------------------------------
1 | @import './common/mixin.scss';
2 | @import "./common/animation.scss";
3 |
4 | @include b(icon) {
5 | vertical-align: middle;
6 | .#{$state-prefix}disabled {
7 | cursor: not-allowed;
8 | fill: $--color-disabled;
9 | }
10 | .icon-loading {
11 | animation: rotating 1s infinite linear;
12 | }
13 | }
--------------------------------------------------------------------------------
/src/style/Input.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/extend.scss';
3 | @import './common/mixin.scss';
4 | @import './config/index.scss';
5 |
6 | @include b(input) {
7 | cursor: pointer;
8 | position: relative;
9 | align-items: center;
10 | display: inline-flex;
11 | background-color: white;
12 | transition: all .3s;
13 | @include b(input__inner) {
14 | border: none;
15 | flex: 1;
16 | width: 100%;
17 | font-size: 1em;
18 | padding: 9px 16px;
19 | &:focus { outline: 0; }
20 | @include placeholder{
21 | color: $--color-input-placeholder;
22 | }
23 | };
24 | @include b(input__prefix) {
25 | align-items: center;
26 | display: inline-flex;
27 | &:hover{transform: scale(1.1)}
28 | @include when(left) {
29 | padding-left:6px;
30 | }
31 | @include when(right) {
32 | padding-right:6px;
33 | }
34 | };
35 | @include b(input__clear){
36 | position: absolute;
37 | right: 24px;
38 | &:hover{ animation: size .5s infinite linear;}
39 | };
40 | @include b(input--input__disabled){
41 | @include commonShadow(disabled);
42 | };
43 | @at-root {
44 | @include b(input__normal){
45 | @include commonShadow($--color-black);
46 | &:hover {
47 | z-index: 6;
48 | transform: scale(1.2);
49 | }
50 | }
51 | @include b(input__error){
52 | @include commonShadow(danger);
53 | }
54 | @include b(input__abnormal){
55 | @include commonShadow($--color-black);
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/src/style/Loading.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/extend.scss';
3 | @import './common/mixin.scss';
4 |
5 | @include b(loading) {
6 | @include position();
7 | &__curtain{
8 | @include position();
9 | }
10 | &__icon{
11 | position: absolute;
12 | color: $--color-nomal;
13 | z-index: 10;
14 | @include flexCenter;
15 | @include position();
16 | :nth-child(1){
17 | margin-bottom:6px;
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/style/Pagination.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/extend.scss';
3 | @import './common/mixin.scss';
4 | @import './config/index.scss';
5 |
6 | @include b(pagination) {
7 | cursor: pointer;
8 | color: #606266;
9 | align-items: center;
10 | display: inline-flex;
11 | justify-content: center;
12 | .btn-prev {
13 | border: none;
14 | outline: none;
15 | background-color: transparent;
16 | &:hover {
17 | color: $--color-nomal
18 | }
19 | }
20 |
21 | .total-number {
22 | font-size: 13px;
23 | margin-right: 5px;
24 | }
25 |
26 | @include e(box) {
27 | flex-wrap: wrap;
28 | display: inline-flex;
29 | &>li {
30 | min-width: 30px;
31 | margin: 3px 5px;
32 | padding: 4px 8px;
33 | @include flexCenter();
34 | @include when(active) {
35 | color: $--color-nomal
36 | }
37 | &:hover {
38 | color: $--color-nomal
39 | }
40 | }
41 | }
42 | .ground {
43 | background-color: #f4f4f5;
44 | border-radius: 4px;
45 | &:hover {
46 | color: white;
47 | background-color: $--color-nomal;
48 | }
49 | }
50 | .ground-box { // 背景色是关键字
51 | &>li {
52 | @extend .ground;
53 | }
54 | &>.is-active{
55 | color: white;
56 | background-color: $--color-nomal;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/src/style/Popover.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/extend.scss';
3 | @import './common/mixin.scss';
4 | @import './config/index.scss';
5 |
6 |
7 | @include b(popover) {
8 | position: relative;
9 | display: inline-block;
10 | @include e(box) {
11 | overflow: auto;
12 | }
13 | @include e(content) {
14 | position: absolute;
15 | color: $--color-weak;
16 | background-color: white;
17 | box-shadow: 0px 0px 3px rgb(172, 171, 171);
18 | top: 100px;
19 | left: 100px;
20 | padding: 6px;
21 | @at-root {
22 | .top-end {
23 | @include triangleAfter(top){
24 | right:5px;
25 | };
26 | @include triangleBefore(top){
27 | right:5px;
28 | };
29 | }
30 | .top-middle {
31 | @include triangleAfter(top){
32 | left: calc(50% - 6px);
33 | };
34 | @include triangleBefore(top){
35 | left: calc(50% - 6px);
36 | };
37 | }
38 | .top-start {
39 | @include triangleAfter(top){
40 | left: 5px
41 | };
42 | @include triangleBefore(top){
43 | left: 5px
44 | };
45 | }
46 |
47 | .bottom-end {
48 | @include triangleAfter(bottom){
49 | right:5px;
50 | };
51 | @include triangleBefore(bottom){
52 | right:5px;
53 | };
54 | }
55 | .bottom-middle {
56 | @include triangleAfter(bottom){
57 | left: calc(50% - 6px);
58 | };
59 | @include triangleBefore(bottom){
60 | left: calc(50% - 6px);
61 | };
62 | }
63 | .bottom-start {
64 | @include triangleAfter(bottom){
65 | left: 5px
66 | };
67 | @include triangleBefore(bottom){
68 | left: 5px
69 | };
70 | }
71 |
72 | .right-end {
73 | @include triangleAfter(right){
74 | bottom: 5px;
75 | };
76 | @include triangleBefore(right){
77 | bottom: 5px;
78 | };
79 | }
80 | .right-middle {
81 | @include triangleAfter(right){
82 | top: calc(50% - 6px);
83 | };
84 | @include triangleBefore(right){
85 | top: calc(50% - 6px);
86 | };
87 | }
88 | .right-start {
89 | @include triangleAfter(right){
90 | top: 5px
91 | };
92 | @include triangleBefore(right){
93 | top: 5px
94 | };
95 | }
96 |
97 |
98 | .left-end {
99 | @include triangleAfter(left){
100 | bottom: 5px;
101 | };
102 | @include triangleBefore(left){
103 | bottom: 5px;
104 | };
105 | }
106 | .left-middle {
107 | @include triangleAfter(left){
108 | top: calc(50% - 6px);
109 | };
110 | @include triangleBefore(left){
111 | top: calc(50% - 6px);
112 | };
113 | }
114 | .left-start {
115 | @include triangleAfter(left){
116 | top: 5px
117 | };
118 | @include triangleBefore(left){
119 | top: 5px
120 | };
121 | }
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/src/style/Rate.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/extend.scss';
3 | @import './common/mixin.scss';
4 |
5 | @include b(rate) {
6 | position: relative;
7 | align-items: center;
8 | display: inline-flex;
9 | justify-content: center;
10 |
11 | @include e (dark) {
12 | display: flex;
13 | align-items: center;
14 | height: 100%;
15 | padding-right:3px ;
16 | transition: all .3;
17 | }
18 |
19 | @include e (bright) {
20 | overflow: hidden;
21 | flex-wrap: nowrap;
22 | position: absolute;
23 | align-items: center;
24 | display: inline-flex;
25 | top: 0;
26 | left: 0;
27 | height: 100%;
28 | transition: all .3;
29 | }
30 |
31 | @include e(score) {
32 | margin-left: 6px;
33 | }
34 |
35 | @include m(big) {
36 | transform: scale(.8)
37 | }
38 | }
--------------------------------------------------------------------------------
/src/style/Ske.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/mixin.scss';
3 | @import './common/extend.scss';
4 |
5 | @include b(ske) {
6 | background-color: white;
7 | @include position(fixed);
8 |
9 | @include e(box) {
10 | overflow: hidden;
11 | @include position(absolute, 30px);
12 | }
13 |
14 | @include e(base) {
15 | background-color: #F6F6F6;
16 | width: 100%;
17 | z-index: -1; // 为了伪类能够被挡住
18 | }
19 |
20 | @include e(round) {
21 | display: flex;
22 | position: absolute;
23 | align-items: center;
24 | justify-content: center;
25 | background-color: white;
26 | left: 0px;
27 | width: 180px;
28 | height: 180px;
29 |
30 | &::after {
31 | content: '';
32 | position: absolute;
33 | background-color: #F6F6F6;
34 | width: 70%;
35 | height: 70%;
36 | border-radius: 50%;
37 | }
38 | }
39 |
40 | @include e(rec) {
41 | position: absolute;
42 | background-color: #F6F6F6;
43 | left: 0px;
44 | bottom: 0;
45 | right: 0px;
46 | height: 300px;
47 | @at-root {
48 | @include m(big) {
49 | position: absolute;
50 | background-color: #F6F6F6;
51 | border-right: 20px solid white;
52 | top: 0px;
53 | left: 0px;
54 | width: 260px;
55 | height: 100%;
56 | }
57 | }
58 | }
59 | .across {
60 | // 透明的白色, 惊艳了
61 | background-color: white;
62 | animation: pass 2s infinite linear;
63 | width: 30px;
64 | opacity: 0.8;
65 | height: 2000px;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/style/Tab.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/mixin.scss';
3 | @import './common/extend.scss';
4 |
5 | @include b(tab) {
6 | @include brother(nav) {
7 | display: flex;
8 | flex-wrap: nowrap;
9 | text-align: center;
10 | border-bottom: 1px solid #eee;
11 | margin-bottom: 10px;
12 | &>li {
13 | cursor: pointer;
14 | display: flex;
15 | position: relative;
16 | align-items: center;
17 | border-bottom: none;
18 | background-color: white;
19 | padding: 10px 20px;
20 | transition: all 0.2s;
21 | &:hover {
22 | transform: scale(0.8)
23 | };
24 | &::after {
25 | content: '';
26 | position: absolute;
27 | left: 6px;
28 | bottom: 0;
29 | right: 6px;
30 | transform: scale(0);
31 | transition: all 0.2s;
32 | }
33 | @include when(active) {
34 | color: $--color-nomal;
35 | &::after {
36 | border-bottom: 2px solid $--color-nomal;
37 | transform: scale(1);
38 | }
39 | }
40 | }
41 | @include when(card) {
42 | &::after {
43 | display: none
44 | }
45 | &>li {
46 | border-bottom: none;
47 | border: 1px solid #eee;
48 | &:hover {
49 | transform: scale(1)
50 | }
51 | };
52 | &>li+li {
53 | border-left: none
54 | };
55 | &>.is-active {
56 | border-bottom: none;
57 | &::after {
58 | content: '';
59 | position: absolute;
60 | border-bottom: 2px solid white;
61 | left: 0;
62 | right: 0;
63 | bottom: -1px;
64 | }
65 | };
66 | &>:nth-last-child(1) {
67 | border-top-right-radius: 7px;
68 | };
69 | &>:nth-child(1) {
70 | border-top-left-radius: 7px;
71 | };
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/style/Toast.scss:
--------------------------------------------------------------------------------
1 | @import './common/mixin.scss';
2 | @import "./common/animation.scss";
3 |
4 | @include b(toast) {
5 | position: fixed;
6 | display: inline-block;
7 | background-color: white;
8 | top: 6px;
9 | left: 50%;
10 | padding: 6px 20px;
11 | transition: all .3s;
12 | transform: translateX(-50%);
13 | @include commonShadow($--color-black);
14 | .#{$namespace}-toast__closeButton {
15 | display: flex;
16 | cursor: pointer;
17 | position: absolute;
18 | align-items: center;
19 | justify-content: center;
20 | top: 0;
21 | right: 0;
22 | bottom: 0;
23 | padding: 0 5px;
24 | &:hover {
25 | animation: rotating .5s infinite linear;
26 | }
27 | }
28 | @at-root {
29 | @include commonType(cc-toast--);
30 | .#{$namespace}-toast--big {
31 | padding: 6px 35px 6px 20px;
32 | }
33 | .#{$namespace}-toast-fade-enter,
34 | .#{$namespace}-toast-fade-leave-active {
35 | opacity: 0;
36 | transform: translate(-50%, -100%);
37 | }
38 | };
39 | }
--------------------------------------------------------------------------------
/src/style/Tree.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/mixin.scss';
3 | @import './common/extend.scss';
4 | @import "./common/animation.scss";
5 |
6 | @include b(tree) {
7 | color: $--color-weak;
8 | font-size: 14px;
9 | font-weight: 400;
10 | text-indent: 1em;
11 | @include e(item) {
12 | margin: 2px 0;
13 | }
14 | @include e(content) {
15 | position: relative;
16 | padding: 3px 0;
17 | &:hover {
18 | background-color: $--color-difference;
19 | }
20 | }
21 | @include e(icon) {
22 | overflow: hidden;
23 | position: relative;
24 | display: inline-block;
25 | border-bottom: 3px solid $--color-danger;
26 | width: .5em;
27 | height: .5em;
28 | margin-right:5px;
29 | transform: scale(.8);
30 | transition: all 0.3s;
31 | &::after {
32 | content: '';
33 | display: block;
34 | position: absolute;
35 | background-color: $--color-danger;
36 | left: 50%;
37 | width: 80%;
38 | height: 100%;
39 | }
40 | }
41 | @include e(checkbox){
42 | margin-right: 5px;
43 | transform: translateY(-1px)
44 | }
45 | .is-open {
46 | transform: rotate(90deg) scale(1);
47 | }
48 | .is-active{
49 | background-color: #F5F7FA;
50 | }
51 | .is-all__check{
52 | @extend .check-ball;
53 | background-color: $--color-warning;
54 | }
55 | .is-all__nocheck{
56 | @extend .check-ball;
57 | background-color: $--color-nomal;
58 | }
59 | }
--------------------------------------------------------------------------------
/src/style/common/animation.scss:
--------------------------------------------------------------------------------
1 | @keyframes rotating {
2 | 0% {
3 | transform: rotateZ(0deg);
4 | }
5 |
6 | 100% {
7 | transform: rotateZ(360deg);
8 | }
9 | }
10 |
11 | @keyframes size {
12 | 0% {
13 | transform: scale(1);
14 | }
15 |
16 | 100% {
17 | transform: scale(1.1);
18 | }
19 | }
20 |
21 | @keyframes bling {
22 | 0% {
23 | left: 0;
24 | }
25 |
26 | 100% {
27 | left: 310%;
28 | }
29 | }
30 |
31 |
32 | @keyframes pass {
33 | 0% {
34 | transform: rotate(-45deg) translate(0px);
35 | }
36 |
37 | 100% {
38 | transform: rotate(-45deg) translate(2000px);
39 | }
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/src/style/common/extend.scss:
--------------------------------------------------------------------------------
1 | @import './var.scss';
2 | @import './animation.scss';
3 |
4 | .cc-bling {
5 | &:after {
6 | content: '';
7 | position: absolute;
8 | animation: bling 1s infinite linear;
9 | background-image: linear-gradient(to right, rgb(232, 229, 229), white);
10 | left: 0;
11 | top: -20px;
12 | width: 15px;
13 | height: calc(100% + 30px);
14 | transform: rotate(-30deg);
15 | }
16 | }
17 |
18 | .active-item {
19 | color: $--color-white;
20 | background-color: $--color-nomal;
21 | border-radius: 10px;
22 | }
23 |
24 | .check-ball{
25 | display: inline-block;
26 | width: .5em;
27 | height: .5em;
28 | margin:0 5px;
29 | border-radius: 100%;
30 | }
--------------------------------------------------------------------------------
/src/style/common/mixin.scss:
--------------------------------------------------------------------------------
1 | @import '../config/index.scss';
2 | @import '../common/var.scss';
3 |
4 | @mixin commonShadow($color,$size:2px) {
5 | @if $color== 'success' {
6 | $color: $--color-success;
7 | }
8 |
9 | @if $color== 'warning' {
10 | $color: $--color-warning;
11 | }
12 |
13 | @if $color== 'danger' {
14 | $color: $--color-danger;
15 | }
16 |
17 | @if $color== 'disabled' {
18 | cursor: not-allowed;
19 | $color: $--color-disabled;
20 | }
21 |
22 | color: $color;
23 | border: 1px solid $color;
24 | box-shadow: $size $size $color;
25 | }
26 |
27 | @mixin commonType($name) {
28 | @each $type in (success, warning, danger) {
29 | .#{$name}#{$type} {
30 | @include commonShadow($type);
31 | }
32 | }
33 | }
34 |
35 | @mixin b($block) {
36 | // !global与!defult相对立, 优先与默认编译
37 | $B: $namespace + '-' + $block !global;
38 |
39 | .#{$B} {
40 | @content;
41 | }
42 | }
43 |
44 | @mixin when($state) {
45 | @at-root {
46 | &.#{$state-prefix + $state} {
47 | @content;
48 | }
49 | }
50 | }
51 |
52 | @mixin brother($name) {
53 | &>{-$name} {
54 | @content;
55 | }
56 | }
57 |
58 | @mixin e($name) {
59 | {$cc-separator}#{$name} {
60 | @content;
61 | }
62 | }
63 |
64 | @mixin m($name) {
65 | {$cc-modifier-separator}#{$name} {
66 | @content;
67 | }
68 | }
69 |
70 |
71 |
72 | @mixin placeholder {
73 | &::-webkit-input-placeholder {
74 | @content;
75 | }
76 |
77 | &::-moz-placeholder {
78 | @content;
79 | }
80 |
81 | &:-ms-input-placeholder {
82 | @content;
83 | }
84 | }
85 |
86 | @mixin position($position: absolute, $long: 0) {
87 | margin: auto;
88 | position: $position;
89 | top: $long;
90 | left: $long;
91 | right: $long;
92 | bottom: $long;
93 | }
94 |
95 | @mixin flexCenter ($direction:column) {
96 | display: flex;
97 | align-items: center;
98 | justify-content: center;
99 | flex-direction: $direction;
100 | }
101 |
102 |
103 | @mixin triangleAfter ($where){
104 | &::after{
105 | content: '';
106 | position: absolute;
107 | border: 6px solid white;
108 | border-top-color: transparent;
109 | border-left-color: transparent;
110 | border-right-color: transparent;
111 | border-bottom-color: transparent;
112 | @if $where== 'top' {
113 | bottom: -11px;
114 | border-top-color: white;
115 | }
116 | @if $where== 'right' {
117 | left: -11px;
118 | border-right-color:white;
119 | }
120 | @if $where== 'left' {
121 | right: -11px;
122 | border-left-color:white;
123 | }
124 | @if $where== 'bottom' {
125 | top: -11px;
126 | border-bottom-color: white;
127 | }
128 | @content;
129 | };
130 | }
131 | @mixin triangleBefore ($where){
132 | &::before{
133 | content: '';
134 | position: absolute;
135 | border: 6px solid rgb(172, 171, 171);
136 | border-top-color: transparent;
137 | border-left-color: transparent;
138 | border-right-color: transparent;
139 | border-bottom-color: transparent;
140 | @if $where== 'top' {
141 | bottom: -12px;
142 | border-top-color: rgb(172, 171, 171);
143 | }
144 | @if $where== 'right' {
145 | left:-12px;
146 | border-right-color: rgb(172, 171, 171);
147 | }
148 | @if $where== 'left' {
149 | right: -12px;
150 | border-left-color: rgb(172, 171, 171);
151 | }
152 | @if $where== 'bottom' {
153 | top: -12px;
154 | border-bottom-color: rgb(172, 171, 171);
155 | }
156 | @content;
157 | };
158 | }
--------------------------------------------------------------------------------
/src/style/common/reset.scss:
--------------------------------------------------------------------------------
1 | li,
2 | ul {
3 | list-style: none;
4 | margin: 0;
5 | padding: 0;
6 | box-sizing: border-box;
7 | }
8 |
9 | // 干掉input框typr=number时的上下按钮
10 | input::-webkit-outer-spin-button,
11 | input::-webkit-inner-spin-button {
12 | -webkit-appearance: none;
13 | }
14 |
15 | input[type="number"] {
16 | -moz-appearance: textfield;
17 | }
18 |
19 | // toast
20 | .leave-leave-active {
21 | transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
22 | }
23 |
24 | .leave-leave-to {
25 | transform: translateX(10px);
26 | opacity: 0;
27 | }
28 |
29 | // 渐隐渐现
30 | .fade-enter-active,
31 | .fade-leave-active {
32 | transition: opacity .5s;
33 | }
34 |
35 | .fade-enter,
36 | .fade-leave-to
37 | {
38 | opacity: 0;
39 | }
40 |
41 | // 弹出框
42 | .cc-alert-fade-enter-active,
43 | .cc-alert-fade-leave-active {
44 | transition: all .4s;
45 | }
46 |
47 | .cc-alert-fade-enter,
48 | .cc-alert-fade-leave-to
49 | {
50 | transform: translateY(10px)
51 | }
--------------------------------------------------------------------------------
/src/style/common/var.scss:
--------------------------------------------------------------------------------
1 | // 基本色
2 | $--color-black:#000000 !default;
3 | $--color-white:#FFFFFF !default;
4 | // 基本色鲜艳
5 | $--color-weak:#606266 !default;
6 | $--color-nomal:#409EFF !default;
7 | $--color-success:#7CCD7C !default;
8 | $--color-warning:#FFB90F !default;
9 | $--color-danger: #FF0000 !default;
10 | $--color-disabled: #bbbbbb !default;
11 | $--color-difference: #F5F7FA !default;
12 | // 字体
13 | $--size-big: 16px;
14 | $--size-nomal: 15px;
15 | $--size-small: 14px;
16 | // 输入框
17 | $--color-input-placeholder:#aaa
18 |
--------------------------------------------------------------------------------
/src/style/config/index.scss:
--------------------------------------------------------------------------------
1 | $namespace: 'cc';
2 | $state-prefix: 'is-';
3 |
4 | $cc-separator: '__';
5 | $cc-modifier-separator: '--';
6 |
--------------------------------------------------------------------------------
/src/style/fonts/iconfont.js:
--------------------------------------------------------------------------------
1 | !function(s){var c,t='',l=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(l&&!s.__iconfont__svg__cssinject__){s.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(o=c,a=s.document,i=!1,(e=function(){try{a.documentElement.doScroll("left")}catch(c){return void setTimeout(e,50)}t()})(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,t())});function t(){i||(i=!0,o())}var o,a,i,e}(function(){var c,l;(c=document.createElement("div")).innerHTML=t,t=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",function(c,l){l.firstChild?function(c,l){l.parentNode.insertBefore(c,l)}(c,l.firstChild):l.appendChild(c)}(l,document.body))})}(window);
--------------------------------------------------------------------------------
/src/style/index.scss:
--------------------------------------------------------------------------------
1 |
2 | @import './Tab.scss';
3 | @import './Ske.scss';
4 | @import './Icon.scss';
5 | @import './Rate.scss';
6 | @import './Tree.scss';
7 | @import './Toast.scss';
8 | @import './Input.scss';
9 | @import './Alert.scss';
10 | @import './Button.scss';
11 | @import './Loading.scss';
12 | @import './Popover.scss';
13 | @import './Pagination.scss';
14 | @import './DatePicker.scss';
15 | @import './inputNumber.scss';
16 | @import './common/reset.scss';
--------------------------------------------------------------------------------
/src/style/inputNumber.scss:
--------------------------------------------------------------------------------
1 | @import './common/var.scss';
2 | @import './common/extend.scss';
3 | @import './common/mixin.scss';
4 | @import './config/index.scss';
5 |
6 | @include b(input-number) {
7 | cursor: pointer;
8 | align-items: center;
9 | display: inline-flex;
10 | background-color: white;
11 | transition:all .1s;
12 | &:hover {
13 | z-index: 6;
14 | transform: scale(1.2);
15 | }
16 | @include commonShadow($--color-black);
17 | @include e(add) {
18 | padding: 4px 6px;
19 | @include flexCenter();
20 | }
21 | @include e(reduce) {
22 | padding: 4px 6px;
23 | @include flexCenter();
24 | }
25 | @include e(input) {
26 | border: none;
27 | outline:none;
28 | display: block;
29 | text-align: center;
30 | width:60px;
31 | height: 20px;
32 | }
33 | }
--------------------------------------------------------------------------------
/tests/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | jest: true
4 | }
5 | }
--------------------------------------------------------------------------------
/tests/unit/Button.test.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils';
2 | import Button from '../../src/components/Button';
3 | import { findTestWrapper } from '../utils/util';
4 |
5 |
6 | describe('测试button组件', () => {
7 |
8 | it('1: 可以渲染出button组件', () => {
9 | const wrapper = shallowMount(Button);
10 | expect(wrapper.contains('button')).toBe(true);
11 | });
12 |
13 | it('2: button组件点击时会触发click事件', () => {
14 | const wrapper = shallowMount(Button);
15 | const button = findTestWrapper(wrapper,'button').at(0);
16 | button.trigger('click');
17 | expect(wrapper.emitted().click).toBeTruthy();
18 | });
19 |
20 | it('3: 传入icon参数, 可以显示icon组件', () => {
21 | const wrapper = shallowMount(Button,{
22 | propsData:{
23 | icon:'cc-up'
24 | }
25 | });
26 | const icon = findTestWrapper(wrapper,'icon').at(0);
27 | expect(icon).toBeTruthy();
28 | });
29 |
30 | });
31 |
--------------------------------------------------------------------------------
/tests/unit/Input.test.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils';
2 | import Input from '../../src/components/Input';
3 | import { findTestWrapper } from '../utils/util';
4 |
5 |
6 | describe('测试Input组件', () => {
7 |
8 | it('1: 可以渲染出Input组件', () => {
9 | const wrapper = shallowMount(Input);
10 | expect(wrapper.contains('input')).toBe(true);
11 | });
12 |
13 | it('2: 输入value与显示的内容相同, 并且修改联动', () => {
14 | const wrapper = shallowMount(Input,{
15 | propsData:{
16 | value:'内容1'
17 | }
18 | });
19 | const input = findTestWrapper(wrapper,'input').at(0);
20 | expect(input.element.value).toBe('内容1')
21 | // 改变也随之改变
22 | wrapper.setProps({ value: '内容2' })
23 | expect(input.element.value).toBe('内容2')
24 | });
25 |
26 | it('3: 清除内容按钮有效', () => {
27 | const wrapper = shallowMount(Input,{
28 | propsData:{
29 | value:'内容1',
30 | clear:true
31 | }
32 | });
33 | // hover 时候才会出现!!
34 | wrapper.setData({
35 | hovering:true
36 | })
37 | const clear = findTestWrapper(wrapper,'clear').at(0);
38 | expect(clear).toBeTruthy();
39 |
40 | clear.trigger('click');
41 |
42 | expect(wrapper.emitted().input).toBeTruthy();
43 | });
44 |
45 | it('4: 传入icon参数, 可以显示icon组件', () => {
46 | const wrapper = shallowMount(Input,{
47 | propsData:{
48 | icon:'cc-up'
49 | }
50 | });
51 | const icon = findTestWrapper(wrapper,'icon').at(0);
52 | expect(icon).toBeTruthy();
53 | });
54 |
55 | it('5: 切换type, 出现文本框', () => {
56 | const wrapper = shallowMount(Input,{
57 | propsData:{
58 | type:'textarea'
59 | }
60 | });
61 | const textarea = findTestWrapper(wrapper,'textarea').at(0);
62 | expect(textarea).toBeTruthy();
63 | });
64 |
65 | });
66 |
--------------------------------------------------------------------------------
/tests/unit/Loading.test.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils';
2 | import Loading from '../../src/components/Loading';
3 | import { findTestWrapper } from '../utils/util';
4 |
5 | describe('测试Loading组件', () => {
6 | it('1: 可以渲染出Loading组件', () => {
7 | const wrapper = shallowMount(Loading);
8 | // 渲染出的dom里面 有class为.cc-loading的dom
9 | expect(wrapper.classes()).toContain('cc-loading');
10 | });
11 |
12 | it('2: 传入title显示是否正确', () => {
13 | const wrapper = shallowMount(Loading, {
14 | propsData: {
15 | title: '正在努力ing'
16 | }
17 | });
18 | const title = findTestWrapper(wrapper, 'title').at(0);
19 | expect(title.text()).toBe('正在努力ing');
20 | });
21 | });
22 |
--------------------------------------------------------------------------------
/tests/unit/Pagination.test.js:
--------------------------------------------------------------------------------
1 | import { shallowMount } from '@vue/test-utils';
2 | import Pagination from '../../src/components/Pagination';
3 | import { findTestWrapper } from '../utils/util';
4 |
5 | describe('测试分页器组件', () => {
6 | it('1: 可以渲染出分页器组件', () => {
7 | const wrapper = shallowMount(Pagination,{
8 | propsData:{
9 | pageTotal:5,
10 | value:1
11 | }
12 | });
13 | expect(wrapper.classes()).toContain('cc-pagination');
14 | });
15 |
16 | it('2: 传入1000页是否显示1000页', () => {
17 | const wrapper = shallowMount(Pagination, {
18 | propsData:{
19 | pageTotal:1000,
20 | pageSize:1000,
21 | value:1
22 | }
23 | });
24 | const li = findTestWrapper(wrapper, 'item');
25 | expect(li.length).toBe(1000);
26 | });
27 |
28 | it('3: 点击第三页是否跳转到第三页', () => {
29 | const wrapper = shallowMount(Pagination, {
30 | propsData:{
31 | pageTotal:10,
32 | pageSize:10,
33 | value:1
34 | }
35 | });
36 | wrapper.vm.handlClick(3)
37 | // 发送事件
38 | expect(wrapper.emitted().input).toBeTruthy();
39 | // 发送事件的参数, 注意,是数组的形式
40 | expect(wrapper.emitted().input[0]).toEqual([3])
41 | });
42 | });
--------------------------------------------------------------------------------
/tests/unit/Toast.test.js:
--------------------------------------------------------------------------------
1 | import toast from '../../src/components/Toast/main/toast';
2 | import Vue from 'vue';
3 |
4 | describe('测试toast组件', () => {
5 | it('1: 将方法挂载原型上', () => {
6 | toast.install(Vue);
7 | expect(Vue.prototype.$ccToast).toBeTruthy();
8 | });
9 |
10 | it('2: 传入参数, 形成dom', () => {
11 | toast.install(Vue);
12 | Vue.prototype.$ccToast({
13 | message: '123'
14 | });
15 | let node = document.body.childNodes[0],
16 | classList = 'cc-toast cc-toast--nomal cc-toast--big';
17 | expect(node.className).toBe(classList);
18 | });
19 | });
20 |
--------------------------------------------------------------------------------
/tests/utils/util.js:
--------------------------------------------------------------------------------
1 | // 1: 简易选择
2 | export const findTestWrapper = (wrapper, tag) => {
3 | return wrapper.findAll(`[data-test="${tag}"]`);
4 | };
5 |
--------------------------------------------------------------------------------