├── .browserslistrc ├── .commitlintrc.js ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc.js ├── .prettierignore ├── .prettierrc.js ├── .versionrc.js ├── .vscode └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── build-tools ├── constant.js ├── getBabelCommonConfig.js ├── getPostcssConfig.js ├── gulpfile.js ├── utils │ ├── chalkTip.js │ ├── paths.js │ └── transformLess.js └── webpack │ ├── plugins │ ├── InjectProjectInfoPlugin.js │ └── TerminalPrintPlugin.js │ ├── webpack.common.js │ ├── webpack.demo.js │ ├── webpack.dev.js │ ├── webpack.prod.js │ └── webpack.prod.min.js ├── changelog.option.js ├── components ├── components.js ├── icon │ ├── index.js │ └── style │ │ └── index.js ├── index.js ├── loading │ ├── index.jsx │ └── style │ │ └── index.js ├── message │ ├── index.js │ ├── main.jsx │ └── style │ │ ├── index.js │ │ └── index.less ├── modal │ ├── foot.jsx │ ├── index.jsx │ └── style │ │ ├── index.js │ │ └── index.less ├── style.js ├── switch │ ├── index.jsx │ └── style │ │ ├── index.js │ │ └── index.less ├── table │ ├── index.jsx │ └── style │ │ ├── index.js │ │ └── index.less └── utils │ └── common.js ├── index.js ├── jsconfig.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico └── index.html ├── scripts ├── publish.js ├── release.js └── update.js ├── src ├── App copy 10.vue ├── App copy 11.vue ├── App copy 12.vue ├── App copy 2.vue ├── App copy 3.vue ├── App copy 4.vue ├── App copy 5.vue ├── App copy 6.vue ├── App copy 7.vue ├── App copy 8.vue ├── App copy 9.vue ├── App copy.vue ├── App.vue ├── Appx.jsx ├── a.vue ├── assets │ ├── font │ │ └── Medium.ttf │ └── img │ │ └── author.jpg ├── components │ ├── DirectiveCpt │ │ ├── index.jsx │ │ └── index.less │ ├── JsxCpt │ │ └── index.jsx │ ├── TsxClassCpt │ │ └── index.tsx │ ├── TsxCpt │ │ └── index.tsx │ ├── VueCpt │ │ ├── index.css │ │ └── index.vue │ ├── VueRenderCpt │ │ ├── index copy.vue │ │ ├── index.vue │ │ └── wechat.jpg │ ├── aaaa │ │ ├── DirectiveCpt │ │ │ ├── index.jsx │ │ │ └── style │ │ │ │ ├── index.js │ │ │ │ └── index.less │ │ ├── icon │ │ │ └── index.jsx │ │ ├── input │ │ │ ├── index.tsx │ │ │ ├── input.less │ │ │ └── input.vue │ │ ├── message │ │ │ ├── index.js │ │ │ ├── main.jsx │ │ │ └── style │ │ │ │ ├── index.d.ts │ │ │ │ ├── index.less │ │ │ │ └── index.ts │ │ ├── modal │ │ │ ├── foot.jsx │ │ │ ├── index.jsx │ │ │ └── style │ │ │ │ ├── index.d.ts │ │ │ │ ├── index.less │ │ │ │ └── index.ts │ │ ├── table1 │ │ │ ├── index.jsx │ │ │ ├── index.vue │ │ │ └── style │ │ │ │ ├── index.js │ │ │ │ └── index.less │ │ └── tsxCpt │ │ │ ├── cpt │ │ │ └── style │ │ │ │ └── index.js │ │ │ └── index.tsx │ ├── checkbox │ │ ├── index.vue │ │ └── style │ │ │ ├── index.js │ │ │ └── index.less │ ├── classCpt │ │ └── index.js │ ├── createElementCpt │ │ ├── index copy.vue │ │ ├── index.vue │ │ └── index1.vue │ ├── dynamicCpt │ │ ├── index.jsx │ │ └── style │ │ │ ├── index.js │ │ │ └── index.less │ ├── icon │ │ └── index.jsx │ ├── message │ │ ├── index.js │ │ ├── main.jsx │ │ └── style │ │ │ ├── index.js │ │ │ └── index.less │ ├── table │ │ ├── index copy 2.vue │ │ ├── index.jsx │ │ ├── index.vue │ │ └── style │ │ │ ├── index.js │ │ │ └── index.less │ └── utils │ │ └── common.js ├── cpts │ ├── a.js │ ├── b.js │ ├── c │ │ ├── cc.js │ │ └── style │ │ │ └── index1.js │ └── dd.js ├── demo.tsx ├── index.css ├── index.js ├── index.less ├── plugins │ ├── auth │ │ ├── checkAuth.js │ │ └── index.js │ └── index.js ├── pretter.jsx ├── table │ ├── index.jsx │ └── style │ │ ├── index.js │ │ └── index.less ├── table1 │ ├── index.jsx │ ├── index.vue │ ├── style │ │ ├── index.js │ │ └── index.less │ └── utils │ │ └── common.js ├── test.ts └── utils │ └── hello.ts ├── tsconfig.json └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 0.25% 2 | last 2 version 3 | not dead 4 | -------------------------------------------------------------------------------- /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | console.log( 4 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 5 | `读取了: ${__filename.slice(__dirname.length + 1)}` 6 | )}` 7 | ); 8 | 9 | module.exports = { 10 | extends: ['@commitlint/config-conventional'], 11 | }; 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | # 控制编辑器代码规范,如按下回车的时候,缩进几个空格等等。 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | /es/ 3 | /dist/ 4 | /node_modules/ 5 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | console.log( 4 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 5 | `读取了: ${__filename.slice(__dirname.length + 1)}` 6 | )}` 7 | ); 8 | 9 | module.exports = { 10 | root: true, 11 | env: { 12 | browser: true, 13 | commonjs: true, 14 | }, 15 | extends: [ 16 | 'airbnb-base', // airbnb的eslint规范,indent:2,即一个缩进两个空格,qutoes:single,即单引号,max-len:一行100 17 | 'plugin:vue/strongly-recommended', // 强烈推荐(提高可读性) 18 | 'plugin:prettier/recommended', // prettierrc配置文件声明了singleQuote:true,即单引号,printWidth:80,即一行80,且prettier默认一个缩进四个空格 19 | ], 20 | 21 | parserOptions: { 22 | // parser: 'vue-eslint-parser', // https://eslint.vuejs.org/ 23 | // parser: '@typescript-eslint/parser', 24 | ecmaVersion: 2020, 25 | ecmaFeatures: { 26 | jsx: true, 27 | }, 28 | }, 29 | plugins: [ 30 | // 'vue', 31 | // '@typescript-eslint', 32 | // 'prettier', 33 | // 'plugin:prettier/recommended', // error!!!巨坑,这个写错位置了,应该是写在extends里面的!!!! 34 | // '@typescript-eslint/tslint', 35 | 'import', 36 | ], 37 | overrides: [ 38 | { 39 | files: ['*.ts', '*tsx'], 40 | parser: '@typescript-eslint/parser', 41 | parserOptions: { 42 | // parser: 'vue-eslint-parser', 43 | // ecmaVersion: 12, 44 | // parser: '@typescript-eslint/parser', 45 | ecmaFeatures: { 46 | jsx: true, 47 | }, 48 | }, 49 | plugins: ['@typescript-eslint'], 50 | extends: ['plugin:@typescript-eslint/recommended'], 51 | }, 52 | ], 53 | // rules优先级最高,会覆盖上面的 54 | rules: { 55 | /** 56 | * 0 => off 57 | * 1 => warn 58 | * 2 => error 59 | */ 60 | 'class-methods-use-this': 0, // 类方法如果不使用this的话会报错 61 | 'no-restricted-syntax': [ 62 | // airbnb默认禁用了一些语法 63 | 1, 64 | // 'FunctionExpression', 65 | // 'ForInStatement', 66 | { selector: 'ForInStatement', message: '不建议使用for in' }, 67 | ], 68 | 'guard-for-in': 0, // 当for in循环不使用if语句过滤其结果时,它会发出警告 69 | 'no-nested-ternary': 0, // 禁止嵌套三元 70 | 'no-plusplus': 0, 71 | 'arrow-body-style': [1, 'as-needed'], // 在可以省略的地方强制不使用大括号(默认) 72 | 'global-require': 1, // 此规则要求所有调用require()都在模块的顶层,类似于 ES6import和export语句,也只能在顶层发生。 73 | 'no-shadow': 0, 74 | 'no-undef': 0, // https://github.com/typescript-eslint/typescript-eslint/issues/2528#issuecomment-689369395 75 | 'no-param-reassign': 0, 76 | 'func-names': 0, // 不能是匿名函数 77 | 'spaced-comment': 2, // 此规则将在注释//或开始后强制执行间距的一致性/* 78 | 'no-underscore-dangle': 0, // Unexpected dangling '_' in '_xxx' 79 | 'vars-on-top': 0, // 要求var声明位于其作用域的顶部 80 | 'prefer-rest-params': 0, // 此规则旨在标记arguments变量的使用 81 | 'prefer-const': 1, // xxx is never reassigned. Use 'const' instead,此规则旨在标记使用let关键字声明的变量 82 | 'no-unused-vars': 1, // xxx is assigned a value but never used,此规则旨在消除未使用的变量、函数和函数参数 83 | 'no-var': 1, // Unexpected var, use let or const instead,该规则旨在阻止使用var或鼓励使用const或let代替。 84 | 'no-console': 0, // 此规则不允许调用console对象的方法。 85 | // 'no-console': process.env.NODE_ENV !== 'production' ? 0 : 2, // 此规则不允许调用console对象的方法。 86 | 'no-redeclare': 2, // 此规则旨在消除在同一范围内具有多个声明的变量。 87 | 'no-unused-expressions': [2, { allowShortCircuit: true }], // 期望一个赋值或函数调用,却看到了一个表达式,允许&& 88 | 'array-callback-return': [2, { allowImplicit: false }], // expects a return value from arrow function.期望箭头函数的返回值。 89 | 90 | 'vue/attribute-hyphenation': 0, // 此规则强制在 Vue 模板中的自定义组件上使用带连字符的属性名称。 91 | 92 | // eslint-plugin-import插件 93 | 'import/order': [ 94 | 2, 95 | { 96 | groups: [ 97 | 'builtin', 98 | 'external', 99 | 'internal', 100 | ['sibling', 'parent'], 101 | 'index', 102 | 'object', 103 | 'type', 104 | ], 105 | 'newlines-between': 'always', // 强制或禁止导入组之间的新行: 106 | // 根据导入路径按字母顺序对每个组内的顺序进行排序 107 | alphabetize: { 108 | order: 'asc' /* 按升序排序。选项:['ignore', 'asc', 'desc'] */, 109 | caseInsensitive: false /* 忽略大小写。选项:[true, false] */, 110 | }, 111 | }, 112 | ], 113 | 'import/newline-after-import': 1, // 强制在最后一个顶级导入语句或 require 调用之后有一个或多个空行 114 | 'import/extensions': 0, // 省略导入源路径中的文件扩展名 115 | 'import/no-unresolved': 0, // 导入资源的时候没有后缀会报这个错,这里关掉他 116 | 'import/prefer-default-export': 0, // 当模块只有一个导出时,更喜欢使用默认导出而不是命名导出。 117 | 'import/no-extraneous-dependencies': 0, // 开发/生产依赖混乱 118 | }, 119 | }; 120 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | /es/ 3 | /dist/ 4 | /demoDist/ 5 | /node_modules/ 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | #npm run lint 5 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | console.log( 4 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 5 | `读取了: ${__filename.slice(__dirname.length + 1)}` 6 | )}` 7 | ); 8 | 9 | module.exports = { 10 | '*.{js,jsx,ts,tsx}': ['prettier --write'], 11 | }; 12 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /lib/ 2 | /es/ 3 | /dist/ 4 | /node_modules/ 5 | .gitignore 6 | .prettierignore 7 | .eslintignore 8 | .browserlistrc 9 | .editorconfig 10 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | console.log( 4 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 5 | `读取了: ${__filename.slice(__dirname.length + 1)}` 6 | )}` 7 | ); 8 | 9 | module.exports = { 10 | bracketSpacing: true, // 默认为true,即{ foo: bar };可改为false,即{foo: bar} 11 | singleQuote: true, // 默认为false,即{foo:"bar"},可改为true,即{foo:'bar'} 12 | bracketSameLine: false, // https://prettier.io/blog/2021/09/09/2.4.0.html 13 | // jsxBracketSameLine: false, // true:将多行JSX元素的 > 放在最后一行的末尾。false:将多行JSX元素的 > 单独放在下一行 14 | trailingComma: 'es5', // 默认"es5":在ES5中有效的尾随逗号(对象、数组等)。可选:"none":没有尾随逗号;"all":尽可能尾随逗号 15 | printWidth: 80, // 默认80,printWidth不是硬性的允许行长度上限,不要试图将 printWidth 当作 ESLint 的max-len 来使用——它们不一样 16 | // parser: 'babel', 17 | }; 18 | // prettier默认1个缩进4个空格,即换行后,前面要有四个空格 19 | -------------------------------------------------------------------------------- /.versionrc.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | console.log( 4 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 5 | `读取了: ${__filename.slice(__dirname.length + 1)}` 6 | )}` 7 | ); 8 | 9 | module.exports = { 10 | types: [ 11 | { type: 'feat', section: 'Features', hidden: false }, 12 | { type: 'fix', section: 'Bug Fixes', hidden: false }, 13 | { type: 'docs', section: 'Docs', hidden: false }, 14 | { type: 'style', section: 'Styling', hidden: false }, 15 | { type: 'refactor', section: 'Code Refactoring', hidden: false }, 16 | { type: 'perf', section: 'Performance Improvements', hidden: false }, 17 | { type: 'test', section: 'Tests', hidden: false }, 18 | { type: 'build', section: 'Build System', hidden: false }, 19 | { type: 'ci', section: 'CI', hidden: false }, 20 | { type: 'chore', section: 'Others', hidden: false }, 21 | { type: 'revert', section: 'Reverts', hidden: false }, 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.2.8](https://github.com/galaxy-s10/billd-ui/compare/v1.2.7...v1.2.8) (2022-09-30) 2 | 3 | 4 | ### feat 5 | 6 | * 添加version ([87b4c61](https://github.com/galaxy-s10/billd-ui/commit/87b4c6147273a47972af5b3571abee2ec8722927)) 7 | 8 | ### fix 9 | 10 | * 添加忽略文件 ([598c496](https://github.com/galaxy-s10/billd-ui/commit/598c496b8b78de09285da9956684396c36745bfd)) 11 | 12 | 13 | 14 | ## [1.2.7](https://github.com/galaxy-s10/billd-ui/compare/v1.2.6...v1.2.7) (2022-09-29) 15 | 16 | 17 | ### fix 18 | 19 | * 添加dts ([8b51aa2](https://github.com/galaxy-s10/billd-ui/commit/8b51aa26077165aa2d10209acdad942629ea61aa)) 20 | 21 | 22 | 23 | ## 1.2.6 (2022-09-29) 24 | 25 | 26 | ### feat 27 | 28 | * 阶段保存 ([9db55fb](https://github.com/galaxy-s10/billd-ui/commit/9db55fb2b146168a11f2885ff47f6a21b243aa04)) 29 | 30 | ### fix 31 | 32 | * 修改代码格式 ([cfda2f7](https://github.com/galaxy-s10/billd-ui/commit/cfda2f7fc4264ba859bdbc317bfc569d0881fa94)) 33 | * 修改babel注释 ([baca5a5](https://github.com/galaxy-s10/billd-ui/commit/baca5a52b8f1d863de517802a8649f4728dee776)) 34 | * 修改gitignore ([454fd4d](https://github.com/galaxy-s10/billd-ui/commit/454fd4d4c9238c667f740eee8eebdc66ea5d6d03)) 35 | * 修改readme ([af65738](https://github.com/galaxy-s10/billd-ui/commit/af65738a99bffde34604f39f863fb3a20d466534)) 36 | * 优化代码 ([7dc3a56](https://github.com/galaxy-s10/billd-ui/commit/7dc3a566b16b8d76ca4a6609001d94df15b2ab83)) 37 | * 整理代码 ([2a51ae9](https://github.com/galaxy-s10/billd-ui/commit/2a51ae997f0a73cb27acf68f75844856c3516dbe)) 38 | * fix all errot && warn ([8f6e57b](https://github.com/galaxy-s10/billd-ui/commit/8f6e57bff349655d5416cda2b681572211425a22)) 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-PRESENT shuisheng 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 |

2 | 3 | Billd-UI logo 8 | 9 |

10 | 11 |

Billd-UI

12 | 13 |

基于Vue2.x构建的组件库

14 | 15 |
16 | Version 17 | Downloads 18 | License 19 |
20 | 21 | # 前言 22 | 23 | billd-ui 组件库是 21 年 6 月份开始写的,那会工作不满一年,但是却能独立的写出这个组件库(不仅仅是 billd-ui,还包括 [billd-ui-icons](https://github.com/galaxy-s10/billd-ui-icons)),即使放到现在来看也感觉实属不易,billd-ui 组件库大量借鉴了 [ant-design](https://github.com/ant-design/ant-design) 和 [ant-design-vue](https://github.com/vueComponent/ant-design-vue) 和 [antd-tools](https://github.com/ant-design/antd-tools),虽然 billd-ui 是一个组件库,但是重心并不在写组件的逻辑上,而是如何构建组件库的这个流程上,让我对组件库有了一个比较清晰的认知。 24 | 25 | # 简介 26 | 27 | 基于 vue2.x,使用 vue jsx 语法搭建的组件库,支持按需加载。 28 | 29 | - 前端框架:vue2.x 30 | - 构建工具:webpack5 + gulp4 + bebel7 + less3 31 | - 代码规范:eslint + prettier 32 | - 文档:vuepress 33 | 34 | # 组件列表 35 | 36 | | 名称 | 支持 | 37 | | -------------------------------------------------------------------------- | ---- | 38 | | [icon](http://project.hsslive.cn/billd-ui/component/basic/icon.html) | ✅ | 39 | | [modal](http://project.hsslive.cn/billd-ui/component/basic/modal.html) | ✅ | 40 | | [switch](http://project.hsslive.cn/billd-ui/component/basic/switch.html) | ✅ | 41 | | [loading](http://project.hsslive.cn/billd-ui/component/basic/loading.html) | ✅ | 42 | | [table](http://project.hsslive.cn/billd-ui/component/basic/table.html) | ❗ | 43 | | [message](http://project.hsslive.cn/billd-ui/component/basic/message.html) | ✅ | 44 | 45 | # 生态系统 46 | 47 | billd-ui 组件库将 icon 组件单独抽离出来成一个独立的组件库:`@huangshuisheng/icons-vue` ,而这个 `@huangshuisheng/icons-vue` 又依赖:`@huangshuisheng/icons-svg` 。这样做是为了将 icon 抽象成通用的基础库,后续可基于 `@huangshuisheng/icons-svg` 这个库开发 react,angular 的 icons 组件库,提高扩展性。 48 | 49 | | 包名 | 版本 | 50 | | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------- | 51 | | [billd-ui](https://www.npmjs.com/package/billd-ui) | [![npm](https://img.shields.io/npm/v/billd-ui)](https://www.npmjs.com/package/billd-ui) | 52 | | [@huangshuisheng/icons-vue](https://www.npmjs.com/package/@huangshuisheng/icons-vue) | [![npm](https://img.shields.io/npm/v/@huangshuisheng/icons-vue)](https://www.npmjs.com/package/@huangshuisheng/icons-vue) | 53 | | [@huangshuisheng/icons-svg](https://www.npmjs.com/package/@huangshuisheng/icons-svg) | [![npm](https://img.shields.io/npm/v/@huangshuisheng/icons-svg)](https://www.npmjs.com/package/@huangshuisheng/icons-svg) | 54 | 55 | # 安装 56 | 57 | ```sh 58 | npm i billd-ui --save 59 | ``` 60 | 61 | # 使用 62 | 63 | ## 全局引入 64 | 65 | ```js 66 | import Vue from 'vue'; 67 | import Billd from 'billd-ui'; 68 | import App from './App.vue'; 69 | import 'billd-ui/dist/billd.css'; 70 | 71 | Vue.use(Billd); 72 | 73 | new Vue({ 74 | render: (h) => h(App), 75 | }).$mount('#app'); 76 | ``` 77 | 78 | 全局引入后,就可以在项目的任何地方直接使用 billd-ui 组件 79 | 80 | ```vue 81 | 86 | ``` 87 | 88 | ## 局部引入组件 89 | 90 | > 注意,这种写法只是写一个就引入注册一个组件,仍需手动导入样式, 91 | > 92 | > 自 1.2.6 版本开始,支持原生的 esm tree shaking,即无需使用按需加载插件,也能对 js 代码进行 tree shaking!但是 1.2.6 版本以前,并不支持原生的 esm tree shaking,他会和全局引入一样,都会对整个 billd-ui 进行打包 93 | 94 | ```vue 95 | 100 | 101 | 113 | ``` 114 | 115 | ## 按需加载(推荐) 116 | 117 | 第一种方式:通过以下的写法来按需加载组件: 118 | 119 | ```vue 120 | 125 | 126 | 135 | ``` 136 | 137 | 第二种方式:如果你使用了 babel,可以使用 [babel-plugin-import](https://github.com/ant-design/babel-plugin-import) 来进行按需加载,首先 `npm i babel-plugin-import -D`,然后再添加 babel 的 plugins 配置: 138 | 139 | > 注意:全局引入和这种方式的按需引入有冲突,这两者方式不能同时使用,否则会报错! 140 | 141 | ```js 142 | plugins: [ 143 | // billd-ui支持按需加载,安装babel-plugin-import,然后在babel配置文件添加如下内容即可 144 | [ 145 | 'import', 146 | { 147 | libraryName: 'billd-ui', 148 | libraryDirectory: 'es', // 默认lib 149 | /** 150 | * 这个按需加载很有意思,因为babel-plugin-import这个插件是ant-design官方写的,因此规则也是官方定的, 151 | * 有一点是肯定的:官方根据自家的ant-design组件库的整体架构,从而编写了一个插件专门对自家的ant-design组件库做的按需加载。 152 | * 这个style属性,如果没有这个属性,就不会引入样式;如果有这个style属性,它的值是true,代表:libraryName/libraryDirectory/组件名/style,即会引入style下的index.js 153 | * 如果值是'css',代表:libraryName/libraryDirectory/组件名/style/css,即会引入这个css.js文件 154 | */ 155 | // style: true, // billd-ui使用了less,如果你的项目里面有对less做处理,可以使用此选项 156 | style: 'css', // 如果你的项目没有处理less,就使用这个选项。 157 | }, 158 | 'billd-ui', 159 | ], 160 | ]; 161 | ``` 162 | 163 | 插件会帮你将 `import { Switch } from 'billd-ui'` 转换成 `import Switch from billd-ui/es/switch` ,而且,因为配置了 style 属性,会按需加载该组件的样式,即会引入:`billd-ui/es/switch/style/css` 164 | 165 | ```vue 166 | 171 | 172 | 179 | ``` 180 | 181 | # 在浏览器使用 182 | 183 | 我们构建了 umd 版本,在 `billd-ui/dist` 目录下提供了 184 | 185 | 1. 开发版本: `billd.js`、`billd.css`、`billd.js.map`、`billd.css.map` 186 | 2. 生产版本: `billd.min.js`、`billd.min.css`、`billd.min.js.map`、`billd.min.css.map` 187 | 188 | 在浏览器中使用 `script` 和 `link` 标签直接引入文件,并使用全局变量 `Billd`即可(注意:请提前引入 vue)。 189 | 190 | # 本地调试 191 | 192 | > 可以在 src 目录引入构建好的组件查看效果 193 | 194 | ```sh 195 | npm run dev 196 | ``` 197 | 198 | # 本地编译 199 | 200 | 编译 es、lib、dist 版本: 201 | 202 | ```sh 203 | npm run compile 204 | ``` 205 | 206 | 编译 es 版本: 207 | 208 | ```sh 209 | npm run compile:es 210 | ``` 211 | 212 | 编译 lib 版本: 213 | 214 | ```sh 215 | npm run compile:lib 216 | ``` 217 | 218 | 编译 dist 版本: 219 | 220 | ```sh 221 | npm run compile:dist 222 | ``` 223 | 224 | # 如何发版 225 | 226 | ## 0.确保 git 工作区干净 227 | 228 | 即确保本地的修改已全部提交(git status 的时候会显示:`nothing to commit, working tree clean` ),否则会导致执行 `release:local` 脚本失败 229 | 230 | ## 1.执行本地发版脚本 231 | 232 | ```sh 233 | npm run release:local 234 | ``` 235 | 236 | > 该脚本内部会做以下事情: 237 | 238 | 1. 根据用户选择的版本,更新 package.json 的 version 239 | 2. 构建 components 目录的组件 240 | 3. 对比当前版本与上个版本的差异,生成 changelog 241 | 4. 提交暂存区到本地仓库:git commit -m 'chore(release): v 当前版本' 242 | 5. 生成当前版本 tag:git tag v 当前版本 243 | 244 | ## 2.执行线上发版脚本 245 | 246 | ```sh 247 | npm run release:online 248 | ``` 249 | 250 | > 该脚本内部会做以下事情: 251 | 252 | 1. 提交当前版本:git push 253 | 2. 提交当前版本 tag:git push origin v 当前版本 254 | 3. 根据 meta/packages.ts,发布 packages 里对应的包到 npm 255 | 256 | # 感慨 257 | 258 | 一年前,对前端的代码规范以及工程化没什么概念(即使有,也不是特别深),当时写出来的这个 billd-ui 组件库可以说是超常发挥(也是因为大量借鉴优秀的组件库源码)。但是,它毕竟是一年前写的,现在看来其实存在很多不足的地方,比如当时的发 npm 包都是手动的,没有一个完整的体系(比如:[release-it](https://github.com/release-it/release-it)),其次,组件的按需加载因为入口文件的问题,导致不能原生的支持 esm 的 tree shaking 等问题。billd-ui 这个组件库对我最明显的提升就是让我对 webpack、gulp 、babel 这些工具有了一个认知以及实际上的应用,还有如何开发一个库,需要考虑什么问题等等,对后面开发的一些有意思的库做铺垫~ 259 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | const pkg = require('./package.json'); 4 | 5 | console.log( 6 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 7 | `读取了: ${__filename.slice(__dirname.length + 1)}` 8 | )}` 9 | ); 10 | 11 | const babelRuntimeVersion = pkg.dependencies['@babel/runtime'].replace( 12 | /^[^0-9]*/, 13 | '' 14 | ); 15 | 16 | module.exports = { 17 | presets: [ 18 | // ["@babel/env"], 19 | // "@babel/preset-env", 20 | [ 21 | '@babel/preset-env', 22 | { 23 | // modules: "umd", 24 | // target: {}, 25 | // useBuiltIns: 'usage', 26 | // corejs: 3, 27 | }, 28 | ], 29 | [ 30 | '@vue/babel-preset-jsx', 31 | // { 32 | // injectH: false, 33 | // }, 34 | ], 35 | ], 36 | plugins: [ 37 | // billd-ui支持按需加载,安装babel-plugin-import,然后在babel配置文件添加如下内容即可 38 | // [ 39 | // 'import', 40 | // { 41 | // libraryName: 'billd-ui', 42 | // libraryDirectory: 'es', // 默认lib 43 | // /** 44 | // * 这个按需加载很有意思,因为babel-plugin-import这个插件是ant-design官方写的,因此规则也是官方定的, 45 | // * 有一点是肯定的:官方根据自家的ant-design组件库的整体架构,从而编写了一个插件专门对自家的ant-design组件库做的按需加载。 46 | // * 这个style属性,如果没有这个属性,就不会引入样式;如果有这个style属性,它的值是true,代表:libraryName/libraryDirectory/组件名/style,即会引入style下的index.js 47 | // * 如果值是'css',代表:libraryName/libraryDirectory/组件名/style/css,即会引入这个css.js文件 48 | // */ 49 | // style: true, // billd-ui使用了less,如果你的项目里面有对less做处理,可以使用此选项,如果没有处理less,请不要使用这个选项。 50 | // // style: 'css', // 如果你的项目没有处理less,就使用这个选项。 51 | // }, 52 | // 'billd-ui', 53 | // ], 54 | [ 55 | /** 56 | * useBuiltIns和polyfill选项在 v7 中被删除,只是将其设为默认值。 57 | */ 58 | '@babel/plugin-transform-runtime', 59 | { 60 | absoluteRuntime: false, // boolean或者string,默认为false。 61 | // corejs: 3, // false, 2,3或{ version: 2 | 3, proposals: boolean }, 默认为false 62 | helpers: true, // boolean, 默认为true.切换内联的 Babel 助手(classCallCheck,extends等)是否替换为对 的调用moduleName 63 | regenerator: true, // 切换生成器函数是否转换为使用不污染全局范围的再生器运行时。默认为true 64 | version: babelRuntimeVersion, 65 | }, 66 | ], 67 | ], 68 | }; 69 | -------------------------------------------------------------------------------- /build-tools/constant.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | distDir: path.resolve(__dirname, '../dist'), 5 | demoDistDir: path.resolve(__dirname, '../demoDist'), 6 | }; 7 | -------------------------------------------------------------------------------- /build-tools/getBabelCommonConfig.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | const pkg = require('../package.json'); 4 | 5 | console.log( 6 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 7 | `读取了: ${__filename.slice(__dirname.length + 1)}` 8 | )}` 9 | ); 10 | const babelRuntimeVersion = pkg.dependencies['@babel/runtime'].replace( 11 | /^[^0-9]*/, 12 | '' 13 | ); 14 | 15 | module.exports = function (modules) { 16 | return { 17 | presets: [ 18 | [ 19 | '@babel/preset-env', 20 | { 21 | /** 22 | * useBuiltIns: 23 | * false: 默认值就是false,不用任何的polyfill相关的代码 24 | * usage: 代码中需要哪些polyfill, 就引用相关的api 25 | * entry: 手动在入口文件中导入 core-js/regenerator-runtime, 根据目标浏览器引入所有对应的polyfill 26 | */ 27 | useBuiltIns: false, 28 | // corejs: '3', 29 | 30 | // targets: '>0.25%, last 2 version, not dead', // targets会读取项目里面的.browserslistrc文件,也可以在这里直接设置 31 | 32 | /** 33 | * modules: 'commonjs', 'amd', 'umd', 'systemjs','auto'(默认) 34 | * modules也可以设置false,这样会保留esmodule 35 | */ 36 | modules, 37 | }, 38 | ], 39 | [ 40 | '@vue/babel-preset-jsx', 41 | // { 42 | // injectH: false, 43 | // }, 44 | ], 45 | ], 46 | plugins: [ 47 | [ 48 | /** 49 | * @babel/plugin-transform-runtime 50 | * useBuiltIns和polyfill选项在 v7 中被删除,只是将其设为默认值。 51 | */ 52 | '@babel/plugin-transform-runtime', 53 | { 54 | // absoluteRuntime: false, // boolean或者string,默认为false。 55 | 56 | /** 57 | * corejs:false, 2,3或{ version: 2 | 3, proposals: boolean }, 默认为false 58 | * 设置对应值需要安装对应的包: 59 | * false npm install --save @babel/runtime 60 | * 2 npm install --save @babel/runtime-corejs2 61 | * 3 npm install --save @babel/runtime-corejs3 62 | */ 63 | corejs: false, 64 | /** 65 | * helpers: boolean, 默认true。 66 | * 如果是true,就会把需要他runtime包给引进来,如:import _defineProperty from "@babel/runtime/helpers/defineProperty" 67 | * 如果是false,就会把需要的runtime包里面的代码给嵌进bundle里,如function _defineProperty(){} 68 | * 设置false的话,会导致同一个runtime包里面的代码被很多文件设置,产生冗余的代码。而且因为虽然是同一 69 | * 份runtime包里面的代码,但是他们在不同的文件(模块)里面,都有自己的作用域,因此在使用类似webpack之类的 70 | * 打包工具打包的时候,不会做优化。因此推荐设置true,这样可以通过静态分析的手段进行打包,减少打包后的代码体积。 71 | */ 72 | helpers: true, 73 | regenerator: true, // 切换生成器函数是否转换为使用不污染全局范围的再生器运行时。默认为true 74 | // https://babeljs.io/docs/en/babel-plugin-transform-runtime#version,没想到version会影响到包大小 75 | version: babelRuntimeVersion, // 一定要设置版本,v7.5.0之前,扩展运算符...不会被提取,v7.5.0之后,才会提取成objectSpread2。而且貌似version:7.4.5和version:^7.4.5没区别 76 | }, 77 | ], 78 | // billd-ui支持按需加载,安装babel-plugin-import,然后在babel配置文件添加如下内容即可 79 | // [ 80 | // "import", 81 | // { 82 | // libraryName: "billd-ui", 83 | // libraryDirectory: "dist", 84 | // style: "css", 85 | // }, 86 | // "billd-ui", 87 | // ], 88 | // "transform-vue-jsx",//引入错误,这是Babel6的使用的版本。Babel7不需要这个。 89 | ], 90 | }; 91 | }; 92 | -------------------------------------------------------------------------------- /build-tools/getPostcssConfig.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const postcssPresetEnv = require('postcss-preset-env'); 3 | 4 | console.log( 5 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 6 | `读取了: ${__filename.slice(__dirname.length + 1)}` 7 | )}` 8 | ); 9 | 10 | module.exports = { 11 | plugins: [ 12 | // 'autoprefixer', // postcss-preset-env包含了autoprefixer的功能 13 | // 'postcss-preset-env', //简写,具体看各个插件的官网提供几种写法 14 | postcssPresetEnv, 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /build-tools/gulpfile.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const { chdir, cwd } = require('process'); 3 | 4 | const gulp = require('gulp'); 5 | const babel = require('gulp-babel'); 6 | const clean = require('gulp-clean'); 7 | const concat = require('gulp-concat'); 8 | const ts = require('gulp-typescript'); 9 | const through2 = require('through2'); 10 | const webpack = require('webpack'); 11 | 12 | // const postcss = require('gulp-postcss'); 13 | 14 | const tsProject = require('../tsconfig.json'); 15 | const { distDir } = require('./constant'); 16 | const babelConfig = require('./getBabelCommonConfig'); 17 | const { chalkSUCCESS, chalkINFO } = require('./utils/chalkTip'); 18 | const transformLess = require('./utils/transformLess.js'); 19 | const webpackConfig = require('./webpack/webpack.common'); 20 | 21 | const tsDefaultReporter = ts.reporter.defaultReporter(); 22 | 23 | const componentsDir = path.resolve(__dirname, '../components'); 24 | 25 | const tsFiles = [ 26 | `${componentsDir}/**/*.js`, 27 | `${componentsDir}/**/*.jsx`, 28 | `${componentsDir}/**/*.ts`, 29 | `${componentsDir}/**/*.tsx`, 30 | ]; 31 | 32 | // 删除目录 33 | function delDir(path) { 34 | // gulp-clean:确保返回流,以便gulp知道clean任务是异步的 35 | const res = gulp 36 | .src(path, { 37 | allowEmpty: true, 38 | }) 39 | .pipe(clean({ force: true })); // 不添加force:true属性不能删除上层目录,因此加上。 40 | return new Promise((resolve) => { 41 | res.on('finish', function () { 42 | console.log(chalkSUCCESS('清除旧构建文件成功!')); 43 | resolve(); 44 | }); 45 | }); 46 | } 47 | 48 | // 复制静态资源目录 49 | function copyAssets(modules) { 50 | return function copyassets(done) { 51 | const assetsStream = gulp 52 | .src(`${componentsDir}/assets/**/*`, { allowEmpty: true }) 53 | .pipe(gulp.dest(modules === false ? '../es/assets/' : '../lib/assets/')); 54 | assetsStream.on('finish', () => { 55 | console.log(chalkSUCCESS('复制静态资源目录成功!')); 56 | done(); 57 | }); 58 | return assetsStream; 59 | }; 60 | } 61 | 62 | // 编译less 63 | function compileLess(modules) { 64 | return function compileless(done) { 65 | // 编译src下面的所有less文件,但是排除src下的assets文件夹。 66 | const lessStream = gulp 67 | .src([`components/**/*.less`, `!components/assets/**/*`], { 68 | cwd: '../', 69 | }) 70 | .pipe( 71 | through2.obj(function (file, encoding, next) { 72 | // 将源文件复制一份放流里面 73 | this.push(file.clone()); 74 | // 匹配所有less文件 75 | if (file.path.match(/\.less$/)) { 76 | transformLess(file.path) 77 | .then((css) => { 78 | // File.contents can only be a Buffer, a Stream, or null. 79 | file.contents = Buffer.from(css); 80 | // 将转换后的less文件路径修改文件成css 81 | file.path = file.path.replace(/\.less$/, '.css'); 82 | // 将修改文件路径后的文件放流里面 83 | this.push(file); 84 | next(); 85 | }) 86 | .catch((e) => { 87 | console.error(e); 88 | }); 89 | } else { 90 | next(); 91 | } 92 | }) 93 | ) 94 | // .pipe(postcss()) // gulp-postcss不能在这里使用,因为上面gulp.pipe的结果是promise异步的 95 | .pipe(gulp.dest(modules === false ? '../es' : '../lib')); 96 | // .pipe(gulp.dest('../lib')); 97 | // 旧版使用gulp-less解析less,源文件会被解析成css文件,即原less文件会变成css文件。 98 | // return gulp 99 | // .src("../components/**/*.less") 100 | // .pipe(gulpLess()) 101 | // .pipe(postcss()) 102 | // .pipe(gulp.dest("../lib")); 103 | lessStream.on('finish', () => { 104 | console.log(chalkSUCCESS('编译less成功!')); 105 | done(); 106 | }); 107 | // console.log(lessStream, 98766); 108 | // return lessStream; 109 | }; 110 | } 111 | 112 | // 编译 113 | function compile(modules) { 114 | let error; 115 | const tsResult = gulp.src(tsFiles).pipe( 116 | ts(tsProject.compilerOptions, { 117 | error(e) { 118 | tsDefaultReporter.error(e); 119 | error = 1; 120 | }, 121 | finish: tsDefaultReporter.finish, 122 | }) 123 | ); 124 | function check() { 125 | if (error && !argv['ignore-error']) { 126 | process.exit(1); 127 | } 128 | } 129 | tsResult.dts.pipe(gulp.dest(modules === false ? '../es' : '../lib')); 130 | tsResult.on('finish', check); 131 | tsResult.on('end', check); 132 | const stream = tsResult.js.pipe(babel(babelConfig(modules))); 133 | return stream.pipe(gulp.dest(modules === false ? '../es' : '../lib')); 134 | } 135 | 136 | gulp.task('concat-css', () => 137 | gulp.src('../lib/**/*.css').pipe(concat('all.css')).pipe(gulp.dest('../lib')) 138 | ); 139 | 140 | // es modules 141 | gulp.task( 142 | 'es', 143 | gulp.series( 144 | async function (done) { 145 | await delDir(['../es']); 146 | done(); 147 | }, 148 | gulp.parallel( 149 | copyAssets(false), 150 | compileLess(false), 151 | function compileEs(done) { 152 | console.log(chalkINFO('开始编译es版本...')); 153 | compile(false).on('finish', () => { 154 | console.log(chalkSUCCESS('编译es版本完成!')); 155 | done(); 156 | }); 157 | } 158 | ) 159 | ) 160 | ); 161 | 162 | // commonjs 163 | gulp.task( 164 | 'lib', 165 | gulp.series( 166 | async function (done) { 167 | await delDir(['../lib']); 168 | done(); 169 | }, 170 | gulp.parallel( 171 | copyAssets(undefined), 172 | compileLess(undefined), 173 | function compileLib(done) { 174 | console.log(chalkINFO('开始编译lib版本...')); 175 | compile(undefined).on('finish', () => { 176 | console.log(chalkSUCCESS('编译lib版本完成!')); 177 | done(); 178 | }); 179 | } 180 | ) 181 | ) 182 | ); 183 | 184 | // umd 185 | gulp.task( 186 | 'dist', 187 | gulp.series( 188 | async function (done) { 189 | await delDir([distDir]); 190 | done(); 191 | }, 192 | async (done) => { 193 | chdir(path.resolve(__dirname, '../')); 194 | console.log(chalkINFO('开始编译dist版本...'), cwd()); 195 | // res1会删除dist,res不会删除dist,因此,得将res1放在res的前面 196 | const productionMinConfig = await webpackConfig({ 197 | production: true, 198 | productionMin: true, 199 | }); 200 | const productionConfig = await webpackConfig({ 201 | production: true, 202 | productionMin: false, 203 | }); 204 | // productionConfig会删除dist,productionMinConfig不会删除dist,因此,得将productionConfig放在productionMinConfig的前面 205 | webpack([productionMinConfig, productionConfig], (err, stats) => { 206 | if (err) { 207 | console.error(err); 208 | return; 209 | } 210 | const info = stats.toJson(); 211 | if (stats.hasErrors()) { 212 | console.error(info.errors); 213 | } 214 | if (stats.hasWarnings()) { 215 | console.warn(info.warnings); 216 | } 217 | const buildInfo = stats.toString({ 218 | colors: true, 219 | children: true, 220 | chunks: false, 221 | modules: false, 222 | chunkModules: false, 223 | hash: false, 224 | version: false, 225 | }); 226 | console.log(buildInfo); 227 | done(); 228 | }); 229 | } 230 | ) 231 | ); 232 | 233 | // es、commonjs、umd 234 | gulp.task( 235 | 'default', 236 | gulp.series( 237 | async function (done) { 238 | await delDir(['../lib', '../es', '../dist']); 239 | done(); 240 | }, 241 | gulp.parallel('es', 'lib'), 242 | 'dist', 243 | function allTasksDone(done) { 244 | console.log(chalkSUCCESS('所有任务执行完成!')); 245 | done(); 246 | } 247 | ) 248 | ); 249 | -------------------------------------------------------------------------------- /build-tools/utils/chalkTip.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const emoji = require('node-emoji'); 3 | 4 | const chalkINFO = (v) => 5 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright(v)}`; 6 | const chalkSUCCESS = (v) => 7 | `${chalk.bgGreenBright.black(' SUCCESS ')} ${chalk.greenBright(v)}`; 8 | const chalkERROR = (v) => 9 | `${chalk.bgRedBright.black(' ERROR ')} ${chalk.redBright(v)}`; 10 | const chalkWARN = (v) => 11 | `${chalk.bgHex('#FFA500').black(' WARN ')} ${chalk.hex('#FFA500')(v)}`; 12 | 13 | // 注意:js的对象是引用类型,exports指向module.exports,这里只是将exposts指向了另一个对象,并没有改变原本的module.exports 14 | // exports = { chalkINFO, chalkSUCCESS, chalkERROR }; 15 | 16 | // module.exports和exports操作的是同一块内存,最终暴露的是module.exports。 17 | // 这里虽然module.exports的exports指向了另一个对象,但node找的的module.exports的引用,所以还是会找到module.exports指向的新对象 18 | module.exports = { chalkINFO, chalkSUCCESS, chalkERROR, chalkWARN, emoji }; 19 | -------------------------------------------------------------------------------- /build-tools/utils/paths.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const { chalkINFO } = require('./chalkTip'); 4 | const appDir = path.resolve(__dirname, '../../'); 5 | 6 | console.log(chalkINFO(`Node.js进程的当前工作目录:${process.cwd()}`)); 7 | 8 | // 返回相对于项目根目录的绝对路径。 9 | const resolveApp = (relativePath) => path.resolve(appDir, relativePath); 10 | 11 | module.exports = { resolveApp }; 12 | -------------------------------------------------------------------------------- /build-tools/utils/transformLess.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require('fs'); 2 | const path = require('path'); 3 | 4 | const less = require('less'); 5 | const postcss = require('postcss'); 6 | 7 | const postcssConfig = require('../getPostcssConfig'); 8 | const { resolveApp } = require('./paths'); 9 | // less3+默认就会搜索npm路径 10 | // const NpmImportPlugin = require("less-plugin-npm-import"); 11 | 12 | function transformLess(lessFile) { 13 | // 使用node的fs模块,同步读取lessFile文件,拿到的是字符串 14 | let data = readFileSync(lessFile, 'utf-8'); 15 | // 这里应该是处理编码问题,参考了antd-tools 16 | data = data.replace(/^\uFEFF/, ''); 17 | const resolvedLessFile = resolveApp(lessFile); 18 | // https://less.bootcss.com/usage/#lessjs-options 19 | // 设置less选项,方便处理编译less时遇到的警告或报错。 20 | const lessOpts = { 21 | paths: [path.dirname(resolvedLessFile)], 22 | path: resolvedLessFile, 23 | filename: resolvedLessFile, 24 | // plugins: [new NpmImportPlugin({ prefix: "~" })], 25 | // compress: true, //使用less内置的压缩,但最好在解析完成less后,用第三方插件再次解析压缩。 26 | javascriptEnabled: true, // 允许在.less文件中内联计算JavaScript 27 | }; 28 | // https://postcss.org/api/ 29 | const str = less 30 | .render(data, lessOpts) 31 | .then( 32 | (result) => 33 | postcss(postcssConfig.plugins).process(result.css, { from: undefined }) // https://stackoverflow.com/questions/48162738/without-from-option-postcss-could-generate-wrong-source-map-or-do-not-find-bro 34 | ) 35 | .then((r) => r.css); 36 | return str; 37 | // return less.render(data, lessOpts).then(r => { 38 | // // r.css是解析less后得到的css字符串 39 | // return r.css; 40 | // }); 41 | } 42 | 43 | module.exports = transformLess; 44 | -------------------------------------------------------------------------------- /build-tools/webpack/plugins/InjectProjectInfoPlugin.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | 5 | const pkg = require('../../../package.json'); 6 | 7 | let commitHash; 8 | let commitUserName; 9 | let commitDate; 10 | let commitMessage; 11 | try { 12 | // commit哈希 13 | commitHash = execSync('git show -s --format=%H').toString().trim(); 14 | // commit用户名 15 | commitUserName = execSync('git show -s --format=%cn').toString().trim(); 16 | // commit日期 17 | commitDate = new Date( 18 | execSync(`git show -s --format=%cd`).toString() 19 | ).toLocaleString(); 20 | // commit消息 21 | commitMessage = execSync('git show -s --format=%s').toString().trim(); 22 | } catch (error) { 23 | console.log(error); 24 | } 25 | 26 | const templateStr = ` 27 | (function(){ 28 | var log = (title, value) => { 29 | console.log( 30 | '%c ' + title + ' %c ' + value + ' ' + '%c', 31 | 'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px; color: #fff', 32 | 'background:#41b883 ; padding: 1px; border-radius: 0 3px 3px 0; color: #fff', 33 | 'background:transparent' 34 | ); 35 | }; 36 | 37 | log('项目名称:', {pkgName}); 38 | log('项目版本:', {pkgVersion}); 39 | log('项目仓库:', {pkgRepository}); 40 | log('构建仓库:', {buildRepository}); 41 | log('最后构建:', {lastBuild}); 42 | log('构建仓库git提交用户:', {commitUserName}); 43 | log('构建仓库git提交日期:', {commitDate}); 44 | log('构建仓库git提交信息:', {commitMessage}); 45 | log('构建仓库git提交哈希:', {commitHash}); 46 | console.warn( 47 | '当前项目的代码是托管在github,但由于线上构建如果使用github的话,会因为github拉取代码太慢以及一些秘钥文件不方便处理等原因,' 48 | ); 49 | console.warn( 50 | '因此目前的线上构建是将github的代码做一层处理,然后再复制到gitee的私有仓库,使用gitee的代码进行构建的,' 51 | ); 52 | console.warn( 53 | '因为github和gitee提交的数据不一致,因此项目信息和git信息会对不上,大多数情况下github和gitee仓库代码都会尽量保持同步~' 54 | ); 55 | })() 56 | `; 57 | 58 | const pkgName = pkg.name; 59 | const pkgVersion = pkg.version; 60 | const pkgRepository = pkg.repository.url; 61 | 62 | const replaceKeyFromValue = (str, obj) => { 63 | let res = str; 64 | Object.keys(obj).forEach((v) => { 65 | res = res.replace(new RegExp(`{${v}}`, 'ig'), obj[v]); 66 | }); 67 | return res; 68 | }; 69 | class InjectProjectInfoPlugin { 70 | constructor(options) { 71 | this.options = options; 72 | } 73 | 74 | apply(compiler) { 75 | compiler.hooks.compilation.tap('InjectProjectInfoPlugin', (compilation) => { 76 | HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync( 77 | 'InjectProjectInfoPlugin', 78 | (data, cb) => { 79 | // 获取原来内容 80 | const buildRepository = this.options.isProduction 81 | ? `https://gitee.com/galaxy-s10/${pkgName}` 82 | : pkgRepository; 83 | const info = replaceKeyFromValue(templateStr.toString(), { 84 | pkgName: JSON.stringify(pkgName), 85 | pkgVersion: JSON.stringify(pkgVersion), 86 | pkgRepository: JSON.stringify(pkgRepository), 87 | buildRepository: JSON.stringify(buildRepository), 88 | lastBuild: JSON.stringify(new Date().toLocaleString()), 89 | commitDate: JSON.stringify(commitDate), 90 | commitHash: JSON.stringify(commitHash), 91 | commitMessage: JSON.stringify(commitMessage), 92 | commitUserName: JSON.stringify(commitUserName), 93 | }); 94 | data.html = data.html.replace( 95 | '', 96 | `\n` 97 | ); 98 | cb(null, data); 99 | } 100 | ); 101 | }); 102 | } 103 | } 104 | 105 | module.exports = InjectProjectInfoPlugin; 106 | -------------------------------------------------------------------------------- /build-tools/webpack/plugins/TerminalPrintPlugin.js: -------------------------------------------------------------------------------- 1 | // const readline = require('readline'); 2 | 3 | const chalk = require('chalk'); 4 | 5 | class TerminalPrintPlugin { 6 | constructor(options) { 7 | this.options = options; 8 | } 9 | 10 | apply(compiler) { 11 | const plugin = { name: 'TerminalPrintPlugin' }; 12 | 13 | const log = () => { 14 | console.log(' App running at:'); 15 | console.log(`- Local: ${chalk.cyan(this.options.local)}`); 16 | console.log(`- Network: ${chalk.cyan(this.options.network)}`); 17 | console.log(); 18 | }; 19 | 20 | // const clearConsole = () => { 21 | // readline.cursorTo(process.stdout, 0, 0); 22 | // readline.clearScreenDown(process.stdout); 23 | // }; 24 | 25 | // const doneFn = () => { 26 | // clearConsole(); 27 | // console.log( 28 | // `${chalk.bgGreenBright(' DONE ')} ${chalk.green( 29 | // 'Compiled successfully !' 30 | // )}` 31 | // ); 32 | // log(); 33 | // }; 34 | 35 | // const invalidFn = () => { 36 | // clearConsole(); 37 | // console.log( 38 | // `${chalk.blue.bgBlueBright(' INFO ')} ${chalk.blue( 39 | // 'WAIT Compiling...' 40 | // )}` 41 | // ); 42 | // }; 43 | 44 | compiler.hooks.done.tapAsync(plugin, (compilation, callback) => { 45 | log(); 46 | callback(); 47 | }); 48 | // compiler.hooks.invalid.tap(plugin, invalidFn); 49 | } 50 | } 51 | 52 | module.exports = TerminalPrintPlugin; 53 | -------------------------------------------------------------------------------- /build-tools/webpack/webpack.demo.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const TerserPlugin = require('terser-webpack-plugin'); 3 | 4 | const { demoDistDir } = require('../constant'); 5 | const { resolveApp } = require('../utils/paths'); 6 | 7 | console.log( 8 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 9 | `读取了: ${__filename.slice(__dirname.length + 1)}` 10 | )}` 11 | ); 12 | module.exports = { 13 | mode: 'production', 14 | devtool: false, 15 | entry: { 16 | main: { 17 | import: resolveApp('./src/index.js'), 18 | }, 19 | }, 20 | output: { 21 | clean: true, 22 | path: demoDistDir, 23 | }, 24 | optimization: { 25 | // concatenateModules: true, // production模式下默认true。告知 webpack 去寻找模块图形中的片段,哪些是可以安全地被合并到单一模块中。 26 | usedExports: true, // production模式或者不设置usedExports,它默认就是true。usedExports的目的是标注出来哪些函数是没有被使用 unused,会结合Terser进行处理 27 | sideEffects: true, // 告知 webpack 去辨识 package.json 中的 副作用 标记或规则 28 | minimize: true, // 是否开启Terser,不手动设置的话默认就根据环境判断,production环境就是true,非production环境就是false。设置false后,不会压缩和转化 29 | minimizer: [ 30 | `...`, // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`) 31 | new TerserPlugin({ 32 | parallel: true, // 使用多进程并发运行以提高构建速度 33 | extractComments: false, // 去除打包生成的bundle.js.LICENSE.txt 34 | terserOptions: { 35 | // Terser 压缩配置 36 | parse: { 37 | // default {},如果希望指定其他解析选项,请传递一个对象。 38 | }, 39 | compress: { 40 | // default {},传递false表示完全跳过压缩。传递一个对象来指定自定义压缩选项。 41 | arguments: true, // default: false,尽可能将参数[index]替换为函数参数名 42 | dead_code: true, // 删除死代码,默认就会删除,实际测试设置false也没用,还是会删除 43 | toplevel: false, // default: false,在顶级作用域中删除未引用的函数("funcs")和/或变量("vars"), 设置true表示同时删除未引用的函数和变量 44 | keep_classnames: false, // default: false,传递true以防止压缩器丢弃类名 45 | keep_fnames: false, // default: false,传递true以防止压缩器丢弃函数名 46 | }, 47 | /** 48 | * mangle,默认值true,会将keep_classnames,keep_fnames,toplevel等等mangle options的所有选项设为true。 49 | * 传递false以跳过篡改名称,或者传递一个对象来指定篡改选项 50 | */ 51 | mangle: true, 52 | toplevel: true, // default false,如果希望启用顶级变量和函数名修改,并删除未使用的变量和函数,则设置为true。 53 | keep_classnames: true, // default: undefined,传递true以防止丢弃或混淆类名。 54 | keep_fnames: true, // default: false,传递true以防止函数名被丢弃或混淆。 55 | }, 56 | }), 57 | ], 58 | }, 59 | plugins: [ 60 | // new webpack.optimize.ModuleConcatenationPlugin(), //作用域提升。!!!在使用cdn时,axios和iview有问题,先不用!!! 61 | ], 62 | }; 63 | -------------------------------------------------------------------------------- /build-tools/webpack/webpack.dev.js: -------------------------------------------------------------------------------- 1 | // const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); 2 | 3 | // const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin'); // webpack4 4 | const FriendlyErrorsWebpackPlugin = require('@soda/friendly-errors-webpack-plugin'); // webapck5对等依赖 5 | const chalk = require('chalk'); 6 | const portfinder = require('portfinder'); 7 | const WebpackDevServer = require('webpack-dev-server'); 8 | 9 | const { distDir } = require('../constant'); 10 | const { resolveApp } = require('../utils/paths'); 11 | const TerminalPrintPlugin = require('./plugins/TerminalPrintPlugin'); 12 | 13 | const localIPv4 = WebpackDevServer.internalIPSync('v4'); 14 | 15 | console.log( 16 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 17 | `读取了: ${__filename.slice(__dirname.length + 1)}` 18 | )}` 19 | ); 20 | 21 | module.exports = new Promise((resolve) => { 22 | // 默认端口8088,如果被占用了,会自动递增+1 23 | const port = 8088; 24 | portfinder.basePort = port; 25 | portfinder 26 | .getPortPromise({ 27 | port, 28 | stopPort: 9000, 29 | }) 30 | .then((port) => { 31 | resolve({ 32 | /** 33 | * .browserlistrc文件导致的热更新不生效。https://github.com/webpack/webpack-dev-server/pull/2761 34 | * 删掉.browserlistrc文件即可解决。但是我没有删,将webpack-dev-server升级到了4.x解决了,但也需要修改devServe属性的部分东西。 35 | */ 36 | // mode: "production", 37 | mode: 'development', 38 | devtool: 'eval', // eval,具有最高性能的开发构建的推荐选择。 39 | entry: { 40 | main: { 41 | import: resolveApp('./src/index.js'), 42 | }, 43 | }, 44 | output: { 45 | filename: 'js/[name]-bundle.js', // 入口文件打包生成后的文件的文件名 46 | chunkFilename: 'js/[name]-[contenthash:6]-bundle-chunk.js', 47 | path: distDir, 48 | assetModuleFilename: 'assets/[name]-[contenthash:6].[ext]', // 静态资源生成目录(不管什么资源默认都统一生成到这里,除非单独设置了generator) 49 | publicPath: '/', // 打包成dist后,如果想直接打开index.html看效果,就将该路径改成:"./",上线后改回:"/" 50 | }, 51 | stats: 'none', 52 | // 这个infrastructureLogging设置参考了vuecli5,如果不设置,webpack-dev-server会打印一些信息 53 | infrastructureLogging: { 54 | level: 'none', 55 | }, 56 | devServer: { 57 | client: { 58 | logging: 'none', // https://webpack.js.org/configuration/dev-server/#devserverclient 59 | }, 60 | // stats: 'errors-only', // 只显示错误信息(如果eslint有警告和错误,只会显示警告信息,不会显示错误信息) 61 | // overlay: true, // 出现编译器错误或警告时,在浏览器中显示全屏覆盖。 62 | /** 63 | * https://github.com/geowarin/friendly-errors-webpack-plugin 64 | * If you use the webpack-dev-server, there is a setting in webpack's devServer options: 65 | * quiet: true 66 | * 启用 devServer.quiet 后,除了初始启动信息外,什么都不会写入控制台。 67 | * 这也意味着来自 webpack 的错误或警告是不可见的。 68 | * warn:不使用FriendlyErrorsWebpackPlugin这个插件请把这个属性注释掉!否则控制台无警告和错误! 69 | */ 70 | // quiet: true, 71 | hot: true, // hrm,开启模块热替换 72 | // hotOnly: true, // 默认情况下(hotOnly:false),如果编译失败会刷新页面。设置了true后就不会刷新整个页面(!!!webpack-dev-server@4.x已改!!!) 73 | compress: true, // 开启gizp压缩 74 | port, // 默认端口号8080 75 | // open: true, //默认不打开浏览器 76 | /** 77 | * contentBase默认为package.json文件所在的根目录,即hss_webpack5目录 78 | * 打开localhost:8080/hss/demo.js,就会访问hss_webpack5目录下的hss目录下的demo.js。 79 | * 设置contentBase: path.resolve(__dirname, '../hss')后,打开localhost:8080/demo.js,即可访问hss_webpack5目录下的hss目录下的demo.js 80 | */ 81 | // contentBase: path.resolve(__dirname, '../public'), // 模拟vuecli的public(!!!webpack-dev-server@4.x已改!!!) 82 | // watchContentBase: true, //监听contenBase目录(!!!webpack-dev-server@4.x已改!!!) 83 | // static: [resolveApp("./public")], //模拟vuecli的public 84 | historyApiFallback: true, // 默认值:false,设置true后可解决spa页面刷新404 85 | // historyApiFallback: { 86 | // rewrites: [ 87 | // // 如果publicPath设置了/abc,就不能直接设置historyApiFallback: true,这样会重定向到hss_webpack5根目录下的index.html 88 | // // publicPath设置了/abc,就重定向到/abc,这样就可以了 89 | // { from: /abc/, to: "/abc" } 90 | // ] 91 | // }, 92 | // devMiddleware: { 93 | // // webpack-dev-server4+写法。https://github.com/webpack/webpack-dev-server/blob/master/CHANGELOG.md 94 | // publicPath: "./" 95 | // }, 96 | static: { 97 | watch: true, // 告诉 dev-server 监听文件。默认启用,文件更改将触发整个页面重新加载。可以通过将 watch 设置为 false 禁用。 98 | publicPath: '/', 99 | }, 100 | // publicPath: '/', // devServer的publicPath建议与output的publicPath一致(!!!webpack-dev-server@4.x已改!!!) 101 | // proxy: { 102 | // "/api": { 103 | // // target: 'https://www.zhengbeining.com/api/', //默认:/api/type/pageList ===>https://www.zhengbeining.com/api/api/type/pageList 104 | // target: "http://42.193.157.44/api/", //默认:/api/type/pageList ===>https://www.zhengbeining.com/api/api/type/pageList 105 | // secure: false, //默认情况下(secure: true),不接受在HTTPS上运行的带有无效证书的后端服务器。设置secure: false后,后端服务器的HTTPS有无效证书也可运行 106 | // /** 107 | // * changeOrigin,是否修改请求地址的源 108 | // * 默认changeOrigin: false,即发请求即使用devServer的localhost:port发起的,如果后端服务器有校验源,就会有问题 109 | // * 设置changeOrigin: true,就会修改发起请求的源,将原本的localhost:port修改为target,这样就可以通过后端服务器对源的校验 110 | // */ 111 | // changeOrigin: true, 112 | // pathRewrite: { 113 | // "^/api": "", //重写后:/api/type/pageList ===>https://www.zhengbeining.com/api/type/pageList 114 | // }, 115 | // }, 116 | // }, 117 | }, 118 | plugins: [ 119 | new FriendlyErrorsWebpackPlugin({}), 120 | // 打印控制调试地址 121 | new TerminalPrintPlugin({ 122 | local: `http://localhost:${port}`, 123 | network: `http://${localIPv4}:${port}`, 124 | }), 125 | ], 126 | }); 127 | }) 128 | .catch((err) => { 129 | console.log(err); 130 | }); 131 | }); 132 | -------------------------------------------------------------------------------- /build-tools/webpack/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | const { distDir } = require('../constant'); 4 | const { resolveApp } = require('../utils/paths'); 5 | 6 | console.log( 7 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 8 | `读取了: ${__filename.slice(__dirname.length + 1)}` 9 | )}` 10 | ); 11 | module.exports = { 12 | mode: 'production', 13 | devtool: 'source-map', 14 | entry: { 15 | main: { 16 | import: resolveApp('./index.js'), 17 | }, 18 | }, 19 | output: { 20 | clean: true, 21 | path: distDir, 22 | filename: 'billd.js', 23 | library: { 24 | name: 'Billd', 25 | // root: 'MyLibrary', 26 | // amd: 'my-library', 27 | // commonjs: 'my-common-library', 28 | type: 'umd', 29 | }, 30 | // library: "billd_ui", 31 | // libraryTarget: "umd",//module,commonjs,umd 32 | // umdNamedDefine: true, 33 | }, 34 | externals: { 35 | vue: { 36 | root: 'Vue', 37 | commonjs2: 'vue', 38 | commonjs: 'vue', 39 | amd: 'vue', 40 | }, 41 | }, 42 | optimization: { 43 | // concatenateModules: true, // production模式下默认true。告知 webpack 去寻找模块图形中的片段,哪些是可以安全地被合并到单一模块中。 44 | usedExports: false, // production模式或者不设置usedExports,它默认就是true。usedExports的目的是标注出来哪些函数是没有被使用 unused,会结合Terser进行处理 45 | sideEffects: false, // 告知 webpack 去辨识 package.json 中的 副作用 标记或规则 46 | minimize: false, // 是否开启Terser,不手动设置的话默认就根据环境判断,production环境就是true,非production环境就是false。设置false后,不会压缩和转化 47 | }, 48 | plugins: [ 49 | // new webpack.optimize.ModuleConcatenationPlugin(), //作用域提升。!!!在使用cdn时,axios和iview有问题,先不用!!! 50 | ], 51 | }; 52 | -------------------------------------------------------------------------------- /build-tools/webpack/webpack.prod.min.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); 3 | const TerserPlugin = require('terser-webpack-plugin'); 4 | 5 | const { distDir } = require('../constant'); 6 | const { resolveApp } = require('../utils/paths'); 7 | 8 | console.log( 9 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 10 | `读取了: ${__filename.slice(__dirname.length + 1)}` 11 | )}` 12 | ); 13 | 14 | module.exports = { 15 | mode: 'production', 16 | devtool: 'source-map', 17 | entry: { 18 | main: { 19 | import: resolveApp('./index.js'), 20 | }, 21 | }, 22 | output: { 23 | path: distDir, 24 | // publicPath: "/library/", 25 | filename: 'billd.min.js', 26 | library: { 27 | name: 'Billd', 28 | // root: 'MyLibrary', 29 | // amd: 'my-library', 30 | // commonjs: 'my-common-library', 31 | type: 'umd', 32 | }, 33 | // library: "billd_ui", 34 | // libraryTarget: "umd",//module,commonjs,umd 35 | // umdNamedDefine: true, 36 | }, 37 | externals: { 38 | vue: { 39 | root: 'Vue', 40 | commonjs2: 'vue', 41 | commonjs: 'vue', 42 | amd: 'vue', 43 | }, 44 | }, 45 | optimization: { 46 | usedExports: false, // production模式或者不设置usedExports,它默认就是true。usedExports的目的是标注出来哪些函数是没有被使用 unused,会结合Terser进行处理 47 | sideEffects: false, // 告知 webpack 去辨识 package.json 中的 副作用 标记或规则 48 | minimize: true, // 是否开启Terser,不手动设置的话默认就根据环境判断,production环境就是true,非production环境就是false。设置false后,不会压缩和转化 49 | minimizer: [ 50 | `...`, // 在 webpack@5 中,你可以使用 `...` 语法来扩展现有的 minimizer(即 `terser-webpack-plugin`) 51 | new TerserPlugin({ 52 | parallel: true, // 使用多进程并发运行以提高构建速度 53 | extractComments: false, // 去除打包生成的bundle.js.LICENSE.txt 54 | terserOptions: { 55 | // Terser 压缩配置 56 | parse: { 57 | // default {},如果希望指定其他解析选项,请传递一个对象。 58 | }, 59 | compress: { 60 | // default {},传递false表示完全跳过压缩。传递一个对象来指定自定义压缩选项。 61 | arguments: true, // default: false,尽可能将参数[index]替换为函数参数名 62 | dead_code: true, // 删除死代码,默认就会删除,实际测试设置false也没用,还是会删除 63 | toplevel: false, // default: false,在顶级作用域中删除未引用的函数("funcs")和/或变量("vars"), 设置true表示同时删除未引用的函数和变量 64 | keep_classnames: false, // default: false,传递true以防止压缩器丢弃类名 65 | keep_fnames: false, // default: false,传递true以防止压缩器丢弃函数名 66 | }, 67 | /** 68 | * mangle,默认值true,会将keep_classnames,keep_fnames,toplevel等等mangle options的所有选项设为true。 69 | * 传递false以跳过篡改名称,或者传递一个对象来指定篡改选项 70 | */ 71 | mangle: true, 72 | toplevel: true, // default false,如果希望启用顶级变量和函数名修改,并删除未使用的变量和函数,则设置为true。 73 | keep_classnames: true, // default: undefined,传递true以防止丢弃或混淆类名。 74 | keep_fnames: true, // default: false,传递true以防止函数名被丢弃或混淆。 75 | }, 76 | }), 77 | new CssMinimizerPlugin({ 78 | parallel: true, // 使用多进程并发执行,提升构建速度。 79 | }), // css压缩,去除无用的空格等等 80 | ], 81 | }, 82 | plugins: [], 83 | }; 84 | -------------------------------------------------------------------------------- /changelog.option.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | console.log( 4 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 5 | `读取了: ${__filename.slice(__dirname.length + 1)}` 6 | )}` 7 | ); 8 | 9 | // https://github.com/CaoMeiYouRen/conventional-changelog-cmyr-config 10 | // https://github.com/ITxiaohao/conventional-changelog-custom-config 11 | // https://juejin.cn/post/6844903888072654856#heading-10 12 | // https://blog.cmyr.ltd/archives/caf24092.html 13 | // 上面的都是fork了官方的预设二次修改的实现自定义的, 14 | // 我这里直接使用conventional-changelog-eslint的预设,它会生成所有类型(feat/fix/docs/style等等)的commit 15 | 16 | module.exports = { 17 | // writerOpts: { 18 | // transform: (commit, context) => { 19 | // console.log(commit) 20 | // return commit 21 | // }, 22 | // } 23 | }; 24 | -------------------------------------------------------------------------------- /components/components.js: -------------------------------------------------------------------------------- 1 | export { default as Loading } from './loading'; 2 | export { default as Switch } from './switch'; 3 | export { default as Icon } from './icon'; 4 | export { default as Table } from './table'; 5 | export { default as Modal } from './modal'; 6 | export { default as Message } from './message'; 7 | -------------------------------------------------------------------------------- /components/icon/index.js: -------------------------------------------------------------------------------- 1 | const Icon = { 2 | name: 'BIcon', 3 | inheritAttrs: false, 4 | render() { 5 | console.warn( 6 | 'please use @huangshuisheng/icons-vue(https://www.npmjs.com/package/@huangshuisheng/icons-vue)' 7 | ); 8 | }, 9 | }; 10 | 11 | Icon.install = function (Vue) { 12 | Vue.component(Icon.name, Icon); 13 | }; 14 | 15 | export default Icon; 16 | -------------------------------------------------------------------------------- /components/icon/style/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/components/icon/style/index.js -------------------------------------------------------------------------------- /components/index.js: -------------------------------------------------------------------------------- 1 | import pkg from '../package.json'; 2 | import * as components from './components'; 3 | 4 | export * from './components'; 5 | 6 | const install = function (Vue) { 7 | Object.keys(components).forEach((key) => { 8 | const component = components[key]; 9 | if (component.install) { 10 | Vue.use(components[key]); 11 | } 12 | }); 13 | Vue.prototype.$Message = components.Message; 14 | }; 15 | 16 | // export导出的不是一个对象!!!下面的是错误写法 17 | // export { install: install }; 18 | export default { install, version: pkg.version }; 19 | -------------------------------------------------------------------------------- /components/loading/index.jsx: -------------------------------------------------------------------------------- 1 | import { LoadingOutlined } from '@huangshuisheng/icons-vue'; 2 | 3 | const Loading = { 4 | name: 'BLoading', 5 | components: { LoadingOutlined }, 6 | render() { 7 | return ( 8 |
9 | 17 | 加载中... 18 |
19 | ); 20 | }, 21 | }; 22 | 23 | Loading.install = function (Vue) { 24 | Vue.component(Loading.name, Loading); 25 | }; 26 | 27 | export default Loading; 28 | -------------------------------------------------------------------------------- /components/loading/style/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/components/loading/style/index.js -------------------------------------------------------------------------------- /components/message/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import Main from './main'; 4 | 5 | let baseConfig = { 6 | type: 'info', 7 | closeAble: false, 8 | duration: 2000, 9 | }; 10 | const MessageConstructor = Vue.extend(Main); 11 | const messageInstance = new MessageConstructor(); 12 | messageInstance.$mount(); 13 | // document.body.appendChild(messageInstance.$el); 14 | // 支持服务端渲染 15 | Vue.mixin({ 16 | mounted() { 17 | document.body.appendChild(messageInstance.$el); 18 | }, 19 | }); 20 | 21 | const Message = function (options) { 22 | const newOptions = { ...baseConfig, ...options }; 23 | if (!messageInstance[newOptions.type]) 24 | return console.error( 25 | `Message.type: '${newOptions.type}' is error, Message.type can only be one of 'success' 'warning' 'info' 'error' 'loading'` 26 | ); 27 | return messageInstance[newOptions.type](newOptions); 28 | }; 29 | 30 | ['success', 'warning', 'info', 'error', 'loading'].forEach((type) => { 31 | Message[type] = (options) => { 32 | options.type = type; 33 | return Message(options); 34 | }; 35 | }); 36 | 37 | Message.closeAll = function () { 38 | messageInstance.closeAll(); 39 | }; 40 | Message.config = function (options) { 41 | baseConfig = { ...baseConfig, ...options }; 42 | }; 43 | 44 | export default Message; 45 | -------------------------------------------------------------------------------- /components/message/main.jsx: -------------------------------------------------------------------------------- 1 | // import HssIcon from '../icon/index'; 2 | import BilldIcon from '../icon'; 3 | // const closepng = require('../../assets/img/close.svg'); 4 | 5 | export default { 6 | props: { 7 | icon: undefined, 8 | msg: undefined, 9 | time: { 10 | type: Number, 11 | default: undefined, 12 | }, 13 | }, 14 | // components: { HssIcon }, 15 | data() { 16 | return { 17 | list: [], 18 | timerList: [], 19 | timerID: 1, 20 | bool: true, 21 | }; 22 | }, 23 | render() { 24 | /** 25 | * 这个变量不能定义在外层,这和vue的响应式更新有关,vue会收集依赖然后统一更新(即异步更新),如果放在外层就是全局变量,会导致最后的一次更新覆盖掉之前的更新 26 | * 必须定义在函数里面作为函数的局部变量,以闭包的形式返回出去,这样才正确。 27 | */ 28 | // const iconData = { 29 | // type: '', 30 | // customStyle: { marginRight: '5px' }, 31 | // spin: false, 32 | // }; 33 | function handleType(type) { 34 | const iconData = { 35 | type: '', 36 | customStyle: { marginRight: '5px', marginTop: '1px' }, 37 | spin: false, 38 | }; 39 | switch (type) { 40 | case 'success': 41 | iconData.type = 'CheckCircleFilled'; 42 | iconData.customStyle.color = '#72c140'; 43 | break; 44 | case 'warning': 45 | iconData.type = 'ExclamationCircleFilled'; 46 | iconData.customStyle.color = '#efb041'; 47 | break; 48 | case 'error': 49 | iconData.type = 'CloseCircleFilled'; 50 | iconData.customStyle.color = '#e13c39'; 51 | break; 52 | case 'info': 53 | iconData.type = 'ExclamationCircleFilled'; 54 | iconData.customStyle = Object.assign(iconData.customStyle, { 55 | color: '#448ef7', 56 | display: 'block', 57 | transform: 'rotate(180deg)', 58 | }); 59 | break; 60 | case 'loading': 61 | iconData.type = 'LoadingOutlined'; 62 | iconData.customStyle.color = '#448ef7'; 63 | iconData.spin = true; 64 | break; 65 | default: 66 | iconData.type = 'ExclamationCircleFilled'; 67 | iconData.customStyle = Object.assign(iconData.customStyle, { 68 | color: '#448ef7', 69 | display: 'block', 70 | transform: 'rotate(180deg)', 71 | }); 72 | break; 73 | } 74 | return iconData; 75 | } 76 | 77 | return ( 78 |
79 |
80 | 81 | {this.list.map((item, index) => ( 82 |
83 |
84 |
85 | {/* */} 86 | {!item.icon && ( 87 | 90 | )} 91 | {this.renderDom('icon', item.icon)} 92 | {this.$slots.icon} 93 | {/* */} 94 | {/* */} 95 | {this.renderDom('content', item.content)} 96 | {this.$slots.content} 97 | {/* */} 98 | {item.closeAble && ( 99 | this.close(item)} 102 | type="CloseOutlined" 103 | > 104 | )} 105 |
106 |
107 |
108 | ))} 109 |
110 |
111 |
112 | ); 113 | }, 114 | computed: {}, 115 | created() {}, 116 | mounted() {}, 117 | methods: { 118 | transition() { 119 | // if (this.bool == false) { 120 | // this.bool = true; 121 | // } 122 | setTimeout(() => { 123 | this.bool = false; 124 | }, 100); 125 | return this.bool; 126 | }, 127 | isVNode(v) { 128 | const vnode = this.$createElement(
); 129 | const VNode = vnode.constructor; 130 | return v instanceof VNode; 131 | }, 132 | close(v) { 133 | const index = this.list.findIndex((item) => item.id === v.id); 134 | this.list.splice(index, 1); 135 | clearTimeout(this.timerList[index].timer); 136 | this.timerList.splice(index, 1); 137 | }, 138 | closeAll() { 139 | const { length } = this.list; 140 | for (let i = length - 1; i >= 0; i--) { 141 | this.close(this.list[i]); 142 | } 143 | }, 144 | handle({ type, icon, content, closeAble, duration }) { 145 | const id = this.timerID++; 146 | const temp = { 147 | id, 148 | type, 149 | icon, 150 | content, 151 | closeAble, 152 | duration, 153 | }; 154 | this.list.push(temp); 155 | const timer = setTimeout(() => { 156 | this.close(temp); 157 | }, duration); 158 | this.timerList.push({ id, timer }); 159 | }, 160 | success(v) { 161 | this.handle(v); 162 | }, 163 | warning(v) { 164 | this.handle(v); 165 | }, 166 | error(v) { 167 | this.handle(v); 168 | }, 169 | info(v) { 170 | this.handle(v); 171 | }, 172 | loading(v) { 173 | this.handle(v); 174 | }, 175 | renderDom(slot, node) { 176 | if (this.isVNode(node)) { 177 | // console.log('???????', node); 178 | this.$slots[slot] = node; 179 | } else { 180 | // console.log('!!!!!!', node); //node有可能是undefined,这样的话就会渲染多一个span标签 181 | this.$slots[slot] = node && {node}; 182 | } 183 | }, 184 | }, 185 | }; 186 | -------------------------------------------------------------------------------- /components/message/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /components/message/style/index.less: -------------------------------------------------------------------------------- 1 | .billd-list-enter-active { 2 | transition: all 0.5s ease; 3 | } 4 | .billd-list-enter, .billd-list-leave-to 5 | /* .list-leave-active for below version 2.1.8 */ { 6 | opacity: 0; 7 | transform: translateY(-60px); 8 | } 9 | .billd-message { 10 | position: fixed; 11 | top: 10px; 12 | width: 100%; 13 | text-align: center; 14 | pointer-events: none; 15 | .content { 16 | display: inline-block; 17 | padding: 10px 16px; 18 | border-radius: 4px; 19 | background: #fff; 20 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 21 | pointer-events: auto; 22 | 23 | .close-btn { 24 | margin-left: 20px; 25 | width: 15px; 26 | height: 15px; 27 | opacity: 0.6; 28 | cursor: pointer; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /components/modal/foot.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | components: {}, 3 | data() { 4 | return {}; 5 | }, 6 | computed: {}, 7 | created() {}, 8 | mounted() {}, 9 | render() { 10 | return ( 11 |
12 |
13 | {this.$scopedSlots.footer !== undefined && 14 | (this.$scopedSlots.footer({ ...this.$attrs }) === undefined ? ( 15 |
16 | 17 | {this.$attrs.cancelText || '取消'} 18 | 19 | 20 | {this.$attrs.confirmText || '确定'} 21 | 22 |
23 | ) : ( 24 | this.$scopedSlots.footer({ ...this.$attrs }) 25 | ))} 26 |
27 |
28 | ); 29 | }, 30 | methods: { 31 | cancel() { 32 | // console.log("h-foot组件里面emit cancel事件"); 33 | this.$emit('foot-cancel'); 34 | }, 35 | confirm() { 36 | // console.log("h-foot组件里面emit confirm事件"); 37 | this.$emit('foot-confirm'); 38 | }, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /components/modal/index.jsx: -------------------------------------------------------------------------------- 1 | import { Fragment as HVueFragment } from 'vue-fragment'; 2 | 3 | import HFoot from './foot'; 4 | 5 | const Modal = { 6 | name: 'BModal', 7 | inheritAttrs: false, // 将自定义组件的attrs不显示在渲染的html元素上,防止冲突(比如title) 8 | props: { 9 | // cancelText: String, 10 | // confirmText: String, 11 | visible: { 12 | type: Boolean, 13 | default: false, 14 | }, 15 | }, 16 | components: { HFoot, HVueFragment }, 17 | data() { 18 | return { 19 | modalvisible: this.visible, 20 | }; 21 | }, 22 | watch: { 23 | visible(newVal) { 24 | this.modalvisible = newVal; 25 | this.$emit('visible-change', newVal); 26 | }, 27 | }, 28 | model: { 29 | prop: 'visible', 30 | // event: "change", 31 | event: 'input', 32 | }, 33 | render() { 34 | return ( 35 |
46 | 66 |
67 | ); 68 | }, 69 | computed: {}, 70 | created() {}, 71 | mounted() { 72 | // console.log(this.$attrs, this.visible); 73 | }, 74 | methods: { 75 | footCancel() { 76 | this.$emit('input', false); 77 | this.$emit('on-cancel'); 78 | }, 79 | footConfirm() { 80 | this.$emit('input', false); 81 | this.$emit('on-confirm'); 82 | }, 83 | close() { 84 | // console.log("点击了modal的关闭按钮,隐藏modal"); 85 | this.$emit('input', false); 86 | this.$emit('on-close'); 87 | }, 88 | // 如果未定义maskClosable或者手动设置maskClosable为true,点击遮罩都是会隐藏modal;即只有手动设置maskClosable为false才会点击遮罩层不隐藏modal 89 | maskClose() { 90 | // console.log("点击了遮罩层,判断maskClosable属性觉得是否隐藏modal"); 91 | if ( 92 | this.$attrs.maskClosable === undefined || 93 | this.$attrs.maskClosable === true 94 | ) { 95 | // console.log("判断结果:隐藏modal"); 96 | this.$emit('input', false); 97 | } else { 98 | // console.log("判断结果:不隐藏modal"); 99 | } 100 | }, 101 | }, 102 | }; 103 | 104 | Modal.install = function (Vue) { 105 | Vue.component(Modal.name, Modal); 106 | }; 107 | 108 | export default Modal; 109 | -------------------------------------------------------------------------------- /components/modal/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /components/modal/style/index.less: -------------------------------------------------------------------------------- 1 | .billd-modal-mask { 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | .modal-wrap { 8 | position: relative; 9 | left: 50%; 10 | transform: translateX(-50%); 11 | top: 30%; 12 | width: 500px; 13 | border-radius: 4px; 14 | background-color: #fff; 15 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 16 | 17 | box-sizing: border-box; 18 | 19 | .modal-head { 20 | padding: 10px 20px; 21 | display: flex; 22 | justify-content: space-between; 23 | border-bottom: 1px solid #e8e8e8; 24 | .modal-close-btn { 25 | cursor: pointer; 26 | } 27 | } 28 | .modal-content { 29 | padding: 30px 20px; 30 | border-bottom: 1px solid #e8e8e8; 31 | } 32 | } 33 | } 34 | 35 | .billd-modal-foot { 36 | padding: 10px 20px; 37 | text-align: right; 38 | .confirm-btn, 39 | .cancel-btn { 40 | display: inline-block; 41 | padding: 5px 15px; 42 | background-color: red; 43 | border-radius: 4px; 44 | background-color: #fff; 45 | text-align: center; 46 | background-image: none; 47 | box-shadow: 0 2px 0 rgb(0 0 0 / 2%); 48 | cursor: pointer; 49 | } 50 | .cancel-btn { 51 | border: 1px solid #d9d9d9; 52 | box-shadow: 0 2px 0 rgb(0 0 0 / 2%); 53 | } 54 | .confirm-btn { 55 | background-color: #1890ff; 56 | color: white; 57 | margin-left: 10px; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /components/style.js: -------------------------------------------------------------------------------- 1 | import './message/style'; 2 | import './modal/style'; 3 | import './switch/style'; 4 | import './table/style'; 5 | -------------------------------------------------------------------------------- /components/switch/index.jsx: -------------------------------------------------------------------------------- 1 | const Switch = { 2 | name: 'BSwitch', 3 | components: {}, 4 | inheritAttrs: false, // 将自定义组件的attrs不显示在渲染的html元素上,防止冲突(比如title) 5 | model: { 6 | prop: 'switchVal', 7 | event: 'input', 8 | }, 9 | props: { 10 | switchVal: { 11 | type: Boolean, 12 | default: undefined, 13 | }, 14 | }, 15 | data() { 16 | return { 17 | isChecked: this.switchVal, 18 | }; 19 | }, 20 | computed: {}, 21 | watch: { 22 | switchVal(newVal) { 23 | // console.log("switchVal变了"); 24 | this.isChecked = newVal; 25 | this.$emit('changeSwitch', this.isChecked); 26 | }, 27 | }, 28 | mounted() { 29 | /** 30 | * 如果defaultChecked和v-model绑定的值冲突,则v-model的优先级高,defaultChecked不生效。 31 | * 即switchVal是false,那么就是isChecked就是false。 32 | * 其次,如果defaultChecked是true且swtichVal也是开或者没设置,最终结果就是开启 33 | */ 34 | // console.log(this.$attrs, this.switchVal); 35 | 36 | if (this.switchVal !== undefined) { 37 | this.isChecked = this.switchVal; 38 | return; 39 | } 40 | if ( 41 | this.$attrs.defaultChecked && 42 | (this.switchVal === undefined || this.switchVal === true) 43 | ) { 44 | this.isChecked = true; 45 | } 46 | }, 47 | created() {}, 48 | 49 | methods: { 50 | clickSwitch(event) { 51 | if (this.switchVal === undefined) { 52 | // 如果没有使用v-model或者switchVal,则手动回调事件 53 | this.$emit('clickSwitch', this.isChecked, event); // 最终拿到两个形参,即:fasle/true,event 54 | this.isChecked = !this.isChecked; 55 | this.$emit('changeSwitch', this.isChecked); 56 | } else { 57 | this.$emit('clickSwitch', this.isChecked, event); // 最终拿到两个形参,即:fasle/true,event 58 | this.$emit('input', !this.isChecked); 59 | } 60 | 61 | // this.$emit("clickSwitch", { checked: !this.isChecked, event }); //最终拿到一个形参,即:{} 62 | }, 63 | }, 64 | render() { 65 | // 如果有插槽,就使用插槽,如果没有插槽,就使用openText,如果openText没有,就代表没有文字 66 | return ( 67 |
this.clickSwitch(e)} 73 | > 74 | 75 | {this.isChecked 76 | ? this.$scopedSlots.openSlot 77 | ? this.$scopedSlots.openSlot({}) 78 | : this.$attrs.openText 79 | ? this.$attrs.openText 80 | : ' ' 81 | : this.$scopedSlots.closeSlot 82 | ? this.$scopedSlots.closeSlot({}) 83 | : this.$attrs.closeText 84 | ? this.$attrs.closeText 85 | : ''} 86 | 87 |
88 | ); 89 | }, 90 | }; 91 | 92 | Switch.install = function (Vue) { 93 | Vue.component(Switch.name, Switch); 94 | }; 95 | 96 | export default Switch; 97 | -------------------------------------------------------------------------------- /components/switch/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /components/switch/style/index.less: -------------------------------------------------------------------------------- 1 | .billd-switch { 2 | position: relative; 3 | display: inline-block; 4 | box-sizing: border-box; 5 | min-width: 40px; 6 | height: 22px; 7 | border-radius: 10px; 8 | background-color: rgba(0, 0, 0, 0.25); 9 | font-size: 14px; 10 | line-height: 22px; 11 | cursor: pointer; 12 | &.billd-switch-checked { 13 | background-color: #1890ff; 14 | font-size: 10px; 15 | &:after { 16 | left: 100%; 17 | margin-left: -2px; 18 | transform: translate(-100%, -50%); 19 | } 20 | .billd-switch-inner { 21 | margin-right: 24px; 22 | margin-left: 6px; 23 | 24 | user-select: none; 25 | } 26 | } 27 | .billd-switch-inner { 28 | display: block; 29 | margin-right: 6px; 30 | margin-left: 24px; 31 | color: white; 32 | font-size: 12px; 33 | 34 | user-select: none; 35 | } 36 | &:after { 37 | position: absolute; 38 | top: 50%; 39 | left: 2px; 40 | width: 18px; 41 | height: 18px; 42 | border-radius: 18px; 43 | background-color: #fff; 44 | content: ' '; 45 | cursor: pointer; 46 | transition: all 0.36s cubic-bezier(0.78, 0.14, 0.15, 0.86); 47 | transform: translate(0, -50%); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /components/table/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /components/table/style/index.less: -------------------------------------------------------------------------------- 1 | .billd-table { 2 | position: relative; 3 | ::-webkit-scrollbar { 4 | // width: 8px; 5 | // height: 15px; 6 | // background: #b9b9b9; 7 | } 8 | &.border { 9 | table thead > tr > th, 10 | table tbody > tr > td { 11 | box-sizing: border-box; 12 | border-right: 1px solid #e8e8e8; 13 | } 14 | } 15 | .billd-table-header { 16 | background-color: #fafafa; 17 | } 18 | .billd-table-body { 19 | background-color: #fff; 20 | } 21 | table { 22 | min-width: 100%; 23 | table-layout: fixed; 24 | // width: 100%; 25 | } 26 | .billd-checkbox { 27 | position: relative; 28 | display: block; 29 | .billd-checkbox-input { 30 | position: absolute; 31 | top: 0; 32 | right: 0; 33 | bottom: 0; 34 | left: 0; 35 | z-index: 1; 36 | margin: 0; 37 | width: 18px; 38 | height: 18px; 39 | opacity: 0; 40 | cursor: pointer; 41 | &.billd-checkbox-checked { 42 | opacity: 1; 43 | } 44 | &.no-all .billd-checkbox-inner::after { 45 | opacity: 1 !important; 46 | } 47 | } 48 | &.billd-checkbox-disabled { 49 | cursor: not-allowed; 50 | 51 | .billd-checkbox-inner, 52 | .billd-checkbox-input { 53 | border-color: #d9d9d9 !important; 54 | background-color: #f5f5f5; 55 | cursor: not-allowed; 56 | } 57 | } 58 | .billd-checkbox-inner { 59 | position: relative; 60 | top: 0; 61 | left: 0; 62 | display: block; 63 | width: 16px; 64 | height: 16px; 65 | border: 1px solid #d9d9d9; 66 | border-collapse: separate; 67 | border-radius: 2px; 68 | background-color: #fff; 69 | transition: all 0.3s; 70 | &.no-all::after { 71 | opacity: 1 !important; 72 | } 73 | &::after { 74 | position: absolute; 75 | top: 50%; 76 | left: 50%; 77 | width: 8px; 78 | height: 8px; 79 | border: 0; 80 | background-color: #1890ff; 81 | content: ' '; 82 | opacity: 0; 83 | transform: translate(-50%, -50%) scale(1); 84 | } 85 | } 86 | } 87 | .billd-table-selection-col { 88 | width: 60px; 89 | } 90 | .table-scroll { 91 | overflow: auto; 92 | overflow-x: hidden; 93 | width: 100%; 94 | .billd-hide-scrollbar::-webkit-scrollbar { 95 | background-color: transparent; 96 | } 97 | .billd-table-scroll-body { 98 | } 99 | .billd-table-tbody > tr:hover { 100 | background-color: #e9f6fe; 101 | } 102 | table { 103 | border-spacing: 0; 104 | // text-align: left; 105 | // border-radius: 4px 4px 0 0; 106 | border-collapse: separate; 107 | thead > tr > th { 108 | border-bottom: 1px solid #e8e8e8; 109 | background: #fafafa; 110 | color: rgba(0, 0, 0, 0.85); 111 | text-align: left; 112 | font-weight: 500; 113 | transition: background 0.3s ease; 114 | } 115 | tbody > tr > td { 116 | padding: 16px; 117 | border-bottom: 1px solid #e8e8e8; 118 | } 119 | } 120 | } 121 | .billd-table-tbody { 122 | td { 123 | // word-wrap: break-word; 124 | word-break: break-word; 125 | 126 | overflow-wrap: break-word; 127 | } 128 | } 129 | .ellipsis { 130 | overflow: hidden; 131 | text-overflow: ellipsis; 132 | white-space: nowrap; 133 | } 134 | .billd-table-tbody > tr.hovertr { 135 | background-color: #e9f6fe; 136 | } 137 | .billd-table-thead > tr > th { 138 | padding: 16px; 139 | 140 | overflow-wrap: break-word; 141 | } 142 | 143 | .fixed-left { 144 | position: absolute; 145 | top: 0; 146 | left: 0; 147 | z-index: 100; 148 | overflow: hidden; 149 | box-shadow: 6px 0 6px -4px rgba(0, 0, 0, 0.15); 150 | table { 151 | border-spacing: 0; 152 | // text-align: left; 153 | // border-radius: 4px 4px 0 0; 154 | border-collapse: separate; 155 | } 156 | .billd-table-thead { 157 | background-color: #fafafa; 158 | } 159 | // .billd-table-tbody > tr:hover { 160 | // background-color: #e9f6fe; 161 | // } 162 | & > div { 163 | // background: #fafafa; 164 | color: rgba(0, 0, 0, 0.85); 165 | font-weight: 500; 166 | transition: background 0.3s ease; 167 | 168 | overflow-wrap: break-word; 169 | } 170 | 171 | .billd-table-thead > tr > th { 172 | border-bottom: 1px solid #e8e8e8; 173 | } 174 | .billd-table-tbody > tr > td { 175 | padding: 16px; 176 | border-bottom: 1px solid #e8e8e8; 177 | } 178 | } 179 | .fixed-right { 180 | position: absolute; 181 | top: 0; 182 | right: 0; 183 | overflow: hidden; 184 | background-color: #ffffff; 185 | // margin-bottom: -17px; 186 | // overflow: scroll; 187 | box-shadow: -6px 0 6px -4px rgba(0, 0, 0, 0.15); 188 | table { 189 | border-spacing: 0; 190 | // text-align: left; 191 | // border-radius: 4px 4px 0 0; 192 | border-collapse: separate; 193 | } 194 | .billd-table-thead { 195 | background-color: #fafafa; 196 | } 197 | // .billd-table-tbody > tr:hover { 198 | // background-color: #e9f6fe; 199 | // } 200 | & > div { 201 | // background: #fafafa; 202 | color: rgba(0, 0, 0, 0.85); 203 | font-weight: 500; 204 | transition: background 0.3s ease; 205 | 206 | overflow-wrap: break-word; 207 | } 208 | 209 | .billd-table-thead > tr > th { 210 | border-bottom: 1px solid #e8e8e8; 211 | } 212 | .billd-table-tbody > tr > td { 213 | padding: 16px; 214 | border-bottom: 1px solid #e8e8e8; 215 | } 216 | } 217 | 218 | .scroll-bar { 219 | min-width: unset; 220 | &::-webkit-scrollbar { 221 | min-width: inherit; 222 | background-color: transparent; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /components/utils/common.js: -------------------------------------------------------------------------------- 1 | export function throttle(fn, interval, option) { 2 | let lastTime = 0; 3 | let timer; 4 | option = option || {}; 5 | const trailing = option.trailing || false; 6 | return function () { 7 | const _this = this; 8 | const _arguments = arguments; 9 | const newTime = new Date().getTime(); 10 | 11 | if (timer) { 12 | clearTimeout(timer); 13 | } 14 | 15 | let result; 16 | return new Promise((resolve) => { 17 | if (newTime - lastTime > interval) { 18 | result = fn.apply(_this, _arguments); 19 | resolve(result); 20 | 21 | lastTime = newTime; 22 | } else if (trailing) { 23 | timer = setTimeout(() => { 24 | result = fn.apply(_this, _arguments); 25 | resolve(result); 26 | }, interval); 27 | } 28 | }); 29 | }; 30 | } 31 | export function debounce(fn, delay, leading) { 32 | let timer; 33 | leading = leading || false; 34 | const debounceFn = function () { 35 | if (timer) { 36 | clearTimeout(timer); 37 | } 38 | const _this = this; 39 | const _arguments = arguments; 40 | return new Promise((resolve) => { 41 | if (leading) { 42 | let isFirst = false; 43 | if (!timer) { 44 | resolve(fn.apply(_this, _arguments)); 45 | isFirst = true; 46 | } 47 | timer = setTimeout(() => { 48 | timer = null; 49 | if (!isFirst) { 50 | resolve(fn.apply(_this, _arguments)); 51 | } 52 | }, delay); 53 | } else { 54 | timer = setTimeout(() => { 55 | resolve(fn.apply(_this, _arguments)); 56 | }, delay); 57 | } 58 | }); 59 | }; 60 | 61 | debounceFn.cancel = function () { 62 | clearTimeout(timer); 63 | timer = null; 64 | }; 65 | return debounceFn; 66 | } 67 | 68 | export function getScrollBarWidth() { 69 | const inner = document.createElement('p'); 70 | inner.style.width = '100%'; 71 | inner.style.height = '200px'; 72 | 73 | const outer = document.createElement('div'); 74 | outer.style.position = 'absolute'; 75 | outer.style.top = '0px'; 76 | outer.style.left = '0px'; 77 | outer.style.visibility = 'hidden'; 78 | outer.style.width = '200px'; 79 | outer.style.height = '150px'; 80 | outer.style.overflow = 'hidden'; 81 | outer.appendChild(inner); 82 | 83 | document.body.appendChild(outer); 84 | const w1 = inner.offsetWidth; 85 | outer.style.overflow = 'scroll'; 86 | let w2 = inner.offsetWidth; 87 | if (w1 === w2) w2 = outer.clientWidth; 88 | 89 | document.body.removeChild(outer); 90 | 91 | return w1 - w2; 92 | } 93 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // 这个文件是生产dist用的 2 | // 引入所有样式 3 | require.context('./components', true, /^\.\/([^/]+)\/style\/index.js$/); 4 | // 引入所有组件 5 | require('./components'); 6 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": {} 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "billd-ui", 3 | "version": "1.2.8", 4 | "description": "基于vue2.x构建的billd-ui组件库", 5 | "keywords": [ 6 | "billd", 7 | "ui", 8 | "vue2" 9 | ], 10 | "homepage": "http://project.hsslive.cn/billd-ui/", 11 | "bugs": { 12 | "url": "https://github.com/galaxy-s10/billd-ui/issues" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/galaxy-s10/billd-ui" 17 | }, 18 | "license": "MIT", 19 | "author": "shuisheng ", 20 | "sideEffects": [ 21 | "es/**/style/*", 22 | "lib/**/style/*", 23 | "dist/*" 24 | ], 25 | "main": "lib/index.js", 26 | "module": "es/index.js", 27 | "typings": "lib/index.d.ts", 28 | "files": [ 29 | "/lib", 30 | "/es", 31 | "/dist" 32 | ], 33 | "scripts": { 34 | "changelog": "conventional-changelog -n ./changelog.option.js -p eslint -i CHANGELOG.md -s", 35 | "changelog:first": "conventional-changelog -n ./changelog.option.js -p eslint -i CHANGELOG.md -s -r 0", 36 | "commit": "cz", 37 | "compile": "gulp -f ./build-tools/gulpfile.js", 38 | "compile:es": "gulp -f ./build-tools/gulpfile.js es", 39 | "compile:lib": "gulp -f ./build-tools/gulpfile.js lib", 40 | "compile:dist": "gulp -f ./build-tools/gulpfile.js dist", 41 | "dev": "webpack serve --config ./build-tools/webpack/webpack.common.js --env development", 42 | "dev:dashboard": "webpack-dashboard -- webpack serve --config ./build-tools/webpack/webpack.common.js --env development", 43 | "dist": "cross-env BUILD_DEMO=ture webpack --config ./build-tools/webpack/webpack.common.js --env production", 44 | "lint": "eslint --config ./.eslintrc.js . --ext .js,.jsx,.ts,.tsx,.vue", 45 | "lint:fix": "eslint --config ./.eslintrc.js --fix . --ext .js,.jsx,.ts,.tsx,.vue", 46 | "prepare": "husky install", 47 | "prettier": "prettier --write *", 48 | "release:local": "node ./scripts/release.js", 49 | "release:online": "node ./scripts/publish.js" 50 | }, 51 | "config": { 52 | "commitizen": { 53 | "path": "./node_modules/cz-conventional-changelog" 54 | } 55 | }, 56 | "dependencies": { 57 | "@babel/runtime": "^7.18.9", 58 | "@huangshuisheng/icons-svg": "^1.1.1", 59 | "@huangshuisheng/icons-vue": "^1.1.1", 60 | "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", 61 | "vue": "^2.6.14", 62 | "vue-fragment": "^1.5.2" 63 | }, 64 | "devDependencies": { 65 | "@babel/core": "^7.18.9", 66 | "@babel/plugin-proposal-object-rest-spread": "^7.18.9", 67 | "@babel/plugin-transform-object-assign": "^7.18.6", 68 | "@babel/plugin-transform-runtime": "^7.18.9", 69 | "@babel/preset-env": "^7.18.9", 70 | "@commitlint/cli": "^17.0.3", 71 | "@commitlint/config-conventional": "^17.0.3", 72 | "@soda/friendly-errors-webpack-plugin": "^1.8.0", 73 | "@typescript-eslint/eslint-plugin": "^5.30.6", 74 | "@typescript-eslint/parser": "^5.30.6", 75 | "@vue/babel-preset-jsx": "^1.2.4", 76 | "@vue/preload-webpack-plugin": "^2.0.0", 77 | "babel-loader": "^8.2.2", 78 | "babel-plugin-import": "^1.13.3", 79 | "chalk": "^4.1.1", 80 | "clean-webpack-plugin": "^4.0.0-alpha.0", 81 | "commitizen": "^4.2.4", 82 | "compression-webpack-plugin": "^8.0.1", 83 | "conventional-changelog-cli": "^2.2.2", 84 | "cross-env": "^7.0.3", 85 | "css-loader": "^5.2.6", 86 | "css-minimizer-webpack-plugin": "^3.0.2", 87 | "cz-conventional-changelog": "^3.3.0", 88 | "eslint": "^7.31.0", 89 | "eslint-config-airbnb-base": "^14.2.1", 90 | "eslint-config-prettier": "^8.3.0", 91 | "eslint-loader": "^4.0.2", 92 | "eslint-plugin-import": "^2.26.0", 93 | "eslint-plugin-prettier": "^3.4.0", 94 | "eslint-plugin-vue": "^7.14.0", 95 | "eslint-webpack-plugin": "^3.0.1", 96 | "fs-extra": "^10.1.0", 97 | "gulp": "^4.0.2", 98 | "gulp-babel": "^8.0.0", 99 | "gulp-clean": "^0.4.0", 100 | "gulp-concat": "^2.6.1", 101 | "gulp-less": "^5.0.0", 102 | "gulp-postcss": "^9.0.0", 103 | "gulp-typescript": "^6.0.0-alpha.1", 104 | "html-webpack-plugin": "^5.3.2", 105 | "html-webpack-tags-plugin": "^3.0.1", 106 | "husky": "^8.0.1", 107 | "inquirer": "8", 108 | "less": "^3.13.1", 109 | "less-loader": "^7.3.0", 110 | "lint-staged": "^13.0.3", 111 | "mini-css-extract-plugin": "^1.6.2", 112 | "node-emoji": "^1.11.0", 113 | "portfinder": "^1.0.28", 114 | "postcss": "^8.3.5", 115 | "postcss-loader": "^6.1.1", 116 | "postcss-preset-env": "^6.7.0", 117 | "prettier": "^2.7.1", 118 | "readline": "^1.3.0", 119 | "semver": "^7.3.7", 120 | "speed-measure-webpack-plugin": "^1.5.0", 121 | "standard-version": "^9.5.0", 122 | "style-loader": "^3.0.0", 123 | "through2": "^4.0.2", 124 | "ts-loader": "^9.2.3", 125 | "typescript": "^4.7.4", 126 | "vue-loader": "^15.9.7", 127 | "vue-template-compiler": "^2.6.14", 128 | "webpack": "^5.40.0", 129 | "webpack-cli": "^4.7.2", 130 | "webpack-dashboard": "^3.3.5", 131 | "webpack-dev-server": "^4.11.1", 132 | "webpack-merge": "^5.8.0", 133 | "webpackbar": "^5.0.0-3" 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const postcssPresetEnv = require('postcss-preset-env'); 3 | 4 | console.log( 5 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright( 6 | `读取了: ${__filename.slice(__dirname.length + 1)}` 7 | )}` 8 | ); 9 | 10 | module.exports = { 11 | plugins: [ 12 | // 'autoprefixer', // postcss-preset-env包含了autoprefixer的功能 13 | // 'postcss-preset-env', //简写,具体看各个插件的官网提供几种写法 14 | postcssPresetEnv, 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 12 | 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /scripts/publish.js: -------------------------------------------------------------------------------- 1 | const { execSync } = require('child_process'); 2 | const path = require('path'); 3 | 4 | const { 5 | chalkSUCCESS, 6 | chalkERROR, 7 | chalkINFO, 8 | } = require('../build-tools/utils/chalkTip'); 9 | const pkg = require('../package.json'); 10 | 11 | // WARN: yrm切换镜像的时候,会同步切换npm的镜像!但是nrm切换镜像却不会切换yarn的镜像! 12 | // 当使用npm镜像为npm,但是yarn镜像为taobao或者cnpm的时候,但是yarn run mypublish有时候会成功? 13 | // TIP: 确保发包的时候npm和yarn的镜像都得是npm官方镜像即可,这样不管是yarn/npm run mypublish都会按正常情况走 14 | // 不能在这里通过execSync或者spawnSync切换镜像,会有延迟,该次publish不生效,得下一次再执行publish才会生效 15 | 16 | /** 17 | * spawnSync更底层,spawnSync不会抛出错误,但可以获取它的执行 18 | * 结果(stdout/status/stderr/output等等),根据他们的结果判断后面的逻辑 19 | */ 20 | // const result = spawnSync('npm', ['publish'], { 21 | // stdio: "inherit", 22 | // cwd: path.resolve(__dirname, '../'), 23 | // }); 24 | 25 | // if (!result.status) { 26 | // console.log(chalkSUCCESS(`!!!发布线上${pkg.name}@${pkg.version}成功!!!`)); 27 | // } else { 28 | // console.log(chalkERROR(`!!!发布线上${pkg.name}@${pkg.version}失败!!!`)); 29 | // } 30 | 31 | try { 32 | console.log(chalkINFO(`开始发布线上${pkg.name}@${pkg.version}...`)); 33 | // 如果进程超时或有非零退出代码,execSync将抛出Error 对象 34 | execSync(`git push origin v${pkg.version}`, { stdio: 'inherit' }); 35 | execSync(`git push`, { stdio: 'inherit' }); 36 | execSync('npm publish', { 37 | stdio: 'inherit', 38 | cwd: path.resolve(__dirname, '../'), 39 | }); 40 | console.log( 41 | chalkSUCCESS(`!!!发布线上${pkg.name}@${pkg.version}成功!!!`) 42 | ); 43 | } catch (error) { 44 | console.log(chalkERROR(`!!!发布线上${pkg.name}@${pkg.version}失败!!!`)); 45 | console.log(error); 46 | console.log(chalkERROR(`!!!发布线上${pkg.name}@${pkg.version}失败!!!`)); 47 | } 48 | -------------------------------------------------------------------------------- /scripts/release.js: -------------------------------------------------------------------------------- 1 | const { execSync, exec } = require('child_process'); 2 | const path = require('path'); 3 | 4 | const { readJSONSync, writeJSONSync } = require('fs-extra'); 5 | const inquirer = require('inquirer'); 6 | const semver = require('semver'); 7 | 8 | const { 9 | chalkSUCCESS, 10 | chalkERROR, 11 | chalkINFO, 12 | } = require('../build-tools/utils/chalkTip'); 13 | const { updatePackageJSON } = require('./update'); 14 | 15 | const { name: pkgName, version: currentVersion } = readJSONSync('package.json'); // 项目根目录的package.json 16 | 17 | // scripts/release.js只是实现了release-it的基本功能 18 | 19 | const preId = 20 | semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0]; 21 | 22 | const versionChoices = [ 23 | 'patch', 24 | 'minor', 25 | 'major', 26 | ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : []), 27 | ]; 28 | 29 | const inc = (i) => semver.inc(currentVersion, i, preId); 30 | let targetVersion; 31 | const selectReleaseVersion = async () => { 32 | const { release } = await inquirer.prompt([ 33 | { 34 | type: 'list', 35 | name: 'release', 36 | message: 'Select release type', 37 | choices: versionChoices.map((i) => `${i} (${inc(i)})`), 38 | }, 39 | ]); 40 | const pkg = readJSONSync(path.resolve(__dirname, '../package.json')); // 项目根目录的package.json 41 | targetVersion = release.match(/\((.*)\)/)[1]; 42 | 43 | const { confirmRelease } = await inquirer.prompt([ 44 | { 45 | type: 'confirm', 46 | name: 'confirmRelease', 47 | default: false, 48 | message: `Confirm release v${targetVersion}?`, 49 | }, 50 | ]); 51 | 52 | if (confirmRelease) { 53 | console.log(chalkINFO(`开始本地发布${pkg.name}@${targetVersion}...`)); 54 | 55 | // 更新根目录的package.json版本号 56 | writeJSONSync( 57 | 'package.json', 58 | { ...pkg, version: targetVersion }, 59 | { spaces: 2 } 60 | ); 61 | 62 | // 更新package.json 63 | updatePackageJSON(); 64 | 65 | execSync(`npm run compile`, { stdio: 'inherit' }); 66 | 67 | // 生成changelog 68 | execSync(`npm run changelog`, { stdio: 'inherit' }); 69 | 70 | // git commit 71 | execSync(`git add .`, { stdio: 'inherit' }); 72 | execSync(`git commit -m 'chore(release): v${targetVersion}'`, { 73 | stdio: 'inherit', 74 | }); 75 | 76 | // git tag 77 | execSync(`git tag v${targetVersion}`, { stdio: 'inherit' }); 78 | } else { 79 | console.log(chalkERROR(`取消本地发布${pkg.name}@${targetVersion}!`)); 80 | } 81 | }; 82 | 83 | function gitIsClean() { 84 | return new Promise((resolve, reject) => { 85 | exec('git status -s', (error, stdout, stderr) => { 86 | if (error || stderr) { 87 | reject(error || stderr); 88 | } 89 | if (stdout.length) { 90 | reject(new Error('请确保git工作区干净!')); 91 | } else { 92 | resolve('ok'); 93 | } 94 | }); 95 | }); 96 | } 97 | 98 | (async () => { 99 | try { 100 | await gitIsClean(); 101 | await selectReleaseVersion(); 102 | console.log(chalkSUCCESS(`本地发布${pkgName}@${targetVersion}成功!`)); 103 | } catch (error) { 104 | console.log( 105 | chalkERROR(`!!!本地发布${pkgName}@${targetVersion}失败!!!`) 106 | ); 107 | console.log(error); 108 | console.log( 109 | chalkERROR(`!!!本地发布${pkgName}@${targetVersion}失败!!!`) 110 | ); 111 | } 112 | })(); 113 | -------------------------------------------------------------------------------- /scripts/update.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const { readJSONSync, writeJSONSync } = require('fs-extra'); 4 | 5 | exports.updatePackageJSON = () => { 6 | const pkg = readJSONSync(path.resolve(__dirname, '../package.json')); // 项目根目录的package.json 7 | const pkgPath = path.resolve(__dirname, '../package.json'); // 项目根目录的package.json路径 8 | const packageJSON = readJSONSync(pkgPath); 9 | packageJSON.version = pkg.version; 10 | writeJSONSync(pkgPath, packageJSON, { spaces: 2 }); 11 | }; 12 | -------------------------------------------------------------------------------- /src/App copy 10.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/App copy 11.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App copy 12.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 281 | 282 | 286 | 287 | 291 | -------------------------------------------------------------------------------- /src/App copy 2.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 61 | -------------------------------------------------------------------------------- /src/App copy 4.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/App copy 5.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /src/App copy 6.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 43 | -------------------------------------------------------------------------------- /src/App copy 7.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 36 | -------------------------------------------------------------------------------- /src/App copy 8.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 293 | 294 | 298 | 299 | 303 | -------------------------------------------------------------------------------- /src/App copy 9.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/App copy.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Appx.jsx: -------------------------------------------------------------------------------- 1 | // import Switch from '../dist/switch'; 2 | // import '../dist/switch/style/css'; 3 | export default { 4 | components: { 5 | // HSwitch: Switch, 6 | }, 7 | methods: { 8 | clickSwitch(v, e) { 9 | console.log(arguments); 10 | console.log(v, e); 11 | }, 12 | }, 13 | render() { 14 | const a = 0; 15 | const b = 2; 16 | return ( 17 |
18 | App.jsx 19 | xxxx 20 | {/* this.clickSwitch(x, e)}> */} 21 |
22 | ); 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /src/a.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 314 | 315 | 319 | 320 | 324 | -------------------------------------------------------------------------------- /src/assets/font/Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/src/assets/font/Medium.ttf -------------------------------------------------------------------------------- /src/assets/img/author.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/src/assets/img/author.jpg -------------------------------------------------------------------------------- /src/components/DirectiveCpt/index.jsx: -------------------------------------------------------------------------------- 1 | // import Vue from "vue"; 2 | // import auth from "../../plugins"; 3 | // Vue.use(auth); 4 | import './index.less'; 5 | 6 | const directives = [{ name: 'auth', value: 'b' }]; 7 | console.log({ ...directives }); 8 | console.log({ ...{ directives } }); 9 | console.log({ ...{ domProps: { key1: 234, xga: 342 } } }); 10 | console.log({ ...{ key1: 234, xga: 342 } }); 11 | export default { 12 | render() { 13 | return ( 14 |
15 | 我是自定义指令Cpt, 16 | v-auth自定义指令 17 | {/*
哈哈哈
*/} 18 | {/*
哈哈哈
*/} 19 | {/*
哈哈哈
*/} 20 | {/* 这个directives会被当成自定义属性。 */} 21 |
22 | 这个directives会被当成自定义属性。 23 |
24 | {/* 这个directives会vnode数据 */} 25 |
26 | 这个directives会vnode数据 27 |
28 | {/* 通过attrs设置dom的自定义属性 */} 29 |
30 | domProps11 31 |
32 |
attrsattrs
33 |
domProps22
34 | {/* 这样写key也会被vue当做是特殊attr */} 35 |
domProps333
36 | {/* 通过vnode指令数据格式1 */} 37 |
64 | 通过vnode指令数据格式1 65 |
66 | {/* v-auth="a"和v-auth={"a"}约等于就是把字符串a传给自定义指令 */} 67 |
自定义指令生效,而且显示
68 |
自定义指令生效,而且显示
69 |
自定义指令生效,但不显示
70 | {/* 下面都是将一个对象传给自定义指令的binding的value */} 71 |
72 | 自定义指令不生效1 73 |
74 |
自定义指令不生效2
75 |
age:1,name:2
76 |
77 | ); 78 | }, 79 | }; 80 | -------------------------------------------------------------------------------- /src/components/DirectiveCpt/index.less: -------------------------------------------------------------------------------- 1 | .aaa { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/JsxCpt/index.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | render() { 3 | const obj = { jsx: 23222225 }; 4 | return
我是jsxCpt{obj}
; 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/components/TsxClassCpt/index.tsx: -------------------------------------------------------------------------------- 1 | // const { hello } = require("../../utils/hello"); //node模块化 2 | 3 | export default { 4 | render(): unknown { 5 | return ( 6 |
7 | 我是tsxClassCpt 8 | {/* */} 9 | {/* this.enterClick(e)} /> */} 10 |
11 | ); 12 | }, 13 | methods: {}, 14 | // enterClick(e) { 15 | // console.log('enter!!!', e); 16 | // } 17 | }; 18 | -------------------------------------------------------------------------------- /src/components/TsxCpt/index.tsx: -------------------------------------------------------------------------------- 1 | // const { hello } = require("../../utils/hello"); //node模块化 2 | // import { hello } from '../../utils/hello'; // es6模块化 3 | // import Input from "../../../components/input/index"; 4 | 5 | // console.log(Input,2342); 6 | 7 | // hello(123); //报错:类型“number”的参数不能赋给类型“string”的参数。ts(2345) 8 | // hello('typescript'); 9 | 10 | export default { 11 | components: { 12 | // HInput:Input, 13 | }, 14 | render(): unknown { 15 | return ( 16 |
17 | 我是tsxCpt 18 | {/* */} 19 |
20 | ); 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/VueCpt/index.css: -------------------------------------------------------------------------------- 1 | .red { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/VueCpt/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/components/VueRenderCpt/index copy.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/components/VueRenderCpt/index.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/components/VueRenderCpt/wechat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/src/components/VueRenderCpt/wechat.jpg -------------------------------------------------------------------------------- /src/components/aaaa/DirectiveCpt/index.jsx: -------------------------------------------------------------------------------- 1 | // import Vue from "vue"; 2 | // import auth from "../../plugins"; 3 | // Vue.use(auth); 4 | import './style/index.less'; 5 | 6 | const directives = [{ name: 'auth', value: 'b' }]; 7 | console.log({ ...directives }); 8 | console.log({ ...{ directives } }); 9 | export default { 10 | render() { 11 | return ( 12 |
13 | 我是自定义指令Cpt, 14 | v-auth自定义指令 15 | {/*
哈哈哈
*/} 16 | {/*
哈哈哈
*/} 17 | {/*
哈哈哈
*/} 18 | {/* 这个directives会被当成自定义属性。 */} 19 |
20 | 这个directives会被当成自定义属性。 21 |
22 | {/* 这个directives会vnode数据 */} 23 |
24 | 这个directives会vnode数据 25 |
26 | {/* 通过vnode指令数据格式1 */} 27 |
54 | 通过vnode指令数据格式1 55 |
56 | {/* v-auth="a"和v-auth={"a"}约等于就是把字符串a传给自定义指令 */} 57 |
自定义指令生效,而且显示
58 |
自定义指令生效,而且显示
59 |
自定义指令生效,但不显示
60 | {/* 下面都是将一个对象传给自定义指令的binding的value */} 61 |
62 | 自定义指令不生效1 63 |
64 |
自定义指令不生效2
65 |
age:1,name:2
66 |
67 | ); 68 | }, 69 | }; 70 | -------------------------------------------------------------------------------- /src/components/aaaa/DirectiveCpt/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/aaaa/DirectiveCpt/style/index.less: -------------------------------------------------------------------------------- 1 | .aaa { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/aaaa/icon/index.jsx: -------------------------------------------------------------------------------- 1 | const icon = require('../../../assets/img/info.svg'); 2 | 3 | export default { 4 | props: { 5 | type: undefined, 6 | }, 7 | components: {}, 8 | data() { 9 | return { 10 | typeList: ['success', 'info', 'error', 'warning'], 11 | // imglist: [{ success: require('../assets/img/') }], 12 | }; 13 | }, 14 | render() { 15 | return ( 16 |
17 | {/* 判断type,如果没有设置type或者设置了type但是找不到,默认icon就是notice */} 18 | {this.typeList.indexOf(this.type) !== -1 ? ( 19 | 24 | ) : ( 25 | 30 | )} 31 |
32 | ); 33 | }, 34 | computed: {}, 35 | created() {}, 36 | mounted() {}, 37 | methods: {}, 38 | }; 39 | -------------------------------------------------------------------------------- /src/components/aaaa/input/index.tsx: -------------------------------------------------------------------------------- 1 | // import Input from "./input"; 2 | import './input.less'; 3 | 4 | console.log('我是index.ts'); 5 | export default { 6 | components: { 7 | // HInput: Input, 8 | }, 9 | render(): unknown { 10 | return
sdgdsg
; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/aaaa/input/input.less: -------------------------------------------------------------------------------- 1 | .agd { 2 | font-size: 123px; 3 | .sgg { 4 | color: red; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/components/aaaa/input/input.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | -------------------------------------------------------------------------------- /src/components/aaaa/message/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import Main from './main'; 4 | 5 | let baseConfig = { 6 | type: 'info', 7 | closeAble: false, 8 | duration: 2000, 9 | }; 10 | const MessageConstructor = Vue.extend(Main); 11 | const messageInstance = new MessageConstructor(); 12 | messageInstance.$mount(); 13 | document.body.appendChild(messageInstance.$el); 14 | 15 | const Message = function (options) { 16 | const newOptions = { ...baseConfig, ...options }; 17 | return messageInstance[newOptions.type](newOptions); 18 | }; 19 | 20 | ['success', 'warning', 'notice', 'error'].forEach((type) => { 21 | Message[type] = (options) => { 22 | options.type = type; 23 | return Message(options); 24 | }; 25 | }); 26 | 27 | Message.closeAll = function () { 28 | messageInstance.closeAll(); 29 | }; 30 | Message.config = function (options) { 31 | baseConfig = { ...baseConfig, ...options }; 32 | }; 33 | export default Message; 34 | -------------------------------------------------------------------------------- /src/components/aaaa/message/main.jsx: -------------------------------------------------------------------------------- 1 | import HssIcon from '../icon/index'; 2 | 3 | const closepng = require('../../../assets/img/close.svg'); 4 | 5 | export default { 6 | props: { 7 | icon: undefined, 8 | msg: undefined, 9 | time: { 10 | type: Number, 11 | default: undefined, 12 | }, 13 | }, 14 | components: { HssIcon }, 15 | data() { 16 | return { 17 | list: [], 18 | timerList: [], 19 | timerID: 1, 20 | bool: true, 21 | }; 22 | }, 23 | render() { 24 | return ( 25 |
26 |
27 | 28 | {this.list.map((item, index) => ( 29 |
30 |
31 |
32 |
33 | {!item.icon && } 34 | {this.renderDom('icon', item.icon)} 35 | {this.$slots.icon} 36 | {/* */} 37 |
38 | {this.renderDom('content', item.content)} 39 | {this.$slots.content} 40 | {/* */} 41 | {item.closeAble && ( 42 | this.close(item)} 47 | /> 48 | )} 49 |
50 |
51 |
52 | ))} 53 |
54 |
55 |
56 | ); 57 | }, 58 | computed: {}, 59 | created() {}, 60 | mounted() {}, 61 | methods: { 62 | transition() { 63 | // if (this.bool == false) { 64 | // this.bool = true; 65 | // } 66 | setTimeout(() => { 67 | this.bool = false; 68 | }, 100); 69 | return this.bool; 70 | }, 71 | isVNode(v) { 72 | const vnode = this.$createElement(
); 73 | const VNode = vnode.constructor; 74 | return v instanceof VNode; 75 | }, 76 | close(v) { 77 | const index = this.list.findIndex((item) => item.id === v.id); 78 | this.list.splice(index, 1); 79 | clearTimeout(this.timerList[index].timer); 80 | this.timerList.splice(index, 1); 81 | }, 82 | closeAll() { 83 | const { length } = this.list; 84 | for (let i = length - 1; i >= 0; i--) { 85 | this.close(this.list[i]); 86 | } 87 | }, 88 | handle({ type, icon, content, closeAble, duration }) { 89 | const id = this.timerID++; 90 | const temp = { 91 | id, 92 | type, 93 | icon, 94 | content, 95 | closeAble, 96 | duration, 97 | }; 98 | this.list.push(temp); 99 | const timer = setTimeout(() => { 100 | this.close(temp); 101 | }, duration); 102 | this.timerList.push({ id, timer }); 103 | }, 104 | success(v) { 105 | this.handle(v); 106 | }, 107 | warning(v) { 108 | this.handle(v); 109 | }, 110 | error(v) { 111 | this.handle(v); 112 | }, 113 | info(v) { 114 | this.handle(v); 115 | }, 116 | renderDom(slot, node) { 117 | if (this.isVNode(node)) { 118 | this.$slots[slot] = node; 119 | } else { 120 | this.$slots[slot] = {node}; 121 | } 122 | }, 123 | }, 124 | }; 125 | -------------------------------------------------------------------------------- /src/components/aaaa/message/style/index.d.ts: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/aaaa/message/style/index.less: -------------------------------------------------------------------------------- 1 | .list-enter-active { 2 | transition: all 0.5s ease; 3 | } 4 | .list-enter, .list-leave-to 5 | /* .list-leave-active for below version 2.1.8 */ { 6 | opacity: 0; 7 | transform: translateY(-60px); 8 | } 9 | .hss-message { 10 | position: fixed; 11 | top: 10px; 12 | width: 100%; 13 | text-align: center; 14 | pointer-events: none; 15 | .content { 16 | display: inline-block; 17 | padding: 10px 16px; 18 | border-radius: 4px; 19 | background: #fff; 20 | box-shadow: 0 4px 12px rgb(0 0 0 / 15%); 21 | pointer-events: auto; 22 | 23 | .close-btn { 24 | margin-left: 20px; 25 | width: 15px; 26 | height: 15px; 27 | opacity: 0.6; 28 | cursor: pointer; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/aaaa/message/style/index.ts: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/aaaa/modal/foot.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | components: {}, 3 | data() { 4 | return {}; 5 | }, 6 | computed: {}, 7 | created() {}, 8 | mounted() {}, 9 | render() { 10 | return ( 11 |
12 | 27 |
28 | ); 29 | }, 30 | methods: { 31 | cancel() { 32 | // console.log("h-foot组件里面emit cancel事件"); 33 | this.$emit('foot-cancel'); 34 | }, 35 | confirm() { 36 | // console.log("h-foot组件里面emit confirm事件"); 37 | this.$emit('foot-confirm'); 38 | }, 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /src/components/aaaa/modal/index.jsx: -------------------------------------------------------------------------------- 1 | import { Fragment as HVueFragment } from 'vue-fragment'; 2 | 3 | import HFoot from './foot'; 4 | 5 | export default { 6 | inheritAttrs: false, // 将自定义组件的attrs不显示在渲染的html元素上,防止冲突(比如title) 7 | props: { 8 | // cancelText: String, 9 | // confirmText: String, 10 | visible: { 11 | type: Boolean, 12 | default: false, 13 | }, 14 | }, 15 | components: { HFoot, HVueFragment }, 16 | data() { 17 | return { 18 | modalvisible: this.visible, 19 | }; 20 | }, 21 | watch: { 22 | visible(newVal) { 23 | this.modalvisible = newVal; 24 | this.$emit('visible-change', newVal); 25 | }, 26 | }, 27 | model: { 28 | prop: 'visible', 29 | // event: "change", 30 | event: 'input', 31 | }, 32 | render() { 33 | return ( 34 | 66 | ); 67 | }, 68 | computed: {}, 69 | created() {}, 70 | mounted() { 71 | // console.log(this.$attrs, this.visible); 72 | }, 73 | methods: { 74 | footCancel() { 75 | this.$emit('input', false); 76 | this.$emit('on-cancel'); 77 | }, 78 | footConfirm() { 79 | this.$emit('input', false); 80 | this.$emit('on-confirm'); 81 | }, 82 | close() { 83 | // console.log("点击了modal的关闭按钮,隐藏modal"); 84 | this.$emit('input', false); 85 | this.$emit('on-close'); 86 | }, 87 | // 如果未定义maskClosable或者手动设置maskClosable为true,点击遮罩都是会隐藏modal;即只有手动设置maskClosable为false才会点击遮罩层不隐藏modal 88 | maskClose() { 89 | // console.log("点击了遮罩层,判断maskClosable属性觉得是否隐藏modal"); 90 | if ( 91 | this.$attrs.maskClosable === undefined || 92 | this.$attrs.maskClosable === true 93 | ) { 94 | // console.log("判断结果:隐藏modal"); 95 | this.$emit('input', false); 96 | } else { 97 | // console.log("判断结果:不隐藏modal"); 98 | } 99 | }, 100 | }, 101 | }; 102 | -------------------------------------------------------------------------------- /src/components/aaaa/modal/style/index.d.ts: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/aaaa/modal/style/index.less: -------------------------------------------------------------------------------- 1 | .modal-mask { 2 | position: absolute; 3 | top: 0; 4 | bottom: 0; 5 | left: 0; 6 | right: 0; 7 | .modal-wrap { 8 | position: relative; 9 | left: 50%; 10 | transform: translateX(-50%); 11 | top: 30%; 12 | width: 500px; 13 | border-radius: 4px; 14 | background-color: #fff; 15 | box-shadow: 0 4px 12px rgb(0 0 0 / 15%); 16 | 17 | box-sizing: border-box; 18 | 19 | .modal-head { 20 | padding: 10px 20px; 21 | display: flex; 22 | justify-content: space-between; 23 | border-bottom: 1px solid #e8e8e8; 24 | .modal-close-btn { 25 | cursor: pointer; 26 | } 27 | } 28 | .modal-content { 29 | padding: 30px 20px; 30 | border-bottom: 1px solid #e8e8e8; 31 | } 32 | } 33 | } 34 | 35 | .modal-foot { 36 | padding: 10px 20px; 37 | text-align: right; 38 | .confirm-btn, 39 | .cancel-btn { 40 | display: inline-block; 41 | padding: 5px 15px; 42 | background-color: red; 43 | border-radius: 4px; 44 | background-color: #fff; 45 | text-align: center; 46 | background-image: none; 47 | box-shadow: 0 2px 0 rgb(0 0 0 / 2%); 48 | cursor: pointer; 49 | } 50 | .cancel-btn { 51 | border: 1px solid #d9d9d9; 52 | box-shadow: 0 2px 0 rgb(0 0 0 / 2%); 53 | } 54 | .confirm-btn { 55 | background-color: #1890ff; 56 | color: white; 57 | margin-left: 10px; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/components/aaaa/modal/style/index.ts: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/aaaa/table1/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/aaaa/table1/style/index.less: -------------------------------------------------------------------------------- 1 | .hss-table { 2 | position: relative; 3 | ::-webkit-scrollbar { 4 | // width: 8px; 5 | // height: 15px; 6 | // background: #b9b9b9; 7 | } 8 | &.border { 9 | table thead > tr > th, 10 | table tbody > tr > td { 11 | box-sizing: border-box; 12 | border-right: 1px solid #e8e8e8; 13 | } 14 | } 15 | .h-table-header { 16 | background-color: #fafafa; 17 | } 18 | .h-table-body { 19 | background-color: #fff; 20 | } 21 | table { 22 | min-width: 100%; 23 | table-layout: fixed; 24 | // width: 100%; 25 | } 26 | .hss-checkbox { 27 | position: relative; 28 | display: block; 29 | .hss-checkbox-input { 30 | position: absolute; 31 | top: 0; 32 | right: 0; 33 | bottom: 0; 34 | left: 0; 35 | z-index: 1; 36 | margin: 0; 37 | width: 18px; 38 | height: 18px; 39 | opacity: 0; 40 | cursor: pointer; 41 | &.hss-checkbox-checked { 42 | opacity: 1; 43 | } 44 | &.no-all .hss-checkbox-inner::after { 45 | opacity: 1 !important; 46 | } 47 | } 48 | &.hss-checkbox-disabled { 49 | cursor: not-allowed; 50 | 51 | .hss-checkbox-inner, 52 | .hss-checkbox-input { 53 | border-color: #d9d9d9 !important; 54 | background-color: #f5f5f5; 55 | cursor: not-allowed; 56 | } 57 | } 58 | .hss-checkbox-inner { 59 | position: relative; 60 | top: 0; 61 | left: 0; 62 | display: block; 63 | width: 16px; 64 | height: 16px; 65 | border: 1px solid #d9d9d9; 66 | border-collapse: separate; 67 | border-radius: 2px; 68 | background-color: #fff; 69 | transition: all 0.3s; 70 | &.no-all::after { 71 | opacity: 1 !important; 72 | } 73 | &::after { 74 | position: absolute; 75 | top: 50%; 76 | left: 50%; 77 | width: 8px; 78 | height: 8px; 79 | border: 0; 80 | background-color: #1890ff; 81 | content: " "; 82 | opacity: 0; 83 | transform: translate(-50%, -50%) scale(1); 84 | } 85 | } 86 | } 87 | .hss-table-selection-col { 88 | width: 60px; 89 | } 90 | .table-scroll { 91 | overflow: auto; 92 | overflow-x: hidden; 93 | width: 100%; 94 | .h-hide-scrollbar::-webkit-scrollbar { 95 | background-color: transparent; 96 | } 97 | .h-table-scroll-body { 98 | } 99 | .hss-table-tbody > tr:hover { 100 | background-color: #e9f6fe; 101 | } 102 | table { 103 | border-spacing: 0; 104 | // text-align: left; 105 | // border-radius: 4px 4px 0 0; 106 | border-collapse: separate; 107 | thead > tr > th { 108 | border-bottom: 1px solid #e8e8e8; 109 | background: #fafafa; 110 | color: rgba(0, 0, 0, 0.85); 111 | text-align: left; 112 | font-weight: 500; 113 | transition: background 0.3s ease; 114 | } 115 | tbody > tr > td { 116 | padding: 16px; 117 | border-bottom: 1px solid #e8e8e8; 118 | } 119 | } 120 | } 121 | .hss-table-tbody { 122 | td { 123 | // word-wrap: break-word; 124 | word-break: break-word; 125 | 126 | overflow-wrap: break-word; 127 | } 128 | } 129 | .ellipsis { 130 | overflow: hidden; 131 | text-overflow: ellipsis; 132 | white-space: nowrap; 133 | } 134 | .hss-table-tbody > tr.hovertr { 135 | background-color: #e9f6fe; 136 | } 137 | .hss-table-thead > tr > th { 138 | padding: 16px; 139 | 140 | overflow-wrap: break-word; 141 | } 142 | 143 | .fixed-left { 144 | position: absolute; 145 | top: 0; 146 | left: 0; 147 | z-index: 100; 148 | overflow: hidden; 149 | box-shadow: 6px 0 6px -4px rgb(0 0 0 / 15%); 150 | table { 151 | border-spacing: 0; 152 | // text-align: left; 153 | // border-radius: 4px 4px 0 0; 154 | border-collapse: separate; 155 | } 156 | .hss-table-thead { 157 | background-color: #fafafa; 158 | } 159 | // .hss-table-tbody > tr:hover { 160 | // background-color: #e9f6fe; 161 | // } 162 | & > div { 163 | // background: #fafafa; 164 | color: rgba(0, 0, 0, 0.85); 165 | font-weight: 500; 166 | transition: background 0.3s ease; 167 | 168 | overflow-wrap: break-word; 169 | } 170 | 171 | .hss-table-thead > tr > th { 172 | border-bottom: 1px solid #e8e8e8; 173 | } 174 | .hss-table-tbody > tr > td { 175 | padding: 16px; 176 | border-bottom: 1px solid #e8e8e8; 177 | } 178 | } 179 | .fixed-right { 180 | position: absolute; 181 | top: 0; 182 | right: 0; 183 | overflow: hidden; 184 | background-color: #fafafa; 185 | // margin-bottom: -17px; 186 | // overflow: scroll; 187 | box-shadow: -6px 0 6px -4px rgb(0 0 0 / 15%); 188 | table { 189 | border-spacing: 0; 190 | // text-align: left; 191 | // border-radius: 4px 4px 0 0; 192 | border-collapse: separate; 193 | } 194 | .hss-table-thead { 195 | background-color: #fafafa; 196 | } 197 | // .hss-table-tbody > tr:hover { 198 | // background-color: #e9f6fe; 199 | // } 200 | & > div { 201 | // background: #fafafa; 202 | color: rgba(0, 0, 0, 0.85); 203 | font-weight: 500; 204 | transition: background 0.3s ease; 205 | 206 | overflow-wrap: break-word; 207 | } 208 | 209 | .hss-table-thead > tr > th { 210 | border-bottom: 1px solid #e8e8e8; 211 | } 212 | .hss-table-tbody > tr > td { 213 | padding: 16px; 214 | border-bottom: 1px solid #e8e8e8; 215 | } 216 | } 217 | 218 | .scroll-bar { 219 | min-width: unset; 220 | &::-webkit-scrollbar { 221 | min-width: inherit; 222 | background-color: transparent; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/components/aaaa/tsxCpt/cpt/style/index.js: -------------------------------------------------------------------------------- 1 | console.log('sdfsf'); 2 | -------------------------------------------------------------------------------- /src/components/aaaa/tsxCpt/index.tsx: -------------------------------------------------------------------------------- 1 | import './abg.js'; 2 | 3 | export default { 4 | render(): unknown { 5 | // eslint-disable-next-line prettier/prettier 6 | return
我是tsxCpt
; 7 | }, 8 | methods: { 9 | clickHandle(): void { 10 | console.log('hi'); 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/components/checkbox/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 33 | 34 | // 35 | 在style标签src样式文件,如less,需要添加对应的lang,如果没有这个lang会报错(实测写了lang=""也不会报错,但一定要有lang)。 36 | 37 | -------------------------------------------------------------------------------- /src/components/checkbox/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/checkbox/style/index.less: -------------------------------------------------------------------------------- 1 | .hss-checkbox { 2 | position: relative; 3 | display: block; 4 | .hss-checkbox-input { 5 | margin: 0; 6 | position: absolute; 7 | top: 0; 8 | right: 0; 9 | bottom: 0; 10 | left: 0; 11 | z-index: 1; 12 | width: 100%; 13 | height: 100%; 14 | cursor: pointer; 15 | opacity: 0; 16 | &.no-all .hss-checkbox-inner::after { 17 | opacity: 1 !important; 18 | } 19 | } 20 | .hss-checkbox-inner { 21 | position: relative; 22 | top: 0; 23 | left: 0; 24 | display: block; 25 | width: 16px; 26 | height: 16px; 27 | background-color: #fff; 28 | border: 1px solid #d9d9d9; 29 | border-radius: 2px; 30 | border-collapse: separate; 31 | transition: all 0.3s; 32 | &.no-all::after { 33 | opacity: 1 !important; 34 | } 35 | &::after { 36 | position: absolute; 37 | top: 50%; 38 | left: 50%; 39 | width: 8px; 40 | height: 8px; 41 | background-color: #1890ff; 42 | border: 0; 43 | transform: translate(-50%, -50%) scale(1); 44 | opacity: 0; 45 | content: " "; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/classCpt/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | // export default { 4 | export default class classCpt extends Vue { 5 | // 翻车 6 | mounted() { 7 | console.log('我是classCpt'); 8 | console.log(this.$el); 9 | } 10 | 11 | render() { 12 | return
classCpt
; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/components/createElementCpt/index copy.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/components/createElementCpt/index.vue: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /src/components/createElementCpt/index1.vue: -------------------------------------------------------------------------------- 1 | 4 | 14 | -------------------------------------------------------------------------------- /src/components/dynamicCpt/index.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | render() { 3 | return ( 4 |
5 | d 6 |
7 | ); 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/dynamicCpt/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/dynamicCpt/style/index.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/src/components/dynamicCpt/style/index.less -------------------------------------------------------------------------------- /src/components/icon/index.jsx: -------------------------------------------------------------------------------- 1 | // const success = require('../../assets/img/success.svg'); 2 | // const error = require('../../assets/img/error.svg'); 3 | // const info = require('../../assets/img/info.svg'); 4 | // const warning = require('../../assets/img/warning.svg'); 5 | 6 | const iconType = { 7 | success, 8 | error, 9 | info, 10 | warning, 11 | }; 12 | export default { 13 | props: { 14 | type: undefined, 15 | }, 16 | components: {}, 17 | data() { 18 | return { 19 | typeList: ['success', 'info', 'error', 'warning'], 20 | // imglist: [{ success: require('../assets/img/') }], 21 | }; 22 | }, 23 | // render() { 24 | // return ( 25 | //
26 | // {/* 判断type,如果没有设置type或者设置了type但是找不到,默认icon就是info */} 27 | // {this.typeList.indexOf(this.type) !== -1 ? ( 28 | // {this.typeList.indexOf(this.type) 35 | // ) : ( 36 | // {this.typeList.indexOf(this.type) 42 | // )} 43 | //
44 | // ); 45 | // }, 46 | computed: {}, 47 | created() {}, 48 | mounted() {}, 49 | methods: {}, 50 | }; 51 | -------------------------------------------------------------------------------- /src/components/message/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import Main from './main'; 4 | 5 | let baseConfig = { 6 | type: 'info', 7 | closeAble: false, 8 | duration: 2000, 9 | }; 10 | const MessageConstructor = Vue.extend(Main); 11 | const messageInstance = new MessageConstructor(); 12 | messageInstance.$mount(); 13 | document.body.appendChild(messageInstance.$el); 14 | 15 | const Message = function (options) { 16 | const newOptions = { ...baseConfig, ...options }; 17 | if (!messageInstance[newOptions.type]) 18 | return console.error( 19 | `Message.type: '${newOptions.type}' is error,Message.type can only be one of 'success' 'warning' 'info' 'error'` 20 | ); 21 | return messageInstance[newOptions.type](newOptions); 22 | }; 23 | 24 | ['success', 'warning', 'info', 'error'].forEach((type) => { 25 | Message[type] = (options) => { 26 | options.type = type; 27 | return Message(options); 28 | }; 29 | }); 30 | 31 | Message.closeAll = function () { 32 | messageInstance.closeAll(); 33 | }; 34 | Message.config = function (options) { 35 | baseConfig = { ...baseConfig, ...options }; 36 | }; 37 | export default Message; 38 | -------------------------------------------------------------------------------- /src/components/message/main.jsx: -------------------------------------------------------------------------------- 1 | import HssIcon from '../icon/index'; 2 | 3 | const closepng = require('../../assets/img/close.svg'); 4 | 5 | export default { 6 | props: { 7 | icon: undefined, 8 | msg: undefined, 9 | time: { 10 | type: Number, 11 | default: undefined, 12 | }, 13 | }, 14 | components: { HssIcon }, 15 | data() { 16 | return { 17 | list: [], 18 | timerList: [], 19 | timerID: 1, 20 | bool: true, 21 | }; 22 | }, 23 | render() { 24 | return ( 25 |
26 |
27 | 28 | {this.list.map((item, index) => ( 29 |
30 |
31 |
32 |
33 | {!item.icon && } 34 | {this.renderDom('icon', item.icon)} 35 | {this.$slots.icon} 36 | {/* */} 37 |
38 | {this.renderDom('content', item.content)} 39 | {this.$slots.content} 40 | {/* */} 41 | {item.closeAble && ( 42 | this.close(item)} 47 | /> 48 | )} 49 |
50 |
51 |
52 | ))} 53 |
54 |
55 |
56 | ); 57 | }, 58 | computed: {}, 59 | created() {}, 60 | mounted() {}, 61 | methods: { 62 | transition() { 63 | // if (this.bool == false) { 64 | // this.bool = true; 65 | // } 66 | setTimeout(() => { 67 | this.bool = false; 68 | }, 100); 69 | return this.bool; 70 | }, 71 | isVNode(v) { 72 | const vnode = this.$createElement(
); 73 | const VNode = vnode.constructor; 74 | return v instanceof VNode; 75 | }, 76 | close(v) { 77 | const index = this.list.findIndex((item) => item.id === v.id); 78 | this.list.splice(index, 1); 79 | clearTimeout(this.timerList[index].timer); 80 | this.timerList.splice(index, 1); 81 | }, 82 | closeAll() { 83 | const { length } = this.list; 84 | for (let i = length - 1; i >= 0; i--) { 85 | this.close(this.list[i]); 86 | } 87 | }, 88 | handle({ type, icon, content, closeAble, duration }) { 89 | const id = this.timerID++; 90 | const temp = { 91 | id, 92 | type, 93 | icon, 94 | content, 95 | closeAble, 96 | duration, 97 | }; 98 | this.list.push(temp); 99 | const timer = setTimeout(() => { 100 | this.close(temp); 101 | }, duration); 102 | this.timerList.push({ id, timer }); 103 | }, 104 | success(v) { 105 | this.handle(v); 106 | }, 107 | warning(v) { 108 | this.handle(v); 109 | }, 110 | error(v) { 111 | this.handle(v); 112 | }, 113 | info(v) { 114 | this.handle(v); 115 | }, 116 | renderDom(slot, node) { 117 | if (this.isVNode(node)) { 118 | this.$slots[slot] = node; 119 | } else { 120 | this.$slots[slot] = {node}; 121 | } 122 | }, 123 | }, 124 | }; 125 | -------------------------------------------------------------------------------- /src/components/message/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/message/style/index.less: -------------------------------------------------------------------------------- 1 | .list-enter-active { 2 | transition: all 0.5s ease; 3 | } 4 | .list-enter, .list-leave-to 5 | /* .list-leave-active for below version 2.1.8 */ { 6 | opacity: 0; 7 | transform: translateY(-60px); 8 | } 9 | .hss-message { 10 | position: fixed; 11 | top: 10px; 12 | width: 100%; 13 | text-align: center; 14 | pointer-events: none; 15 | .content { 16 | display: inline-block; 17 | padding: 10px 16px; 18 | border-radius: 4px; 19 | background: #fff; 20 | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 21 | pointer-events: auto; 22 | 23 | .close-btn { 24 | margin-left: 20px; 25 | width: 15px; 26 | height: 15px; 27 | opacity: 0.6; 28 | cursor: pointer; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/components/table/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/components/table/style/index.less: -------------------------------------------------------------------------------- 1 | .hss-table { 2 | position: relative; 3 | ::-webkit-scrollbar { 4 | // width: 8px; 5 | // height: 15px; 6 | // background: #b9b9b9; 7 | } 8 | &.border { 9 | table thead > tr > th, 10 | table tbody > tr > td { 11 | box-sizing: border-box; 12 | border-right: 1px solid #e8e8e8; 13 | } 14 | } 15 | .h-table-header { 16 | background-color: #fafafa; 17 | } 18 | .h-table-body { 19 | background-color: #fff; 20 | } 21 | table { 22 | min-width: 100%; 23 | table-layout: fixed; 24 | // width: 100%; 25 | } 26 | .hss-checkbox { 27 | position: relative; 28 | display: block; 29 | .hss-checkbox-input { 30 | position: absolute; 31 | top: 0; 32 | right: 0; 33 | bottom: 0; 34 | left: 0; 35 | z-index: 1; 36 | margin: 0; 37 | width: 18px; 38 | height: 18px; 39 | opacity: 0; 40 | cursor: pointer; 41 | &.hss-checkbox-checked { 42 | opacity: 1; 43 | } 44 | &.no-all .hss-checkbox-inner::after { 45 | opacity: 1 !important; 46 | } 47 | } 48 | &.hss-checkbox-disabled { 49 | cursor: not-allowed; 50 | 51 | .hss-checkbox-inner, 52 | .hss-checkbox-input { 53 | border-color: #d9d9d9 !important; 54 | background-color: #f5f5f5; 55 | cursor: not-allowed; 56 | } 57 | } 58 | .hss-checkbox-inner { 59 | position: relative; 60 | top: 0; 61 | left: 0; 62 | display: block; 63 | width: 16px; 64 | height: 16px; 65 | border: 1px solid #d9d9d9; 66 | border-collapse: separate; 67 | border-radius: 2px; 68 | background-color: #fff; 69 | transition: all 0.3s; 70 | &.no-all::after { 71 | opacity: 1 !important; 72 | } 73 | &::after { 74 | position: absolute; 75 | top: 50%; 76 | left: 50%; 77 | width: 8px; 78 | height: 8px; 79 | border: 0; 80 | background-color: #1890ff; 81 | content: " "; 82 | opacity: 0; 83 | transform: translate(-50%, -50%) scale(1); 84 | } 85 | } 86 | } 87 | .hss-table-selection-col { 88 | width: 60px; 89 | } 90 | .table-scroll { 91 | overflow: auto; 92 | overflow-x: hidden; 93 | width: 100%; 94 | .h-hide-scrollbar::-webkit-scrollbar { 95 | background-color: transparent; 96 | } 97 | .h-table-scroll-body { 98 | } 99 | .hss-table-tbody > tr:hover { 100 | background-color: #e9f6fe; 101 | } 102 | table { 103 | border-spacing: 0; 104 | // text-align: left; 105 | // border-radius: 4px 4px 0 0; 106 | border-collapse: separate; 107 | thead > tr > th { 108 | border-bottom: 1px solid #e8e8e8; 109 | background: #fafafa; 110 | color: rgba(0, 0, 0, 0.85); 111 | text-align: left; 112 | font-weight: 500; 113 | transition: background 0.3s ease; 114 | } 115 | tbody > tr > td { 116 | padding: 16px; 117 | border-bottom: 1px solid #e8e8e8; 118 | } 119 | } 120 | } 121 | .hss-table-tbody { 122 | td { 123 | // word-wrap: break-word; 124 | word-break: break-word; 125 | 126 | overflow-wrap: break-word; 127 | } 128 | } 129 | .ellipsis { 130 | overflow: hidden; 131 | text-overflow: ellipsis; 132 | white-space: nowrap; 133 | } 134 | .hss-table-tbody > tr.hovertr { 135 | background-color: #e9f6fe; 136 | } 137 | .hss-table-thead > tr > th { 138 | padding: 16px; 139 | 140 | overflow-wrap: break-word; 141 | } 142 | 143 | .fixed-left { 144 | position: absolute; 145 | top: 0; 146 | left: 0; 147 | z-index: 100; 148 | overflow: hidden; 149 | box-shadow: 6px 0 6px -4px rgb(0 0 0 / 15%); 150 | table { 151 | border-spacing: 0; 152 | // text-align: left; 153 | // border-radius: 4px 4px 0 0; 154 | border-collapse: separate; 155 | } 156 | .hss-table-thead { 157 | background-color: #fafafa; 158 | } 159 | // .hss-table-tbody > tr:hover { 160 | // background-color: #e9f6fe; 161 | // } 162 | & > div { 163 | // background: #fafafa; 164 | color: rgba(0, 0, 0, 0.85); 165 | font-weight: 500; 166 | transition: background 0.3s ease; 167 | 168 | overflow-wrap: break-word; 169 | } 170 | 171 | .hss-table-thead > tr > th { 172 | border-bottom: 1px solid #e8e8e8; 173 | } 174 | .hss-table-tbody > tr > td { 175 | padding: 16px; 176 | border-bottom: 1px solid #e8e8e8; 177 | } 178 | } 179 | .fixed-right { 180 | position: absolute; 181 | top: 0; 182 | right: 0; 183 | overflow: hidden; 184 | background-color: #fafafa; 185 | // margin-bottom: -17px; 186 | // overflow: scroll; 187 | box-shadow: -6px 0 6px -4px rgb(0 0 0 / 15%); 188 | table { 189 | border-spacing: 0; 190 | // text-align: left; 191 | // border-radius: 4px 4px 0 0; 192 | border-collapse: separate; 193 | } 194 | .hss-table-thead { 195 | background-color: #fafafa; 196 | } 197 | // .hss-table-tbody > tr:hover { 198 | // background-color: #e9f6fe; 199 | // } 200 | & > div { 201 | // background: #fafafa; 202 | color: rgba(0, 0, 0, 0.85); 203 | font-weight: 500; 204 | transition: background 0.3s ease; 205 | 206 | overflow-wrap: break-word; 207 | } 208 | 209 | .hss-table-thead > tr > th { 210 | border-bottom: 1px solid #e8e8e8; 211 | } 212 | .hss-table-tbody > tr > td { 213 | padding: 16px; 214 | border-bottom: 1px solid #e8e8e8; 215 | } 216 | } 217 | 218 | .scroll-bar { 219 | min-width: unset; 220 | &::-webkit-scrollbar { 221 | min-width: inherit; 222 | background-color: transparent; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/components/utils/common.js: -------------------------------------------------------------------------------- 1 | export function throttle(fn, interval, option) { 2 | let lastTime = 0; 3 | let timer; 4 | option = option || {}; 5 | const trailing = option.trailing || false; 6 | return function () { 7 | const _this = this; 8 | const _arguments = arguments; 9 | const newTime = new Date().getTime(); 10 | 11 | if (timer) { 12 | clearTimeout(timer); 13 | } 14 | 15 | let result; 16 | return new Promise((resolve) => { 17 | if (newTime - lastTime > interval) { 18 | result = fn.apply(_this, _arguments); 19 | resolve(result); 20 | 21 | lastTime = newTime; 22 | } else if (trailing) { 23 | timer = setTimeout(() => { 24 | result = fn.apply(_this, _arguments); 25 | resolve(result); 26 | }, interval); 27 | } 28 | }); 29 | }; 30 | } 31 | export function debounce(fn, delay, leading) { 32 | let timer; 33 | leading = leading || false; 34 | const debounceFn = function () { 35 | if (timer) { 36 | clearTimeout(timer); 37 | } 38 | const _this = this; 39 | const _arguments = arguments; 40 | return new Promise((resolve) => { 41 | if (leading) { 42 | let isFirst = false; 43 | if (!timer) { 44 | resolve(fn.apply(_this, _arguments)); 45 | isFirst = true; 46 | } 47 | timer = setTimeout(() => { 48 | timer = null; 49 | if (!isFirst) { 50 | resolve(fn.apply(_this, _arguments)); 51 | } 52 | }, delay); 53 | } else { 54 | timer = setTimeout(() => { 55 | resolve(fn.apply(_this, _arguments)); 56 | }, delay); 57 | } 58 | }); 59 | }; 60 | 61 | debounceFn.cancel = function () { 62 | clearTimeout(timer); 63 | timer = null; 64 | }; 65 | return debounceFn; 66 | } 67 | 68 | export function getScrollBarWidth() { 69 | const inner = document.createElement('p'); 70 | inner.style.width = '100%'; 71 | inner.style.height = '200px'; 72 | 73 | const outer = document.createElement('div'); 74 | outer.style.position = 'absolute'; 75 | outer.style.top = '0px'; 76 | outer.style.left = '0px'; 77 | outer.style.visibility = 'hidden'; 78 | outer.style.width = '200px'; 79 | outer.style.height = '150px'; 80 | outer.style.overflow = 'hidden'; 81 | outer.appendChild(inner); 82 | 83 | document.body.appendChild(outer); 84 | const w1 = inner.offsetWidth; 85 | outer.style.overflow = 'scroll'; 86 | let w2 = inner.offsetWidth; 87 | if (w1 === w2) w2 = outer.clientWidth; 88 | 89 | document.body.removeChild(outer); 90 | 91 | return w1 - w2; 92 | } 93 | -------------------------------------------------------------------------------- /src/cpts/a.js: -------------------------------------------------------------------------------- 1 | console.log('aaaaaa'); 2 | -------------------------------------------------------------------------------- /src/cpts/b.js: -------------------------------------------------------------------------------- 1 | console.log('bbbbb'); 2 | -------------------------------------------------------------------------------- /src/cpts/c/cc.js: -------------------------------------------------------------------------------- 1 | console.log('ccccccc'); 2 | -------------------------------------------------------------------------------- /src/cpts/c/style/index1.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/galaxy-s10/billd-ui/f4470dfa66ba846f81e819cede76232256b0425a/src/cpts/c/style/index1.js -------------------------------------------------------------------------------- /src/cpts/dd.js: -------------------------------------------------------------------------------- 1 | console.log('ddd'); 2 | -------------------------------------------------------------------------------- /src/demo.tsx: -------------------------------------------------------------------------------- 1 | // eslint不能识别tsx的vOn:click,会报语法错误 2 | export default { 3 | render() { 4 |
5 | {/* eslint-disable-next-line prettier/prettier */} 6 |
我是tsxCpt
; 7 |
; 8 | }, 9 | methods: { 10 | clickHandle(): void { 11 | console.log('hi'); 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'webfont'; 3 | src: url('./assets/font/Medium.ttf'); 4 | } 5 | 6 | .myfont { 7 | transform: translate(10, 10); 8 | color: red; 9 | font-family: 'webfont'; 10 | transition: all 2s ease; 11 | user-select: none; 12 | } 13 | 14 | :fullscreen { 15 | } 16 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Billd from '../es'; 2 | 3 | console.log(Billd); 4 | 5 | // import { Switch } from '../es'; 6 | 7 | // console.log(Switch); 8 | 9 | // import Vue from 'vue'; 10 | 11 | // import Billd from '../es'; 12 | // import App from './App.vue'; 13 | // import '../es/style'; 14 | 15 | // Vue.use(Billd); 16 | 17 | // const vm = new Vue({ 18 | // render: (h) => h(App), 19 | // }).$mount('#app'); 20 | 21 | // console.log(vm); 22 | 23 | // import Vue from 'vue'; 24 | 25 | // import Billd from '../components/index'; 26 | // import App from './App.vue'; 27 | // import '../components/style'; 28 | 29 | // Vue.use(Billd); 30 | 31 | // const vm = new Vue({ 32 | // render: (h) => h(App), 33 | // }).$mount('#app'); 34 | 35 | // console.log(vm); 36 | -------------------------------------------------------------------------------- /src/index.less: -------------------------------------------------------------------------------- 1 | .aaa { 2 | color: red; 3 | 4 | .bbb { 5 | color: yellow; 6 | } 7 | } 8 | 9 | .ggg { 10 | font-size: 23px; 11 | transform: translate(10, 10); 12 | } -------------------------------------------------------------------------------- /src/plugins/auth/checkAuth.js: -------------------------------------------------------------------------------- 1 | export default (authData, roleRoutes) => { 2 | // console.log(authData); 3 | // console.log('!!!!!!!!!!!!!!!!'); 4 | // console.log(authData); 5 | // console.log(roleRoutes); 6 | let status = false; 7 | // console.log(typeof roleRoutes); 8 | // console.log(roleRoutes instanceof Array); 9 | // console.log(roleRoutes.__proto__ === Array.prototype); 10 | // console.log(roleRoutes.__proto__ === Array.prototype); 11 | if (typeof roleRoutes === 'string') { 12 | // 如果是children里面的authKey 13 | // 如果authData有一个权限在路由表roleRoutes外层的authKey,即显示外层路由 14 | // console.log('如果是children里面的authKey'); 15 | status = authData.includes(roleRoutes); 16 | } else if (roleRoutes instanceof Array) { 17 | // 如果不是children里面的authKey,而是路由表roleRoutes外层的authKey 18 | // console.log('如果不是children里面的authKey,而是外层的authKey'); 19 | // console.log(roleRoutes); 20 | roleRoutes.forEach((item) => { 21 | // console.log(item); 22 | // 如果authData有一个权限在外层的authKey,即显示外层路由 23 | if (authData.includes(item)) { 24 | // console.log('wwwwwwwwwwwww'); 25 | status = true; 26 | } 27 | }); 28 | } 29 | // console.log('status'); 30 | // console.log( roleRoutes,status); 31 | return status; 32 | }; 33 | -------------------------------------------------------------------------------- /src/plugins/auth/index.js: -------------------------------------------------------------------------------- 1 | import checkAuth from './checkAuth'; 2 | // import store from '@/store' 3 | 4 | export default { 5 | bind(el, binding) { 6 | console.log('自定义指令,绑定', el, binding); 7 | }, 8 | inserted(el, binding) { 9 | console.log('自定义指令,插入', el, binding); 10 | const { value } = binding; 11 | // const auths = store.state.user.auth 12 | const auths = ['a', 'b']; 13 | if (value && auths) { 14 | const isPermission = checkAuth(auths, value); 15 | if (!isPermission) { 16 | el.parentNode && el.parentNode.removeChild(el); 17 | } 18 | } else { 19 | console.log('这是不需要验证权限的路由,不移除'); 20 | // throw new Error('缺少指令权限数据') 21 | } 22 | }, 23 | update(el, binding) { 24 | console.log('自定义指令,更新', el, binding); 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /src/plugins/index.js: -------------------------------------------------------------------------------- 1 | import directiveAuth from './auth'; 2 | 3 | export default { 4 | install(Vue) { 5 | Vue.directive('auth', directiveAuth); 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/pretter.jsx: -------------------------------------------------------------------------------- 1 | export default { 2 | render() { 3 | return
我是prettier.jsx
; 4 | }, 5 | methods: { 6 | hanldeClick() { 7 | console.log('xxx'); 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /src/table/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/table/style/index.less: -------------------------------------------------------------------------------- 1 | .hss-table { 2 | position: relative; 3 | ::-webkit-scrollbar { 4 | // width: 8px; 5 | // height: 15px; 6 | // background: #b9b9b9; 7 | } 8 | &.border { 9 | table thead > tr > th, 10 | table tbody > tr > td { 11 | box-sizing: border-box; 12 | border-right: 1px solid #e8e8e8; 13 | } 14 | } 15 | .h-table-header { 16 | background-color: #fafafa; 17 | } 18 | .h-table-body { 19 | background-color: #fff; 20 | } 21 | table { 22 | min-width: 100%; 23 | table-layout: fixed; 24 | // width: 100%; 25 | } 26 | .hss-checkbox { 27 | position: relative; 28 | display: block; 29 | .hss-checkbox-input { 30 | position: absolute; 31 | top: 0; 32 | right: 0; 33 | bottom: 0; 34 | left: 0; 35 | z-index: 1; 36 | margin: 0; 37 | width: 18px; 38 | height: 18px; 39 | opacity: 0; 40 | cursor: pointer; 41 | &.hss-checkbox-checked { 42 | opacity: 1; 43 | } 44 | &.no-all .hss-checkbox-inner::after { 45 | opacity: 1 !important; 46 | } 47 | } 48 | &.hss-checkbox-disabled { 49 | cursor: not-allowed; 50 | 51 | .hss-checkbox-inner, 52 | .hss-checkbox-input { 53 | border-color: #d9d9d9 !important; 54 | background-color: #f5f5f5; 55 | cursor: not-allowed; 56 | } 57 | } 58 | .hss-checkbox-inner { 59 | position: relative; 60 | top: 0; 61 | left: 0; 62 | display: block; 63 | width: 16px; 64 | height: 16px; 65 | border: 1px solid #d9d9d9; 66 | border-collapse: separate; 67 | border-radius: 2px; 68 | background-color: #fff; 69 | transition: all 0.3s; 70 | &.no-all::after { 71 | opacity: 1 !important; 72 | } 73 | &::after { 74 | position: absolute; 75 | top: 50%; 76 | left: 50%; 77 | width: 8px; 78 | height: 8px; 79 | border: 0; 80 | background-color: #1890ff; 81 | content: ' '; 82 | opacity: 0; 83 | transform: translate(-50%, -50%) scale(1); 84 | } 85 | } 86 | } 87 | .hss-table-selection-col { 88 | width: 60px; 89 | } 90 | .table-scroll { 91 | overflow: auto; 92 | overflow-x: hidden; 93 | width: 100%; 94 | .h-hide-scrollbar::-webkit-scrollbar { 95 | background-color: transparent; 96 | } 97 | .h-table-scroll-body { 98 | } 99 | .hss-table-tbody > tr:hover { 100 | background-color: #e9f6fe; 101 | } 102 | table { 103 | border-spacing: 0; 104 | // text-align: left; 105 | // border-radius: 4px 4px 0 0; 106 | border-collapse: separate; 107 | thead > tr > th { 108 | border-bottom: 1px solid #e8e8e8; 109 | background: #fafafa; 110 | color: rgba(0, 0, 0, 0.85); 111 | text-align: left; 112 | font-weight: 500; 113 | transition: background 0.3s ease; 114 | } 115 | tbody > tr > td { 116 | padding: 16px; 117 | border-bottom: 1px solid #e8e8e8; 118 | } 119 | } 120 | } 121 | .hss-table-tbody { 122 | td { 123 | // word-wrap: break-word; 124 | word-break: break-word; 125 | 126 | overflow-wrap: break-word; 127 | } 128 | } 129 | .ellipsis { 130 | overflow: hidden; 131 | text-overflow: ellipsis; 132 | white-space: nowrap; 133 | } 134 | .hss-table-tbody > tr.hovertr { 135 | background-color: #e9f6fe; 136 | } 137 | .hss-table-thead > tr > th { 138 | padding: 16px; 139 | 140 | overflow-wrap: break-word; 141 | } 142 | 143 | .fixed-left { 144 | position: absolute; 145 | top: 0; 146 | left: 0; 147 | z-index: 100; 148 | overflow: hidden; 149 | box-shadow: 6px 0 6px -4px rgba(0, 0, 0, 0.15); 150 | table { 151 | border-spacing: 0; 152 | // text-align: left; 153 | // border-radius: 4px 4px 0 0; 154 | border-collapse: separate; 155 | } 156 | .hss-table-thead { 157 | background-color: #fafafa; 158 | } 159 | // .hss-table-tbody > tr:hover { 160 | // background-color: #e9f6fe; 161 | // } 162 | & > div { 163 | // background: #fafafa; 164 | color: rgba(0, 0, 0, 0.85); 165 | font-weight: 500; 166 | transition: background 0.3s ease; 167 | 168 | overflow-wrap: break-word; 169 | } 170 | 171 | .hss-table-thead > tr > th { 172 | border-bottom: 1px solid #e8e8e8; 173 | } 174 | .hss-table-tbody > tr > td { 175 | padding: 16px; 176 | border-bottom: 1px solid #e8e8e8; 177 | } 178 | } 179 | .fixed-right { 180 | position: absolute; 181 | top: 0; 182 | right: 0; 183 | overflow: hidden; 184 | background-color: #ffffff; 185 | // margin-bottom: -17px; 186 | // overflow: scroll; 187 | box-shadow: -6px 0 6px -4px rgba(0, 0, 0, 0.15); 188 | table { 189 | border-spacing: 0; 190 | // text-align: left; 191 | // border-radius: 4px 4px 0 0; 192 | border-collapse: separate; 193 | } 194 | .hss-table-thead { 195 | background-color: #fafafa; 196 | } 197 | // .hss-table-tbody > tr:hover { 198 | // background-color: #e9f6fe; 199 | // } 200 | & > div { 201 | // background: #fafafa; 202 | color: rgba(0, 0, 0, 0.85); 203 | font-weight: 500; 204 | transition: background 0.3s ease; 205 | 206 | overflow-wrap: break-word; 207 | } 208 | 209 | .hss-table-thead > tr > th { 210 | border-bottom: 1px solid #e8e8e8; 211 | } 212 | .hss-table-tbody > tr > td { 213 | padding: 16px; 214 | border-bottom: 1px solid #e8e8e8; 215 | } 216 | } 217 | 218 | .scroll-bar { 219 | min-width: unset; 220 | &::-webkit-scrollbar { 221 | min-width: inherit; 222 | background-color: transparent; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/table1/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /src/table1/style/index.less: -------------------------------------------------------------------------------- 1 | .billd-table { 2 | position: relative; 3 | ::-webkit-scrollbar { 4 | // width: 8px; 5 | // height: 15px; 6 | // background: #b9b9b9; 7 | } 8 | &.border { 9 | table thead > tr > th, 10 | table tbody > tr > td { 11 | box-sizing: border-box; 12 | border-right: 1px solid #e8e8e8; 13 | } 14 | } 15 | .billd-table-header { 16 | background-color: #fafafa; 17 | } 18 | .billd-table-body { 19 | background-color: #fff; 20 | } 21 | table { 22 | min-width: 100%; 23 | table-layout: fixed; 24 | // width: 100%; 25 | } 26 | .billd-checkbox { 27 | position: relative; 28 | display: block; 29 | .billd-checkbox-input { 30 | position: absolute; 31 | top: 0; 32 | right: 0; 33 | bottom: 0; 34 | left: 0; 35 | z-index: 1; 36 | margin: 0; 37 | width: 18px; 38 | height: 18px; 39 | opacity: 0; 40 | cursor: pointer; 41 | &.billd-checkbox-checked { 42 | opacity: 1; 43 | } 44 | &.no-all .billd-checkbox-inner::after { 45 | opacity: 1 !important; 46 | } 47 | } 48 | &.billd-checkbox-disabled { 49 | cursor: not-allowed; 50 | 51 | .billd-checkbox-inner, 52 | .billd-checkbox-input { 53 | border-color: #d9d9d9 !important; 54 | background-color: #f5f5f5; 55 | cursor: not-allowed; 56 | } 57 | } 58 | .billd-checkbox-inner { 59 | position: relative; 60 | top: 0; 61 | left: 0; 62 | display: block; 63 | width: 16px; 64 | height: 16px; 65 | border: 1px solid #d9d9d9; 66 | border-collapse: separate; 67 | border-radius: 2px; 68 | background-color: #fff; 69 | transition: all 0.3s; 70 | &.no-all::after { 71 | opacity: 1 !important; 72 | } 73 | &::after { 74 | position: absolute; 75 | top: 50%; 76 | left: 50%; 77 | width: 8px; 78 | height: 8px; 79 | border: 0; 80 | background-color: #1890ff; 81 | content: ' '; 82 | opacity: 0; 83 | transform: translate(-50%, -50%) scale(1); 84 | } 85 | } 86 | } 87 | .billd-table-selection-col { 88 | width: 60px; 89 | } 90 | .table-scroll { 91 | overflow: auto; 92 | overflow-x: hidden; 93 | width: 100%; 94 | .billd-hide-scrollbar::-webkit-scrollbar { 95 | background-color: transparent; 96 | } 97 | .billd-table-scroll-body { 98 | } 99 | .billd-table-tbody > tr:hover { 100 | background-color: #e9f6fe; 101 | } 102 | table { 103 | border-spacing: 0; 104 | // text-align: left; 105 | // border-radius: 4px 4px 0 0; 106 | border-collapse: separate; 107 | thead > tr > th { 108 | border-bottom: 1px solid #e8e8e8; 109 | background: #fafafa; 110 | color: rgba(0, 0, 0, 0.85); 111 | text-align: left; 112 | font-weight: 500; 113 | transition: background 0.3s ease; 114 | } 115 | tbody > tr > td { 116 | padding: 16px; 117 | border-bottom: 1px solid #e8e8e8; 118 | } 119 | } 120 | } 121 | .billd-table-tbody { 122 | td { 123 | // word-wrap: break-word; 124 | word-break: break-word; 125 | 126 | overflow-wrap: break-word; 127 | } 128 | } 129 | .ellipsis { 130 | overflow: hidden; 131 | text-overflow: ellipsis; 132 | white-space: nowrap; 133 | } 134 | .billd-table-tbody > tr.hovertr { 135 | background-color: #e9f6fe; 136 | } 137 | .billd-table-thead > tr > th { 138 | padding: 16px; 139 | 140 | overflow-wrap: break-word; 141 | } 142 | 143 | .fixed-left { 144 | position: absolute; 145 | top: 0; 146 | left: 0; 147 | z-index: 100; 148 | overflow: hidden; 149 | box-shadow: 6px 0 6px -4px rgba(0, 0, 0, 0.15); 150 | table { 151 | border-spacing: 0; 152 | // text-align: left; 153 | // border-radius: 4px 4px 0 0; 154 | border-collapse: separate; 155 | } 156 | .billd-table-thead { 157 | background-color: #fafafa; 158 | } 159 | // .billd-table-tbody > tr:hover { 160 | // background-color: #e9f6fe; 161 | // } 162 | & > div { 163 | // background: #fafafa; 164 | color: rgba(0, 0, 0, 0.85); 165 | font-weight: 500; 166 | transition: background 0.3s ease; 167 | 168 | overflow-wrap: break-word; 169 | } 170 | 171 | .billd-table-thead > tr > th { 172 | border-bottom: 1px solid #e8e8e8; 173 | } 174 | .billd-table-tbody > tr > td { 175 | padding: 16px; 176 | border-bottom: 1px solid #e8e8e8; 177 | } 178 | } 179 | .fixed-right { 180 | position: absolute; 181 | top: 0; 182 | right: 0; 183 | overflow: hidden; 184 | background-color: #ffffff; 185 | // margin-bottom: -17px; 186 | // overflow: scroll; 187 | box-shadow: -6px 0 6px -4px rgba(0, 0, 0, 0.15); 188 | table { 189 | border-spacing: 0; 190 | // text-align: left; 191 | // border-radius: 4px 4px 0 0; 192 | border-collapse: separate; 193 | } 194 | .billd-table-thead { 195 | background-color: #fafafa; 196 | } 197 | // .billd-table-tbody > tr:hover { 198 | // background-color: #e9f6fe; 199 | // } 200 | & > div { 201 | // background: #fafafa; 202 | color: rgba(0, 0, 0, 0.85); 203 | font-weight: 500; 204 | transition: background 0.3s ease; 205 | 206 | overflow-wrap: break-word; 207 | } 208 | 209 | .billd-table-thead > tr > th { 210 | border-bottom: 1px solid #e8e8e8; 211 | } 212 | .billd-table-tbody > tr > td { 213 | padding: 16px; 214 | border-bottom: 1px solid #e8e8e8; 215 | } 216 | } 217 | 218 | .scroll-bar { 219 | min-width: unset; 220 | &::-webkit-scrollbar { 221 | min-width: inherit; 222 | background-color: transparent; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/table1/utils/common.js: -------------------------------------------------------------------------------- 1 | export function throttle(fn, interval, option) { 2 | let lastTime = 0; 3 | let timer; 4 | option = option || {}; 5 | const trailing = option.trailing || false; 6 | return function () { 7 | const _this = this; 8 | const _arguments = arguments; 9 | const newTime = new Date().getTime(); 10 | 11 | if (timer) { 12 | clearTimeout(timer); 13 | } 14 | 15 | let result; 16 | return new Promise((resolve) => { 17 | if (newTime - lastTime > interval) { 18 | result = fn.apply(_this, _arguments); 19 | resolve(result); 20 | 21 | lastTime = newTime; 22 | } else if (trailing) { 23 | timer = setTimeout(() => { 24 | result = fn.apply(_this, _arguments); 25 | resolve(result); 26 | }, interval); 27 | } 28 | }); 29 | }; 30 | } 31 | export function debounce(fn, delay, leading) { 32 | let timer; 33 | leading = leading || false; 34 | const debounceFn = function () { 35 | if (timer) { 36 | clearTimeout(timer); 37 | } 38 | const _this = this; 39 | const _arguments = arguments; 40 | return new Promise((resolve) => { 41 | if (leading) { 42 | let isFirst = false; 43 | if (!timer) { 44 | resolve(fn.apply(_this, _arguments)); 45 | isFirst = true; 46 | } 47 | timer = setTimeout(() => { 48 | timer = null; 49 | if (!isFirst) { 50 | resolve(fn.apply(_this, _arguments)); 51 | } 52 | }, delay); 53 | } else { 54 | timer = setTimeout(() => { 55 | resolve(fn.apply(_this, _arguments)); 56 | }, delay); 57 | } 58 | }); 59 | }; 60 | 61 | debounceFn.cancel = function () { 62 | clearTimeout(timer); 63 | timer = null; 64 | }; 65 | return debounceFn; 66 | } 67 | 68 | export function getScrollBarWidth() { 69 | const inner = document.createElement('p'); 70 | inner.style.width = '100%'; 71 | inner.style.height = '200px'; 72 | 73 | const outer = document.createElement('div'); 74 | outer.style.position = 'absolute'; 75 | outer.style.top = '0px'; 76 | outer.style.left = '0px'; 77 | outer.style.visibility = 'hidden'; 78 | outer.style.width = '200px'; 79 | outer.style.height = '150px'; 80 | outer.style.overflow = 'hidden'; 81 | outer.appendChild(inner); 82 | 83 | document.body.appendChild(outer); 84 | const w1 = inner.offsetWidth; 85 | outer.style.overflow = 'scroll'; 86 | let w2 = inner.offsetWidth; 87 | if (w1 === w2) w2 = outer.clientWidth; 88 | 89 | document.body.removeChild(outer); 90 | 91 | return w1 - w2; 92 | } 93 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | class Demo { 2 | a = 1; 3 | 4 | aa() { 5 | console.log(this.a); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/utils/hello.ts: -------------------------------------------------------------------------------- 1 | // const hello = function(username: string) { 2 | // console.log(`Hello ${username}`); 3 | // }; 4 | 5 | // module.exports = { hello }; // node模块化 6 | 7 | // es6模块化 8 | // export const hello = function(username: string) { 9 | // console.log(`Hello ${username}`); 10 | // }; 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "jsx": "preserve", 6 | "noImplicitAny": false, 7 | "lib": ["DOM", "ESNext"], 8 | "skipLibCheck": true, 9 | "allowJs": true, 10 | "moduleResolution": "Node", 11 | "declaration": true, 12 | "allowSyntheticDefaultImports": true, 13 | "baseUrl": "./", 14 | "outDir": "./outDir" 15 | }, 16 | "include": [ 17 | "src/**/*", 18 | "build-tools/**/*", 19 | "components/**/*.js", 20 | "components/**/*.jsx", 21 | "components/**/*.ts", 22 | "components/**/*.tsx" 23 | ], 24 | "exclude": ["dist/**/*", "es/**/*", "lib/**/*", "demoDist/**/*"] 25 | } 26 | --------------------------------------------------------------------------------