├── .editorconfig
├── .eslintrc.js
├── .github
├── ISSUE_TEMPLATE.md
└── dependabot.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── appveyor.yml
├── docs-translations
├── en-US
│ └── README.md
└── zh-TW
│ └── README.md
├── gulpfile.js
├── package.json
├── project
├── _var.scss
├── demo.scss
├── logic
│ └── _logic.scss
└── widget
│ ├── _button.scss
│ ├── _dialog.scss
│ ├── _dropdownMenu.scss
│ ├── _mask.scss
│ ├── _tab.scss
│ ├── _textField.scss
│ └── _widget.scss
├── qmui.config.js
├── qmui.merge.rule.js
├── qmui
├── _function.scss
├── _qmui.scss
├── _reset.scss
└── mixin
│ ├── _adaptation.scss
│ ├── _mixin.scss
│ └── tool
│ ├── _calculate.scss
│ ├── _effect.scss
│ ├── _enhance.scss
│ └── _tool.scss
├── stylelint.config.js
├── workflow
├── Mix.js
├── TimeFormat.js
├── Util.js
├── basicTasks
│ ├── clean.js
│ ├── include.js
│ ├── list.js
│ ├── merge.js
│ ├── proxy.js
│ ├── readToolMethod.js
│ ├── reload.js
│ ├── sass.js
│ ├── server.js
│ └── version.js
├── calculateMargin.js
├── initProject.js
├── start.js
└── watch.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig is awesome: http://EditorConfig.org
2 |
3 | # top-most EditorConfig file
4 | root = true
5 |
6 | # Unix-style newlines with a newline ending every file
7 | [*]
8 | end_of_line = lf
9 | insert_final_newline = false
10 |
11 | # Matches multiple files with brace expansion notation
12 | # Set default charset
13 | charset = utf-8
14 |
15 | # Tab indentation (no size specified)
16 | indent_style = space
17 | indent_size = 4
18 |
19 | # Others
20 | trim_trailing_whitespace = true
21 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'env': {
3 | 'node': true,
4 | 'commonjs': true,
5 | 'es6': true
6 | },
7 | 'extends': 'eslint:recommended',
8 | 'parserOptions': {
9 | 'sourceType': 'module'
10 | },
11 | 'rules': {
12 | 'accessor-pairs': 'error',
13 | 'array-bracket-spacing': [
14 | 'error',
15 | 'never'
16 | ],
17 | 'array-callback-return': 'error',
18 | 'arrow-body-style': 'error',
19 | 'arrow-parens': 'error',
20 | 'arrow-spacing': 'error',
21 | 'block-scoped-var': 'error',
22 | 'block-spacing': 'error',
23 | 'brace-style': [
24 | 'error',
25 | '1tbs'
26 | ],
27 | 'callback-return': 'error',
28 | 'camelcase': 'error',
29 | 'comma-spacing': 'off',
30 | 'comma-style': [
31 | 'error',
32 | 'last'
33 | ],
34 | 'complexity': 'error',
35 | 'computed-property-spacing': [
36 | 'error',
37 | 'never'
38 | ],
39 | 'consistent-return': 'off',
40 | 'consistent-this': 'error',
41 | 'curly': 'error',
42 | 'default-case': 'error',
43 | 'dot-location': [
44 | 'error',
45 | 'property'
46 | ],
47 | 'dot-notation': 'error',
48 | 'eol-last': 'error',
49 | 'eqeqeq': 'error',
50 | 'func-names': 'off',
51 | 'func-style': 'off',
52 | 'generator-star-spacing': 'error',
53 | 'global-require': 'off',
54 | 'guard-for-in': 'off',
55 | 'handle-callback-err': 'error',
56 | 'id-blacklist': 'error',
57 | 'id-length': ['error', {'exceptions': ['_']}],
58 | 'id-match': 'error',
59 | 'indent': 'error',
60 | 'init-declarations': 'off',
61 | 'jsx-quotes': 'error',
62 | 'key-spacing': 'error',
63 | 'keyword-spacing': 'off',
64 | 'linebreak-style': [
65 | 'error',
66 | 'unix'
67 | ],
68 | 'lines-around-comment': 'off',
69 | 'max-depth': [2, 10],
70 | 'max-len': 'off',
71 | 'max-nested-callbacks': 'error',
72 | 'max-params': 'error',
73 | 'max-statements': 'off',
74 | 'max-statements-per-line': 'error',
75 | 'new-cap': 'error',
76 | 'new-parens': 'error',
77 | 'newline-after-var': 'off',
78 | 'newline-before-return': 'off',
79 | 'newline-per-chained-call': 'off',
80 | 'no-alert': 'error',
81 | 'no-array-constructor': 'error',
82 | 'no-bitwise': 'error',
83 | 'no-caller': 'error',
84 | 'no-catch-shadow': 'error',
85 | 'no-confusing-arrow': 'error',
86 | 'no-continue': 'error',
87 | 'no-div-regex': 'error',
88 | 'no-duplicate-imports': 'error',
89 | 'no-else-return': 'error',
90 | 'no-empty-function': 'error',
91 | 'no-eq-null': 'error',
92 | 'no-eval': 'error',
93 | 'no-extend-native': 'error',
94 | 'no-extra-bind': 'error',
95 | 'no-extra-label': 'error',
96 | 'no-extra-parens': 'error',
97 | 'no-floating-decimal': 'error',
98 | 'no-implicit-coercion': 'error',
99 | 'no-implicit-globals': 'error',
100 | 'no-implied-eval': 'error',
101 | 'no-inline-comments': 'off',
102 | 'no-inner-declarations': [
103 | 'error',
104 | 'functions'
105 | ],
106 | 'no-invalid-this': 'error',
107 | 'no-iterator': 'error',
108 | 'no-label-var': 'error',
109 | 'no-labels': 'error',
110 | 'no-lone-blocks': 'error',
111 | 'no-lonely-if': 'error',
112 | 'no-loop-func': 'error',
113 | 'no-magic-numbers': 'off',
114 | 'no-mixed-requires': 'off',
115 | 'no-multi-spaces': 'off',
116 | 'no-multi-str': 'error',
117 | 'no-multiple-empty-lines': 'error',
118 | 'no-native-reassign': 'error',
119 | 'no-negated-condition': 'off',
120 | 'no-nested-ternary': 'error',
121 | 'no-new': 'error',
122 | 'no-new-func': 'error',
123 | 'no-new-object': 'error',
124 | 'no-new-require': 'error',
125 | 'no-new-wrappers': 'error',
126 | 'no-octal-escape': 'error',
127 | 'no-param-reassign': 'off',
128 | 'no-path-concat': 'error',
129 | 'no-plusplus': [
130 | 'error',
131 | {
132 | 'allowForLoopAfterthoughts': true
133 | }
134 | ],
135 | 'no-process-env': 'error',
136 | 'no-process-exit': 'error',
137 | 'no-proto': 'error',
138 | 'no-restricted-globals': 'error',
139 | 'no-restricted-imports': 'error',
140 | 'no-restricted-modules': 'error',
141 | 'no-restricted-syntax': 'error',
142 | 'no-return-assign': 'error',
143 | 'no-script-url': 'error',
144 | 'no-self-compare': 'error',
145 | 'no-sequences': 'off',
146 | 'no-shadow': 'error',
147 | 'no-shadow-restricted-names': 'error',
148 | 'no-spaced-func': 'error',
149 | 'no-sync': 'off',
150 | 'no-ternary': 'off',
151 | 'no-throw-literal': 'error',
152 | 'no-trailing-spaces': 'off',
153 | 'no-undef-init': 'error',
154 | 'no-undefined': 'error',
155 | 'no-underscore-dangle': 'off',
156 | 'no-unmodified-loop-condition': 'error',
157 | 'no-unneeded-ternary': 'error',
158 | 'no-unsafe-finally': 'error',
159 | 'no-unused-expressions': 'off',
160 | 'no-unused-vars': ['error', { 'varsIgnorePattern': 'reload' }],
161 | 'no-use-before-define': 'off',
162 | 'no-useless-call': 'error',
163 | 'no-useless-computed-key': 'error',
164 | 'no-useless-concat': 'error',
165 | 'no-useless-constructor': 'error',
166 | 'no-useless-escape': 'error',
167 | 'no-var': 'off',
168 | 'no-void': 'error',
169 | 'no-warning-comments': 'off',
170 | 'no-whitespace-before-property': 'error',
171 | 'no-with': 'error',
172 | 'object-curly-spacing': [
173 | 'error',
174 | 'never'
175 | ],
176 | 'object-property-newline': 'error',
177 | 'object-shorthand': 'off',
178 | 'one-var': 'off',
179 | 'one-var-declaration-per-line': 'error',
180 | 'operator-assignment': 'error',
181 | 'operator-linebreak': 'error',
182 | 'padded-blocks': 'off',
183 | 'prefer-arrow-callback': 'off',
184 | 'prefer-const': 'error',
185 | 'prefer-reflect': [2, { exceptions: ['delete', 'apply'] }],
186 | 'prefer-rest-params': 'off',
187 | 'prefer-spread': 'off',
188 | 'prefer-template': 'off',
189 | 'quote-props': 'off',
190 | 'quotes': [
191 | 'error',
192 | 'single'
193 | ],
194 | 'radix': 'error',
195 | 'require-jsdoc': 'off',
196 | 'require-yield': 'error',
197 | 'semi': 'off',
198 | 'semi-spacing': [
199 | 'error',
200 | {
201 | 'after': true,
202 | 'before': false
203 | }
204 | ],
205 | 'sort-imports': 'error',
206 | 'sort-vars': 'off',
207 | 'space-before-blocks': 'off',
208 | 'space-before-function-paren': 'off',
209 | 'space-in-parens': [
210 | 'error',
211 | 'never'
212 | ],
213 | 'space-infix-ops': 'error',
214 | 'space-unary-ops': 'error',
215 | 'spaced-comment': [
216 | 'error',
217 | 'always'
218 | ],
219 | 'strict': 'error',
220 | 'template-curly-spacing': 'error',
221 | 'valid-jsdoc': 'error',
222 | 'vars-on-top': 'off',
223 | 'wrap-iife': 'error',
224 | 'wrap-regex': 'error',
225 | 'yield-star-spacing': 'error',
226 | 'yoda': [
227 | 'error',
228 | 'never'
229 | ]
230 | }
231 | };
232 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### 运行环境 ###
2 |
3 | - [x] 操作系统版本:`macOS (10.x)` / `Windows (7/8/10)` / `Linux` / `其他(自行填写具体系统)`
4 | - [x] QMUI Web 版本:`1.x.x`
5 | - [x] Node.js 版本:`4.x.x`
6 |
7 | ### 具体问题描述 ###
8 |
9 | #### 问题截图 ####
10 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 | *.pid.lock
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # nyc test coverage
19 | .nyc_output
20 |
21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
22 | .grunt
23 |
24 | # node-waf configuration
25 | .lock-wscript
26 |
27 | # Compiled binary addons (http://nodejs.org/api/addons.html)
28 | build/Release
29 |
30 | # Dependency directories
31 | node_modules
32 | jspm_packages
33 |
34 | # Optional npm cache directory
35 | .npm
36 |
37 | # Optional eslint cache
38 | .eslintcache
39 |
40 | # Optional REPL history
41 | .node_repl_history
42 |
43 | # Output of 'npm pack'
44 | *.tgz
45 |
46 | # Others
47 | .DS_Store
48 | .idea
49 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | install:
3 | - yarn
4 | cache:
5 | directories:
6 | - node_modules
7 | node_js:
8 | - "14"
9 | before_install:
10 | - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.15.2
11 | - export PATH="$HOME/.yarn/bin:$PATH"
12 | before_script:
13 | - cp qmui.config.js ../qmui.config.js
14 | - yarn install
15 | script:
16 | - gulp initProject
17 | - gulp list
18 | - gulp sass
19 | - gulp include
20 | - gulp clean
21 | - gulp merge
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Tencent is pleased to support the open source community by making QMUI Web available.
2 | Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
3 | If you have downloaded a copy of the QMUI Web binary from Tencent, please note that the QMUI Web binary
4 | is licensed under the MIT License.
5 | If you have downloaded a copy of the QMUI Web source code from Tencent, please note that QMUI Web source
6 | code is licensed under the MIT License. Your integration of QMUI Web into your own projects may require compliance with the MIT License.
7 | A copy of the MIT License is included in this file.
8 |
9 |
10 | Terms of the MIT License:
11 | --------------------------------------------------------------------
12 |
13 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
14 | associated documentation files (the "Software"), to deal in the Software without restriction,
15 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
16 | and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
19 |
20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 |
25 | Other dependencies and licenses:
26 |
27 | Open Source Software Licensed Under Apache -2.0:
28 | ----------------------------------------------------------------------------------------
29 | 1. browser-sync v. 2.18.12
30 | Copyright (c) 2015 Shane Osbourne
31 |
32 | Terms of the Apache -2.0 License:
33 | --------------------------------------------------------------------
34 | Licensed under the Apache License, Version 2.0 (the "License");
35 | You may not use this file except in compliance with the License.
36 | You may obtain a copy of the License at
37 |
38 | http://www.apache.org/licenses/LICENSE-2.0
39 |
40 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS,
41 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42 | See the License for the specific language governing permissions and limitations under the License.
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # QMUI Web [](https://github.com/Tencent/QMUI_Web/ "Version Number")
6 | > 一个旨在提高 UI 开发效率、快速产生项目 UI 的前端框架
7 | >
8 | > 官网:[http://qmuiteam.com/web](http://qmuiteam.com/web)
9 | >
10 | > 下载 Demo:[https://github.com/QMUI/QMUIDemo_Web/releases](https://github.com/QMUI/QMUIDemo_Web/releases)
11 |
12 | [[English]](https://github.com/Tencent/QMUI_Web/tree/master/docs-translations/en-US/README.md) / [[简体中文]](https://github.com/Tencent/QMUI_Web/blob/master/README.md) / [[繁體中文]](//github.com/Tencent/QMUI_Web/tree/master/docs-translations/zh-TW/README.md)
13 |
14 | [](https://travis-ci.org/Tencent/QMUI_Web "Build Status")
15 | [](https://ci.appveyor.com/project/kayo5994/qmui-web)
17 | [](https://github.com/QMUI "QMUI Team")
18 | [](http://opensource.org/licenses/MIT "Feel free to contribute.")
19 |
20 | QMUI Web 是一个专注 Web UI 开发,帮助开发者快速实现特定的一整套设计的框架。框架主要由一个强大的 SASS 方法合集与内置的工作流构成。通过 QMUI Web,开发者可以很轻松地提高 Web UI 开发的效率,同时保持了项目的高可维护性与稳健。如果你需要方便地控制项目的整体样式,或者需要应对频繁的界面变动,那么 QMUI Web 框架将会是你最好的解决方案。
21 |
22 | ## 功能特性
23 |
24 | ### 基础配置与组件
25 | 通过内置的公共组件和对应的 SASS 配置表,你只需修改简单的配置即可快速实现所需样式的组件。([QMUI SASS 配置表和公共组件如何帮忙开发者快速搭建项目基础 UI?](https://github.com/Tencent/QMUI_Web/wiki/Q&A#qmui-sass-%E9%85%8D%E7%BD%AE%E8%A1%A8%E5%92%8C%E5%85%AC%E5%85%B1%E7%BB%84%E4%BB%B6%E5%A6%82%E4%BD%95%E5%B8%AE%E5%BF%99%E5%BC%80%E5%8F%91%E8%80%85%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE%E5%9F%BA%E7%A1%80-ui))
26 |
27 | ### SASS 增强支持
28 | QMUI Web 包含70个 SASS mixin/function/extend,涉及布局、外观、动画、设备适配、数值计算以及 SASS 原生能力增强等多个方面,可以大幅提升开发效率。
29 |
30 | ### 完善的内置工作流
31 | QMUI Web 内置的工作流拥有从初始化项目到变更文件的各种自动化处理,包含了模板引擎,雪碧图处理,图片集中管理与自动压缩,静态资源合并、压缩与变更以及冗余文件清理等功能。
32 |
33 | ### 扩展组件
34 | QMUI Web 除了内置的公共组件外,还通过扩展的方式提供了常用的扩展组件,如等高左右双栏,文件上传按钮,树状选择菜单。
35 |
36 | ## 环境配置
37 | 请确保安装 [Node.js](https://nodejs.org/)(建议 14.0 或以上版本),并用以下命令全局安装 gulp:
38 |
39 | ```bash
40 | #安装 gulp
41 | npm install --global gulp
42 | ```
43 |
44 | ## 快速开始
45 | 推荐使用 [Yeoman](http://yeoman.io/) 脚手架 [generator-qmui](https://github.com/QMUI/generator-qmui) 安装和配置 QMUI Web。该工具可以帮助你完成 QMUI Web 的所有安装和配置。
46 |
47 | ```bash
48 | #安装 Yeoman,如果本地已安装可以忽略
49 | npm install -g yo
50 | #安装 QMUI 的模板
51 | npm install -g generator-qmui
52 | #在项目根目录执行以下命令
53 | yo qmui
54 | ```
55 |
56 |
57 | ### 完成后生成的项目目录结构
58 | ```bash
59 | 项目根目录
60 | ├─public // 静态资源目录,由 gulp 生成
61 | │ ├─js // 静态资源 js 文件
62 | │ └─style // 静态资源 UI 文件
63 | │ ├─css // 静态资源 css 文件
64 | │ └─images // 静态资源 images 文件
65 | ├─UI_dev // 实际进行开发的样式目录
66 | │ ├─project // 项目相关 SASS 与 images 文件,由 gulp 生成
67 | │ │ ├─images // 项目相关图片文件
68 | │ │ ├─logic // 项目相关逻辑样式
69 | │ │ └─widget // 项目相关公共组件样式
70 | │ └─qmui_web // QMUI Web 主源码应放置在这一层目录
71 | ├─UI_html // 静态模板目录
72 | └─UI_html_result // 静态模板 gulp 处理后的版本,用于前端拼接最终的模板
73 | ```
74 |
75 | 对于需要有更强定制性的开发者,请参考[创建新项目(高级)](http://qmuiteam.com/web/page/start.html#qui_createProject)
76 |
77 | ## 工作流任务列表
78 |
79 | ```bash
80 | #在 UI_dev/qmui_web 中执行以下命令可以查看工作流的任务列表及说明
81 | gulp list
82 | ```
83 |
84 | 也可以查看文档中的[详细说明](http://qmuiteam.com/web/page/scaffold.html)。
85 |
86 | ## 完善框架
87 | 如果有意见反馈或者功能建议,欢迎创建 [Issue](https://github.com/Tencent/QMUI_Web/issues) 或发送 [Pull Request](https://github.com/Tencent/QMUI_Web/pulls),调试与修改框架请先阅读[文档](http://qmuiteam.com/web/page/start.html#qui_frameworkImprove),感谢你的支持和贡献。
88 |
89 | 设计稿 Sketch 源文件可在 [Dribbble](https://dribbble.com/shots/2895907-QMUI-Logo) 上获取。
90 |
91 | ## QMUI Web Desktop
92 |
93 | 推荐配合使用的桌面 App:[QMUI Web Desktop](https://github.com/Tencent/QMUI_Web_desktop)。它可以管理基于 QMUI Web 进行开发的项目,通过 GUI 界面处理 QMUI Web 的服务开启/关闭,使框架的使用变得更加便捷,并提供了编译提醒,出错提醒,进程关闭提醒等额外的功能。
94 |
95 |
96 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # AppVeyor file
2 | # http://www.appveyor.com/docs/lang/nodejs-iojs
3 | # http://www.appveyor.com/docs/appveyor-yml
4 |
5 | version: "{build}"
6 |
7 | clone_depth: 10
8 |
9 | # Test against the latest version of this Node.js version
10 | environment:
11 | nodejs_version: "14"
12 |
13 | # Install scripts. (runs after repo cloning)
14 | install:
15 | # Get the latest stable version of Node.js or io.js
16 | - ps: Install-Product node $env:nodejs_version
17 | - cp qmui.config.js ../qmui.config.js
18 | - npm install -g gulp-cli
19 | # install modules
20 | - npm install
21 |
22 | # Post-install test scripts.
23 | test_script:
24 | # Output useful info for debugging.
25 | - node --version
26 | - npm --version
27 | # run tests
28 | - gulp initProject
29 | - gulp list
30 | - gulp sass
31 | - gulp include
32 | - gulp clean
33 | - gulp merge
34 |
35 | # Don't actually build.
36 | build: off
37 |
38 | cache:
39 | - C:\Users\appveyor\AppData\Roaming\npm-cache -> package.json # npm cache
40 | - node_modules -> package.json # local npm modules
--------------------------------------------------------------------------------
/docs-translations/en-US/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # QMUI Web [](https://github.com/Tencent/QMUI_Web/ "Version Number")
6 | > A front-end framework to make web UI development faster and easier.
7 | >
8 | > Official Website:[http://qmuiteam.com/web](http://qmuiteam.com/web)
9 | >
10 | > Demo:[https://github.com/QMUI/QMUIDemo_Web/releases](https://github.com/QMUI/QMUIDemo_Web/releases)
11 |
12 | [[English]](https://github.com/Tencent/QMUI_Web/tree/master/docs-translations/en-US/README.md) / [[简体中文]](https://github.com/Tencent/QMUI_Web/blob/master/README.md) / [[繁體中文]](//github.com/Tencent/QMUI_Web/tree/master/docs-translations/zh-TW/README.md)
13 |
14 | [](https://travis-ci.org/Tencent/QMUI_Web "Build Status")
15 | [](https://ci.appveyor.com/project/kayo5994/qmui-web)
17 | [](https://github.com/QMUI "QMUI Team")
18 | [](http://opensource.org/licenses/MIT "Feel free to contribute.")
19 |
20 | This framework consists of a collection of SASS methods and a built-in workflow, which can help you easily improve the efficiency, maintainability and robustness of your web UI development. Especially, when you want to adjust appearance of your website globally or deal with frequent UI design alteration, QMUI Web will be your best choice.
21 |
22 | ## Features
23 |
24 | ### Components and Configuration
25 | You can easily adjust global appearance of basic built-in components by editing a SASS config file. ([How do QMUI config file and basic components contribute to your UI development?](https://github.com/Tencent/QMUI_Web/wiki/Q&A#qmui-sass-%E9%85%8D%E7%BD%AE%E8%A1%A8%E5%92%8C%E5%85%AC%E5%85%B1%E7%BB%84%E4%BB%B6%E5%A6%82%E4%BD%95%E5%B8%AE%E5%BF%99%E5%BC%80%E5%8F%91%E8%80%85%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE%E5%9F%BA%E7%A1%80-ui))
26 |
27 | ### SASS Enhancement
28 | QMUI Web ships with more than 70 SASS Mixin, Function and Extend, dealing with layout, appearance, animation, device adaptation, math calculation and other SASS enhancement, which will make your development faster and easier.
29 | ### Automatic Processing
30 | QMUI Web has a built-in workflow and tools to automate a lot of work, including the Sprite image, html template engine, image management and compression, static resource merging and compression, and redundant file cleanup.
31 |
32 | ### Extended Component
33 | In addition to the built-in basic components, we also provide several extended components such as file upload button, tree menu, etc.
34 |
35 | ## Setup
36 | ure to install [Node.js](https://nodejs.org/) (recommend 14.0 or later) and then install Gulp globally with the following command:
37 |
38 | ```bash
39 | #Install gulp
40 | npm install --global gulp
41 | ```
42 | ## Getting Started
43 | It is recommend for you to use a [Yeoman](http://yeoman.io/) project named [generator-qmui](https://github.com/QMUI/generator-qmui) to install and configure QMUI Web.
44 |
45 | ```bash
46 | #Install Yeoman. If you have already installed, please ignore this step.
47 | npm install -g yo
48 | #Install QMUI Template
49 | npm install -g generator-qmui
50 | #Execute the following command in the root directory of the project
51 | yo qmui
52 | ```
53 |
54 |
55 | ### Project Structure Generated After Completion
56 | ```bash
57 | Root Directory
58 | ├─public // Static resource directory, generated by gulp
59 | │ ├─js // Javascript
60 | │ └─style // All UI files generate here
61 | │ ├─css // All styles files generate here
62 | │ └─images // Images generate here
63 | ├─UI_dev // Development directory
64 | │ ├─project // SASS and images files
65 | │ │ ├─images // Image source directory
66 | │ │ ├─logic // SASS file of each module
67 | │ │ └─widget // SASS file of components
68 | │ └─qmui_web // QMUI Web Source Code
69 | ├─UI_html // HTML files
70 | └─UI_html_result // HTML files compiled by gulp
71 | ```
72 |
73 | If you need more customization, please refer to [Creating Project (Advanced)](http://qmuiteam.com/web/page/start.html#qui_createProject)
74 |
75 | ## Task List in Build-In Workflow
76 |
77 | ```bash
78 | gulp list
79 | ```
80 |
81 | ## Make Contributions
82 | You can create [issues](https://github.com/Tencent/QMUI_Web/issues) or send [pull requests](https://github.com/Tencent/QMUI_Web/pulls) if you have any feedback or suggestion.
83 | Please read the [documentation](http://qmuiteam.com/web/page/start.html#qui_frameworkImprove) before debugging or modifying this framework.
84 |
85 | Thanks very much for your support and contributions.
86 |
87 | Design file (Sketch) is available on [Dribbble](https://dribbble.com/shots/2895907-QMUI-Logo).
88 |
89 | ## QMUI Web Desktop
90 |
91 | If you prefer visual interface rather than CLI, it is recommended to try an additional desktop application: [QMUI Web Desktop](https://github.com/Tencent/QMUI_Web_desktop). You can manage projects based on QMUI Web, toggle QMUI Web services, and get compilation or error message in time through it.
92 |
93 |
94 |
--------------------------------------------------------------------------------
/docs-translations/zh-TW/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # QMUI Web [](https://github.com/Tencent/QMUI_Web/ "Version Number")
6 | > 一個旨在提高 UI 開發效率、快速產生項目 UI 的前端框架
7 | >
8 | > 官網:[http://qmuiteam.com/web](http://qmuiteam.com/web)
9 | >
10 | > 下載 Demo:[https://github.com/QMUI/QMUIDemo_Web/releases](https://github.com/QMUI/QMUIDemo_Web/releases)
11 |
12 | [[English]](https://github.com/Tencent/QMUI_Web/tree/master/docs-translations/en-US/README.md) / [[简体中文]](https://github.com/Tencent/QMUI_Web/blob/master/README.md) / [[繁體中文]](//github.com/Tencent/QMUI_Web/tree/master/docs-translations/zh-TW/README.md)
13 |
14 | [](https://travis-ci.org/Tencent/QMUI_Web "Build Status")
15 | [](https://ci.appveyor.com/project/kayo5994/qmui-web)
17 | [](https://github.com/QMUI "QMUI Team")
18 | [](http://opensource.org/licenses/MIT "Feel free to contribute.")
19 |
20 | QMUI Web 是一個專註 Web UI 開發,幫助開發者快速實現特定的一整套設計的框架。框架主要由一個強大的 SASS 方法合集與內置的工作流構成。通過 QMUI Web,開發者可以很輕松地提高 Web UI 開發的效率,同時保持了項目的高可維護性與穩健。如果你需要方便地控制項目的整體樣式,或者需要應對頻繁的界面變動,那麽 QMUI Web 框架將會是你最好的解決方案。
21 |
22 | ## 功能特性
23 |
24 | ### 基礎配置與組件
25 | 通過內置的公共組件和對應的 SASS 配置表,你只需修改簡單的配置即可快速實現所需樣式的組件。([QMUI SASS 配置表和公共組件如何幫忙開發者快速搭建項目基礎 UI?](https://github.com/Tencent/QMUI_Web/wiki/Q&A#qmui-sass-%E9%85%8D%E7%BD%AE%E8%A1%A8%E5%92%8C%E5%85%AC%E5%85%B1%E7%BB%84%E4%BB%B6%E5%A6%82%E4%BD%95%E5%B8%AE%E5%BF%99%E5%BC%80%E5%8F%91%E8%80%85%E5%BF%AB%E9%80%9F%E6%90%AD%E5%BB%BA%E9%A1%B9%E7%9B%AE%E5%9F%BA%E7%A1%80-ui))
26 |
27 | ### SASS 增強與支援
28 | QMUI Web 包含70個 SASS mixin/function/extend,涉及布局、外觀、動畫、設備適配、數值計算以及 SASS 原生能力增強等多個方面,可以大幅提升開發效率。
29 |
30 | ### 腳手架(自動化任務執行工具)
31 | QMUI Web 內置的工作流擁有從初始化項目到變更文件的各種自動化處理,包含了模板引擎,雪碧圖處理,圖片集中管理與自動壓縮,靜態資源合並、壓縮與變更以及冗余文件清理等功能。
32 |
33 | ### 擴展組件
34 | QMUI Web 除了內置的公共組件外,還通過擴展的方式提供了常用的擴展組件,如等高左右雙欄,文件上傳按鈕,樹狀選擇菜單。
35 |
36 | ## 環境配置
37 | 请确保安装 [Node.js](https://nodejs.org/)(推薦 14.0 或以上版本),并用以下命令把 gulp 安装到全域环境:
38 |
39 | ```bash
40 | #安裝 gulp
41 | npm install --global gulp
42 | ```
43 | ## 快速開始
44 | 推薦使用 [Yeoman](http://yeoman.io/) 腳手架 [generator-qmui](https://github.com/QMUI/generator-qmui) 安裝和配置 QMUI Web。該工具可以幫助你完成 QMUI Web 的所有安裝和配置。
45 |
46 | ```bash
47 | #安裝 Yeoman,如果本地已安裝可以忽略
48 | npm install -g yo
49 | #安裝 QMUI 的模板
50 | npm install -g generator-qmui
51 | #在項目根目錄執行以下命令
52 | yo qmui
53 | ```
54 |
55 |
56 | ### 完成後生成的項目目錄結構
57 | ```bash
58 | 项目根目录
59 | ├─public // 靜態資源目錄,由 gulp 生成
60 | │ ├─js // 靜態資源 js 文件
61 | │ └─style // 靜態資源 UI 文件
62 | │ ├─css // 靜態資源 css 文件
63 | │ └─images // 靜態資源 images 文件
64 | ├─UI_dev // 實際進行開發的樣式目錄
65 | │ ├─project // 項目相關 SASS 與 images 文件,由 gulp 生成
66 | │ │ ├─images // 項目相關圖片文件
67 | │ │ ├─logic // 項目相關邏輯樣式
68 | │ │ └─widget // 項目相關公共組件樣式
69 | │ └─qmui_web // QMUI Web 主源碼應放置在這一層目錄
70 | ├─UI_html // 靜態模板目錄
71 | └─UI_html_result // 靜態模板 gulp 處理後的版本,用於前端拼接最終的模板
72 | ```
73 |
74 | 對於需要有更強定制性的開發者,請參考[創建新項目(進階)](http://qmuiteam.com/web/page/start.html#qui_createProject)
75 |
76 | ## 工作流任務列表
77 |
78 | ```bash
79 | #在 UI_dev/qmui_web 中執行以下命令可以查看工作流的任務列表及說明
80 | gulp list
81 | ```
82 |
83 | 也可以查看文檔中的[詳細說明](http://qmuiteam.com/web/page/scaffold.html)。
84 |
85 | ## 完善框架
86 | 如果有意見反饋或者功能建議,歡迎創建 [Issue](https://github.com/Tencent/QMUI_Web/issues) 或發送 [Pull Request](https://github.com/Tencent/QMUI_Web/pulls),調試與修改框架請先閱讀[文檔](http://qmuiteam.com/web/page/start.html#qui_frameworkImprove),感謝你的支持和貢獻。
87 |
88 | 設計稿 Sketch 源文件可在 [Dribbble](https://dribbble.com/shots/2895907-QMUI-Logo) 上獲取。
89 |
90 | ## QMUI Web Desktop
91 |
92 | 推薦配合使用的桌機應用程式:[QMUI Web Desktop](https://github.com/Tencent/QMUI_Web_desktop)。它可以管理基於 QMUI Web 進行開發的項目,通過 GUI 界面處理 QMUI Web 的服務開啟/關閉,使框架的使用變得更加便捷,並提供了編譯提醒,出錯提醒,進程關閉提醒等額外的功能。
93 |
94 |
95 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // gulpfile.js QMUI Web Gulp 工作流
17 | const gulp = require('gulp');
18 | const fs = require('fs');
19 | const mix = new (require('./workflow/Mix.js'))();
20 |
21 | // 载入基础任务
22 | const basicTaskPath = 'workflow/basicTasks';
23 | const combinedTaskPath = 'workflow';
24 |
25 | const basicTaskPathFilterCallback = (file) => file.match(/js$/); // 排除非 JS 文件,如 Vim 临时文件
26 |
27 | fs.readdirSync(basicTaskPath).filter(basicTaskPathFilterCallback).sort().forEach((file) => {
28 | require('./' + basicTaskPath + '/' + file)(gulp, mix);
29 | });
30 |
31 | // 载入复合任务
32 |
33 | // 载入 watch 任务
34 | require('./' + combinedTaskPath + '/watch')(gulp, mix);
35 |
36 | // 载入自定义任务
37 | if (mix.config.customTasks) {
38 | Object.keys(mix.config.customTasks).forEach((customTaskName) => {
39 | require('./' + mix.config.customTasks[customTaskName])(gulp, mix);
40 | });
41 | }
42 |
43 | // 载入 start 和 initProject 任务
44 | ['start', 'initProject'].forEach((file) => {
45 | require('./' + combinedTaskPath + '/' + file)(gulp, mix);
46 | });
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qmui_web",
3 | "version": "3.3.0",
4 | "description": "一个旨在提高 UI 开发效率、快速产生项目 UI 的前端框架",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/Tencent/QMUI_Web.git"
8 | },
9 | "main": "gulpfile.js",
10 | "dependencies": {
11 | "ansi-colors": "^4.1.3",
12 | "autoprefixer": "^9.8.6",
13 | "beeper": "^2.1.0",
14 | "browser-sync": "^2.27.11",
15 | "color-support": "^1.1.3",
16 | "del": "^5.1.0",
17 | "fancy-log": "^2.0.0",
18 | "gulp": "^4.0.2",
19 | "gulp-better-sass-inheritance": "^0.0.3",
20 | "gulp-clean-css": "^4.3.0",
21 | "gulp-concat": "^2.6.1",
22 | "gulp-dart-sass": "^1.0.2",
23 | "gulp-debug": "^4.0.0",
24 | "gulp-file-include": "^2.3.0",
25 | "gulp-file-sync": "^2.1.0",
26 | "gulp-htmlmin": "^5.0.1",
27 | "gulp-if": "^3.0.0",
28 | "gulp-imagemin": "^8.0.0",
29 | "gulp-load-plugins": "^2.0.8",
30 | "gulp-plumber": "^1.2.1",
31 | "gulp-postcss": "^9.0.1",
32 | "gulp-rename": "^2.0.0",
33 | "gulp-replace": "^1.1.4",
34 | "gulp-sourcemaps": "^3.0.0",
35 | "gulp-uglify": "^3.0.2",
36 | "imagemin-pngquant": "^9.0.2",
37 | "js-md5": "^0.7.3",
38 | "lodash": "^4.17.21",
39 | "lodash.template": "^4.5.0",
40 | "minimatch": "^5.1.0",
41 | "mkdirp": "^1.0.4",
42 | "path": "^0.12.7",
43 | "postcss-lazysprite": "^1.8.2",
44 | "postcss-svg-sprite": "^1.0.6",
45 | "sass": "^1.57.1",
46 | "set-value": "^4.1.0",
47 | "static-eval": "^2.1.0",
48 | "through2": "^4.0.2",
49 | "yargs": "^17.6.2"
50 | },
51 | "scripts": {
52 | "lint": "./node_modules/.bin/eslint ."
53 | },
54 | "devDependencies": {
55 | "eslint": "^8.31.0",
56 | "sassdoc": "^2.7.4",
57 | "stylelint-wechat-work-css": "^0.5.0"
58 | },
59 | "keywords": [
60 | "QMUI"
61 | ],
62 | "author": "QMUI Team",
63 | "license": "MIT",
64 | "bugs": {
65 | "url": "https://github.com/Tencent/QMUI_Web/issues"
66 | },
67 | "homepage": "http://qmuiteam.com/web/"
68 | }
69 |
--------------------------------------------------------------------------------
/project/_var.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * var.scss 变量
4 | * @author Kayo&Clearwu
5 | * @date 2014-11-14
6 | *
7 | * --- function ----
8 | * #function QMUI 功能相关
9 | *
10 | * --- common ----
11 | * #common 通用
12 |
13 | * --- component ----
14 | * #button 按钮组件
15 | * #dialog 对话框组件
16 | * #mask 遮罩层组件
17 | * #dropdownMenu 下拉菜单组件
18 | * #tab 选项卡组件
19 | * #inputText、#textarea 文本输入组件
20 | *
21 | */
22 | // 计算的工具方法
23 | @import "../qmui/mixin/tool/_calculate";
24 |
25 | // #common 通用
26 | $common_fontFamily: "Helvetica Neue", Helvetica, Verdana, san-serif;
27 | $common_fontSize: 14px;
28 | $common_body_background: #FFF;
29 | $common_body_color: #000;
30 | $common_color_link: #2685D2;
31 | $common_color_linkTapHighlight: rgba(105, 186, 255, .13);
32 | $common_color_separator: #CCCCCE;
33 | $common_color_border: $common_color_separator;
34 | $common_color_placeholder: #ADB4BE;
35 | $common_color_placeholderFocus: $common_color_placeholder;
36 | $common_zIndex_1: 10;
37 | $common_zIndex_2: 20;
38 | $common_zIndex_3: 30;
39 | $common_zIndex_4: 40;
40 | $common_zIndex_5: 50;
41 | $common_zIndex_6: 60;
42 | $common_zIndex_7: 70;
43 | $common_zIndex_8: 80;
44 | $common_zIndex_9: 90;
45 | $common_zIndex_10: 100;
46 |
47 | // #button 按钮组件
48 | $btn_minWidth: 46px;
49 | $btn_height: 30px;
50 | $btn_lineHeight: $btn_height;
51 | $btn_padding: 0 12px;
52 | $btn_background: #04C9E8;
53 | $btn_border: 1px solid transparent;
54 | $btn_fontSize: 13px;
55 | $btn_color: #FFF;
56 |
57 | // #dialog 对话框组件
58 | $dialog_zIndex: $common_zIndex_10;
59 | $dialog_minWidth: 420px; // 对话框的最小宽度
60 | $dialog_background: #fff;
61 | $dialog_border: none;
62 | $dialog_radius: 5px;
63 |
64 | $dialog_head_background: #00C0E1;
65 | $dialog_head_borderBottom: none; // 对话框头部的下边线
66 |
67 | $dialog_title_height: 40px;
68 | $dialog_title_fontSize: 14px;
69 | $dialog_title_fontWeight: bold;
70 | $dialog_title_color: #FFF;
71 | $dialog_title_lineHeight: $dialog_title_height;
72 |
73 | $dialog_close_top: 5px;
74 | $dialog_close_right: 6px;
75 |
76 | $dialog_body_background: #fff;
77 |
78 | $dialog_foot_background: $dialog_background; // 对话框底部的背景,默认与对话框的背景一样
79 | $dialog_foot_borderTop: $dialog_head_borderBottom; // 对话框底部的上边线,样式默认与对话框边线一样
80 |
81 | // #mask 遮罩层组件
82 | $mask_zIndex: $common_zIndex_9;
83 | $maskWrap_zIndex: $mask_zIndex;
84 | $mask_background: #000;
85 | $mask_opacity: .5;
86 |
87 | // #dropdownMenu 下拉菜单组件
88 | $dropdownMenu_zIndex: $common_zIndex_3;
89 | $dropdownMenu_background: #fff;
90 | $dropdownMenu_border: 1px solid $common_color_border;
91 | $dropdownMenu_ulPadding: 12px 0;
92 | $dropdownMenu_itemLink_height: 32px;
93 | $dropdownMenu_itemLink_lineHeight: $dropdownMenu_itemLink_height;
94 | $dropdownMenu_itemLink_padding: 0 15px;
95 | $dropdownMenu_itemLink_color: #000;
96 | $dropdownMenu_split_margin: 5px 0;
97 | $dropdownMenu_split_borderTop: 1px solid #ccc;
98 |
99 | // #tab 选项卡组件
100 | $tab_background: #fff;
101 | $tab_head_background: $tab_background;
102 | $tabNav_padding: 0 16px;
103 | $tabNav_item_margin: 0 24px -1px 0;
104 | $tabNav_itemLink_height: 43px;
105 | $tabNav_itemLink_lineHeight: $tabNav_itemLink_height;
106 | $tabNav_itemLink_padding: 0;
107 | $tabNav_itemLink_background: transparent;
108 | $tabNav_itemLink_color: #858C96;
109 |
110 | // #inputText、#textarea 文本输入组件
111 | $textField_width: 270px;
112 | $textField_lineHeight: 22px;
113 | $textField_padding: 12px 16px;
114 | $textField_background: #fff;
115 | $textField_border: 1px solid $common_color_border;
116 | $textField_borderRadius: 5px;
117 |
118 | $inputText_height: $textField_lineHeight;
119 |
120 | $textarea_height: $textField_lineHeight * 3;
121 |
--------------------------------------------------------------------------------
/project/demo.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | /**
4 | * main.scss 项目总样式文件
5 | * @author Kayo
6 | * @date 2014-10-31
7 | *
8 | */
9 |
10 | // 业务公共变量
11 | @import "_var";
12 |
13 | // 引入 QMUI
14 | @import "../qmui/_qmui";
15 |
16 | // 业务公共组件
17 | @import "widget/_widget";
18 |
19 | // 业务逻辑代码
20 | @import "logic/_logic";
21 |
--------------------------------------------------------------------------------
/project/logic/_logic.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * _logic.scss 业务逻辑代码
4 | * @author Kayo
5 | * @date 2014-10-31
6 | *
7 | */
8 |
--------------------------------------------------------------------------------
/project/widget/_button.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * _button.scss 按钮组件
4 | * @author Kayo
5 | * @date 2014-11-04
6 | *
7 | * .qui_btn
8 | * + .qui_btn_Ghost
9 | *
10 | */
11 |
12 | // .qui_btn
13 | .qui_btn {
14 | display: inline-block;
15 | margin: 0; // input = button有默认 margin 值,这里需要重置
16 | box-sizing: content-box;
17 | min-width: $btn_minWidth;
18 | height: $btn_height;
19 | padding: $btn_padding;
20 | background: $btn_background;
21 | border: $btn_border;
22 | // line-height 的偏移值依赖于不同字体
23 | line-height: $btn_lineHeight + 2;
24 | line-height: $btn_lineHeight + 1 \9\0;
25 | outline: none;
26 | cursor: pointer;
27 | text-align: center;
28 | font-size: $btn_fontSize;
29 | color: $btn_color;
30 | user-select: none;
31 | border-radius: 2px;
32 |
33 | &:hover {
34 | background-color: #05D7F7;
35 | }
36 | &:active,
37 | &_Active {
38 | background-color: #04B1CC;
39 | }
40 | &[Disabled] {
41 | opacity: .5;
42 | }
43 | }
44 |
45 | a.qui_btn {
46 | text-decoration: none;
47 |
48 | &:hover {
49 | text-decoration: none;
50 | }
51 | }
52 |
53 | .a[title = "1"] {
54 |
55 | }
56 |
57 | .qui_btn_Ghost {
58 | background-color: transparent;
59 | border: 1px solid #04C9E8;
60 | color: #04C9E8;
61 |
62 | &:hover {
63 | background-color: transparent;
64 | border-color: #05D7F7;
65 | color: #05D7F7;
66 | }
67 | &:active,
68 | &_Active {
69 | background-color: transparent;
70 | border-color: #04B1CC;
71 | color: #04B1CC;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/project/widget/_dialog.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * _dialog.scss 对话框组件
4 | * @author Kayo
5 | * @date 2014-11-03
6 | *
7 | * .qui_dialog
8 | * > .qui_dialog_head 对话框的头部
9 | * > .qui_dialog_title 对话框的标题
10 | * > [.qui_dialog_close] 可选。对话框的关闭按钮
11 | * > .qui_dialog_cnt 对话框的内容区域
12 | * > [.qui_dialog_foot] 可选。对话框的底部
13 | */
14 |
15 | // .qui_dialog
16 | .qui_dialog {
17 | position: absolute;
18 | z-index: $dialog_zIndex;
19 | min-width: $dialog_minWidth;
20 | background: $dialog_background;
21 | border: $dialog_border;
22 | border-radius: $dialog_radius;
23 | box-shadow: 0 2px 20px 0 rgba(0, 0, 0, .15);
24 | }
25 |
26 | .qui_dialog_head {
27 | padding: 0 12px;
28 | background: $dialog_head_background;
29 | border-bottom: $dialog_head_borderBottom;
30 | border-radius: $dialog_radius $dialog_radius 0 0;
31 | }
32 |
33 | .qui_dialog_title {
34 | height: $dialog_title_height;
35 | line-height: $dialog_title_lineHeight;
36 | font-size: $dialog_title_fontSize;
37 | font-weight: $dialog_title_fontWeight;
38 | color: $dialog_title_color;
39 | }
40 |
41 | .qui_dialog_close {
42 | position: absolute;
43 | top: $dialog_close_top;
44 | right: $dialog_close_right;
45 | padding: 5px; // 把点击区域做大
46 | line-height: 1;
47 | font-size: 18px;
48 | color: #fff;
49 |
50 | &:hover {
51 | color: #d6d9de;
52 | text-decoration: none;
53 | }
54 | &:active {
55 | color: #eaecee;
56 | }
57 | }
58 |
59 | .qui_dialog_body {
60 | padding: 23px 30px 30px 37px;
61 | background: $dialog_body_background;
62 | font-size: 14px;
63 | color: #353C46;
64 | }
65 |
66 | .qui_dialog_foot {
67 | padding: 17px 12px;
68 | background: $dialog_foot_background;
69 | border-top: $dialog_foot_borderTop;
70 | border-radius: 0 0 $dialog_radius $dialog_radius;
71 | text-align: right;
72 | line-height: 25px;
73 | }
74 |
75 | .qui_dialog_foot .qui_btn {
76 | margin-left: 12px;
77 | }
78 |
--------------------------------------------------------------------------------
/project/widget/_dropdownMenu.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * _dropdownMenu.scss 下拉菜单组件
4 | * @author clearwu
5 | * @date 2014-11-11
6 | *
7 | * .qui_dropdownMenu
8 | *
9 | */
10 |
11 | // .qui_dropdownMenu
12 | .qui_dropdownMenu {
13 | position: absolute;
14 | z-index: $dropdownMenu_zIndex;
15 | min-width: 206px;
16 | background: $dropdownMenu_background;
17 | border: $dropdownMenu_border;
18 | box-shadow: 0 1px 2px rgba(0, 0, 0, .2);
19 | border-radius: 4px;
20 | }
21 |
22 | .qui_dropdownMenu ul {
23 | padding: $dropdownMenu_ulPadding;
24 | }
25 |
26 | .qui_dropdownMenu_itemLink {
27 | display: block;
28 | height: $dropdownMenu_itemLink_height;
29 | line-height: $dropdownMenu_itemLink_lineHeight;
30 | padding: $dropdownMenu_itemLink_padding;
31 | color: $dropdownMenu_itemLink_color;
32 |
33 | &:hover {
34 | background-color: #F5F5F5;
35 | text-decoration: none;
36 | }
37 | &:active {
38 | background-color: #EBEBEB;
39 | }
40 | }
41 |
42 | .qui_dropdownMenu_split {
43 | display: block;
44 | height: 0;
45 | line-height: 0;
46 | font-size: 0;
47 | margin: $dropdownMenu_split_margin;
48 | border-top: $dropdownMenu_split_borderTop;
49 | }
50 |
--------------------------------------------------------------------------------
/project/widget/_mask.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * _mask.scss 遮罩层组件
4 | * @author Kayo
5 | * @date 2014-11-11
6 | *
7 | * .qui_maskWrap 遮罩层的包裹容器,包裹遮罩层以及需要弹出的内容(如对话框)
8 | * > .qui_mask
9 | */
10 |
11 | // 遮罩层的包裹容器,包裹遮罩层以及需要弹出的内容(如对话框)
12 | .qui_maskWrap {
13 | position: relative;
14 | z-index: $maskWrap_zIndex;
15 | }
16 |
17 | .qui_mask {
18 | position: fixed;
19 | top: 0;
20 | right: 0;
21 | bottom: 0;
22 | left: 0;
23 | z-index: $mask_zIndex;
24 | background: $mask_background;
25 | opacity: $mask_opacity;
26 | filter: alpha(opacity=$mask_opacity * 100);
27 | }
28 |
--------------------------------------------------------------------------------
/project/widget/_tab.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * _tab.scss 选项卡组件
4 | * @author clearwu
5 | * @date 2014-11-12
6 | *
7 | * .qui_tab
8 | * > .qui_tab_title
9 | * > .qui_tabNav
10 | * > .qui_tabNav_item
11 | * > .qui_tabNav_itemLink
12 | * > .qui_tab_cnt
13 | *
14 | */
15 |
16 | // .qui_tab
17 | .qui_tab {
18 | background: $tab_background;
19 | border: 1px solid #DEE0E2;
20 | }
21 |
22 | .qui_tab_title {
23 | background: $tab_head_background;
24 | }
25 |
26 | .qui_tabNav {
27 | @include clear;
28 | padding: $tabNav_padding;
29 | border-bottom: 1px solid #DEE0E2;
30 | }
31 |
32 | .qui_tabNav_item {
33 | float: left;
34 | margin: $tabNav_item_margin;
35 | border-bottom: 3px solid transparent;
36 | font-size: 16px;
37 |
38 | &_Curr {
39 | border-color: #04C9E8;
40 |
41 | .qui_tabNav_itemLink {
42 | color: #04C9E8;
43 | }
44 | }
45 | }
46 |
47 | .qui_tabNav_itemLink {
48 | display: block;
49 | height: $tabNav_itemLink_height;
50 | line-height: $tabNav_itemLink_lineHeight;
51 | padding: $tabNav_itemLink_padding;
52 | background: $tabNav_itemLink_background;
53 | color: $tabNav_itemLink_color;
54 |
55 | &:hover {
56 | color: #04C9E8;
57 | text-decoration: none;
58 |
59 | }
60 | }
61 |
62 | .qui_tab_cnt {
63 | min-height: 100px;
64 | padding: 16px 24px;
65 | }
66 |
--------------------------------------------------------------------------------
/project/widget/_textField.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | /**
3 | * _textField.scss 文本输入组件
4 | * @author clearwu
5 | * @date 2014-11-13
6 | *
7 | * .qui_inputText
8 | * .qui_textarea
9 | *
10 | */
11 |
12 | // placeholder 颜色重置
13 | // Webkit
14 | input::-webkit-input-placeholder,
15 | textarea::-webkit-input-placeholder {
16 | color: $common_color_placeholder;
17 | }
18 |
19 | input:focus::-webkit-input-placeholder,
20 | textarea:focus::-webkit-input-placeholder {
21 | color: $common_color_placeholderFocus;
22 | }
23 |
24 | // Firefox < 19
25 | input:-moz-placeholder,
26 | textarea:-moz-placeholder {
27 | color: $common_color_placeholder;
28 | }
29 |
30 | input:focus:-moz-placeholder,
31 | textarea:focus:-moz-placeholder {
32 | color: $common_color_placeholderFocus;
33 | }
34 |
35 | // Firefox > 19
36 | input::-moz-placeholder,
37 | textarea::-moz-placeholder {
38 | color: $common_color_placeholder;
39 | }
40 |
41 | input:focus::-moz-placeholder,
42 | textarea:focus::-moz-placeholder {
43 | color: $common_color_placeholderFocus;
44 | }
45 |
46 | // IE10
47 | input:-ms-input-placeholder,
48 | textarea:-ms-input-placeholder {
49 | color: $common_color_placeholder;
50 | }
51 |
52 | input:focus:-ms-input-placeholder,
53 | textarea:focus:-ms-input-placeholder {
54 | color: $common_color_placeholder;
55 | }
56 |
57 | %textField {
58 | display: block;
59 | width: $textField_width;
60 | line-height: $textField_lineHeight;
61 | padding: $textField_padding;
62 | background: $textField_background;
63 | border: $textField_border;
64 | border-radius: $textField_borderRadius;
65 | box-sizing: content-box;
66 | }
67 |
68 | .qui_inputText,
69 | .qui_textarea {
70 | font-size: 15px;
71 | color: #353C46;
72 | }
73 |
74 | // .qui_inputText
75 | .qui_inputText {
76 | @extend %textField;
77 | height: $inputText_height;
78 | &::-ms-clear {
79 | display: none; // 去除 IE10 中的输入框清除按钮效果
80 | }
81 | }
82 |
83 | // .qui_textarea
84 | .qui_textarea {
85 | @extend %textField;
86 | height: $textarea_height;
87 | }
88 |
--------------------------------------------------------------------------------
/project/widget/_widget.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /**
3 | * _widget.scss 业务公共组件
4 | * @author Kayo
5 | * @date 2014-10-31
6 | *
7 | */
8 |
9 |
10 | // 按钮
11 | @import "_button";
12 |
13 | // 对话框
14 | @import "_dialog";
15 |
16 | // 遮罩层
17 | @import "_mask";
18 |
19 | // 下拉菜单
20 | @import "_dropdownMenu";
21 |
22 | // 选项卡
23 | @import "_tab";
24 |
25 | // 文本输入
26 | @import "_textField";
27 |
--------------------------------------------------------------------------------
/qmui.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | /**
3 | * 项目相关部分代码,复制后应首先进行这些配置
4 | *
5 | */
6 | 'project': 'Demo',
7 | 'prefix': 'dm',
8 | 'resultCssFileName': 'main.scss',
9 | 'cleanFileType': ['../.sass-cache', '../.sass-cache/**/*'],
10 | 'needsSourceMaps': false,
11 | 'needsImagesMinAndSync': true,
12 |
13 | /**
14 | * 项目的路径配置,建议尽量使用推荐的路径,若要修改,请保持与 config.rb 中的指向的目录保持一致,但需要注意因为相对位置不同(这里是以 qmui_web 目录为 Base Path),所以这里的值应该比 config.rb 中的多了一个 ../
15 | *
16 | */
17 | 'paths': {
18 | 'htmlSourcePath': ['../../UI_html/**/*.html'],
19 | 'imagesSourcePath': '../project/images',
20 | 'htmlResultPath': '../../UI_html_result',
21 | 'imagesResultPath': '../../public/style/images',
22 | 'independentImagesDirectory': '/independent',
23 | 'styleResultPath': '../../public/style/css'
24 | },
25 |
26 | /**
27 | * BrowerSync 设置
28 | *
29 | */
30 | 'browserSync': {
31 | // browserSync 的模式,本地模式、代理模式或者关闭(server/proxy/close)
32 | 'browserSyncMod': 'server',
33 | // 自定义端口
34 | 'browserSyncPort': 3030,
35 | // 是否显示 BrowserSync 的日志
36 | 'browserSyncShowLog': false,
37 | // server 开启后的默认路径
38 | 'browserSyncStartPath': '/web',
39 | 'browserSyncHost': '',
40 | 'browserSyncWatchPath': ['../../UI_html_result/*.html', '../../public/**/*'],
41 | // 自定义路由,server 模式下方可产生作用
42 | 'browserSyncServerRoute': {
43 | '/public': '../../public',
44 | '/web': '../../UI_html_result'
45 | },
46 | // 自定义代理源地址,proxy 模式下方可产生作用
47 | 'browserSyncProxy': ''
48 | },
49 |
50 | /**
51 | * 模板 include 引擎
52 | *
53 | */
54 | 'template': {
55 | 'openIncludeFunction': true,
56 | 'includePrefix': '@@'
57 | }
58 | };
59 |
--------------------------------------------------------------------------------
/qmui.merge.rule.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'comment': '以下是示例内容,使用时需要修改为项目实际需要的内容并必须删除本段注释',
3 | '../public/js/all.js': ['../public/js/prettify.js', '../public/js/lang-css.js', '../public/js/main.js'],
4 | '../public/style/css/main.css': ['../public/style/css/logic.css', '../public/style/css/widget.css']
5 | };
6 |
--------------------------------------------------------------------------------
/qmui/_function.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /**
3 | * _function.scss
4 | * @author Kayo
5 | * @date 2014-11-17
6 | */
7 |
8 | .qui_txtNormal {
9 | font-weight: normal !important;
10 | }
11 |
12 | .qui_txtBold {
13 | font-weight: bold !important;
14 | }
15 |
16 | .qui_txtLeft {
17 | text-align: left !important;
18 | }
19 |
20 | .qui_txtRight {
21 | text-align: right !important;
22 | }
23 |
24 | .qui_txtUnderline {
25 | text-decoration: underline !important;
26 | }
27 |
28 | .qui_txtUnderlineNone {
29 | text-decoration: none !important;
30 | }
31 |
32 | .qui_txtOverflow {
33 | @include text-ellipsis;
34 | }
35 |
36 | .qui_clear {
37 | @include clear;
38 | }
39 |
40 | .qui_txtNowrap {
41 | white-space: nowrap !important;
42 | }
43 |
44 | .qui_layoutLeft {
45 | float: left !important;
46 | }
47 |
48 | .qui_layoutRight {
49 | float: right !important;
50 | }
51 |
52 | .qui_displayNone {
53 | display: none !important;
54 | }
55 |
--------------------------------------------------------------------------------
/qmui/_qmui.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | @charset "utf-8";
17 | /**
18 | * _qmui.scss QMUI 总样式文件
19 | * @author Kayo
20 | * @date 2014-10-31
21 | *
22 | */
23 |
24 |
25 | // 常用 mixin 封装
26 | @import "mixin/_mixin";
27 |
28 | // CSS Reset
29 | @import "_reset";
30 |
31 | // 常用样式(原子类)
32 | @import "_function";
33 |
--------------------------------------------------------------------------------
/qmui/_reset.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /**
3 | * _reset.scss
4 | * @author Kayo
5 | * @date 2014-10-30
6 | *
7 | * #reset
8 | * #html5
9 | */
10 |
11 | /* #reset */
12 | body, dl, dd, h1, h2, h3, h4, h5, h6, p, pre, form, fieldset, legend {
13 | margin: 0;
14 | }
15 |
16 | ul, ol, fieldset {
17 | margin: 0;
18 | padding: 0;
19 | }
20 |
21 | th, td {
22 | padding: 0;
23 | }
24 |
25 | table {
26 | font-size: inherit;
27 | }
28 |
29 | fieldset, img {
30 | border: 0;
31 | }
32 |
33 | ul, ol, li {
34 | list-style: none;
35 | }
36 |
37 | body {
38 | font-size: $common_fontSize;
39 | line-height: 1.5;
40 | background: $common_body_background;
41 | color: $common_body_color;
42 | }
43 |
44 | h1, h2, h3, h4 {
45 | font-size: 18px;
46 | font-weight: normal;
47 | }
48 |
49 | body, input, textarea, select, button {
50 | font-family: $common_fontFamily;
51 | outline: none;
52 | // stylelint-disable
53 | // For Webkit kernel on mobile
54 | -webkit-text-size-adjust: none;
55 | // stylelint-enable
56 | }
57 |
58 | input, textarea, select, button {
59 | font-size: inherit; // form control's default font in webkit is "-webkit-small-control"
60 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); // remove widget tap highlighted in mobile safari
61 | }
62 |
63 | a {
64 | color: $common_color_link;
65 | text-decoration: none;
66 | -webkit-tap-highlight-color: $common_color_linkTapHighlight;
67 | }
68 |
69 | :focus {
70 | outline: none;
71 | }
72 |
73 | /* #html5 HTML5 元素的支持 */
74 | article, aside, details,
75 | figcaption, figure,
76 | footer, header, hgroup,
77 | main, nav, section,
78 | summary {
79 | display: block;
80 | }
81 |
82 | audio, canvas, video {
83 | display: inline-block;
84 | }
85 |
--------------------------------------------------------------------------------
/qmui/mixin/_adaptation.scss:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 | ////
4 | /// 平台与浏览器适配相关方法
5 | /// @author Clear, Molice, Zhoon, Kayo,Jeff
6 | /// @group 兼容性封装
7 | /// @date 2014-08-19
8 | ////
9 |
10 | /// 清除浮动
11 | ///
12 | /// @group 布局
13 | /// @name clear
14 | @mixin clear {
15 | &:after {
16 | clear: both;
17 | content: ".";
18 | display: block;
19 | line-height: 0;
20 | font-size: 0;
21 | visibility: hidden;
22 | }
23 | }
24 |
25 | %clear {
26 | @include clear;
27 | }
28 |
29 | /// 单行省略号
30 | ///
31 | /// @group 外观
32 | /// @name text-ellipsis
33 | @mixin text-ellipsis {
34 | overflow: hidden;
35 | white-space: nowrap;
36 | text-overflow: ellipsis;
37 | word-break: break-all;
38 | //在IE9的中,如果之前已经设置了word-wrap:break-word,则这里的white-space:nowrap会失效,所以要在这里加上word-wrap:normal来以防万一
39 | word-wrap: normal;
40 | }
41 |
42 | %text-ellipsis {
43 | @include text-ellipsis;
44 | }
45 |
46 | /// 多行省略号
47 | ///
48 | /// @group 外观
49 | /// @name text-multiLine-ellipsis
50 | /// @param {Number} $line - 文字的行数
51 | /// @param {Measure} $lineHeight - 文字行高
52 | /// @throw 不支持多行省略的浏览器降级处理为结尾处没有省略号,直接裁剪掉。
53 | @mixin text-multiLine-ellipsis($line: 2, $lineHeight: 20px) {
54 | line-height: $lineHeight;
55 | overflow: hidden;
56 | height: $lineHeight * $line;
57 | // stylelint-disable
58 | display: -webkit-box;
59 | display: -moz-box;
60 | text-overflow: ellipsis;
61 | -webkit-line-clamp: $line;
62 | -moz-line-clamp: $line;
63 | line-clamp: $line;
64 | -webkit-box-orient: vertical;
65 | -moz-box-orient: vertical;
66 | -webkit-text-size-adjust: none;
67 | // stylelint-enable
68 | box-orient: vertical;
69 | }
70 |
71 | /// 在长单词或 URL 地址内部进行换行,适用于以中文为主混有英文的文字排版
72 | ///
73 | /// @group 外观
74 | /// @name text-breakWord
75 | @mixin text-breakWord {
76 | word-wrap: break-word;
77 | word-break: break-word;
78 | }
79 |
80 | %text-breakWord {
81 | @include text-breakWord;
82 | }
83 |
84 | /// 适配多倍屏的 CSS 选择器
85 | ///
86 | /// @group 设备适配
87 | /// @name screenResolution
88 | /// @param {Number} $num - 需要适配的屏幕倍数
89 | @mixin screenResolution($num) {
90 | @media (-webkit-min-device-pixel-ratio: $num), (min--moz-device-pixel-ratio: $num), (min-device-pixel-ratio: $num), (min-resolution: #{$num}dppx), (min-resolution: #{$num * 96}dpi) {
91 | @content;
92 | }
93 | }
94 |
95 | /// 适配 IE 10 及以上版本的 CSS 选择器,需要针对 IE10 或以上版本的样式可以写在这里
96 | ///
97 | /// @group 设备适配
98 | /// @name screenForIE10AndLater
99 | @mixin screenForIE10AndLater {
100 | @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
101 | @content;
102 | }
103 | }
104 |
105 | /// 单独适配 IE 8 CSS 选择器,需要仅针对 IE 8 的样式可以写在这里
106 | ///
107 | /// @group 设备适配
108 | /// @name forIE8
109 | @mixin forIE8 {
110 | @media \0screen {
111 | @content;
112 | }
113 | }
114 |
115 | /// 单独适配 IE 9 CSS 选择器,需要仅针对 IE 9(不包括 IE 10 等更高版本) 的样式可以写在这里
116 | ///
117 | /// @group 设备适配
118 | /// @name forIE9
119 | @mixin forIE9 {
120 | @media all and (min-width: 0\0) and (min-resolution: .001dpcm) {
121 | @content;
122 | }
123 | }
124 |
125 | /// 半透明背景颜色
126 | ///
127 | /// @group 外观
128 | /// @name bgWithOpacity
129 | /// @param {Color} $color - 背景色的颜色值
130 | /// @param {Number} $alpha - 背景色的透明度
131 | @mixin bgWithOpacity($color, $alpha) {
132 | background-color: rgba($color, $alpha);
133 | @include forIE8 {
134 | filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#{ie-hex-str(rgba($color, $alpha))}, endcolorstr=#{ie-hex-str(rgba($color, $alpha))});
135 | }
136 | }
137 |
138 | /// 跨浏览器的渐变背景,垂直渐变,自上而下
139 | ///
140 | /// @group 外观
141 | /// @name gradient-vertical
142 | /// @param {Color} $start-color [#555] - 渐变的开始颜色
143 | /// @param {Color} $end-color [#333] - 渐变的结束颜色
144 | /// @param {Number} $start-percent [0%] - 渐变的开始位置,需要以百分号为单位
145 | /// @param {Number} $end-percent [100%] - 渐变的结束位置,需要以百分号为单位
146 | @mixin gradient-vertical($start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) {
147 | background-image: linear-gradient(to bottom, $start-color $start-percent, $end-color $end-percent);
148 | background-repeat: repeat-x;
149 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#{ie-hex-str($start-color)}", endColorstr="#{ie-hex-str($end-color)}", GradientType=0); // IE9 and down
150 | }
151 |
152 | /// 跨浏览器的渐变背景,水平渐变,自左而右
153 | ///
154 | /// @group 外观
155 | /// @name gradient-horizontal
156 | /// @param {Color} $start-color [#555] - 渐变的开始颜色
157 | /// @param {Color} $end-color [#333] - 渐变的结束颜色
158 | /// @param {Number} $start-percent [0%] - 渐变的开始位置,需要以百分号为单位
159 | /// @param {Number} $end-percent [100%] - 渐变的结束位置,需要以百分号为单位
160 | @mixin gradient-horizontal($start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) {
161 | background-image: linear-gradient(to right, $start-color $start-percent, $end-color $end-percent);
162 | background-repeat: repeat-x;
163 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#{ie-hex-str($start-color)}", endColorstr="#{ie-hex-str($end-color)}", GradientType=1); // IE9 and down
164 | }
165 |
166 | /// 跨浏览器的渐变背景,带角度
167 | ///
168 | /// @group 外观
169 | /// @name gradient-on-axis
170 | /// @param {Degree} $axis-degree [135deg] - 渐变的轴
171 | /// @param {Color} $start-color [#555] - 渐变的开始颜色
172 | /// @param {Color} $end-color [#333] - 渐变的结束颜色
173 | /// @param {Number} $start-percent [0%] - 渐变的开始位置,需要以百分号为单位
174 | /// @param {Number} $end-percent [100%] - 渐变的结束位置,需要以百分号为单位
175 | @mixin gradient-on-axis($axis-degree: 0, $start-color: #555, $end-color: #333, $start-percent: 0%, $end-percent: 100%) {
176 | background-image: linear-gradient($axis-degree, $start-color $start-percent, $end-color $end-percent);
177 | background-repeat: repeat-x;
178 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#{ie-hex-str($start-color)}", endColorstr="#{ie-hex-str($end-color)}", GradientType=0); // IE9 and down
179 | }
180 |
181 | /// 跨浏览器的渐变背景,垂直渐变,自上而下,支持三个渐变点
182 | ///
183 | /// @group 外观
184 | /// @name gradient-vertical-threeColor
185 | /// @param {Color} $start-color [#555] - 渐变的开始颜色
186 | /// @param {Color} $middle-color [#444] - 渐变的中间颜色
187 | /// @param {Color} $end-color [#333] - 渐变的结束颜色
188 | /// @param {Number} $start-percent [0%] - 渐变的开始位置,需要以百分号为单位
189 | /// @param {Number} $start-percent [50%] - 渐变的中间位置,需要以百分号为单位
190 | /// @param {Number} $end-percent [100%] - 渐变的结束位置,需要以百分号为单位
191 | @mixin gradient-vertical-threeColor($start-color: #555, $middle-color: #444, $end-color: #333, $start-percent: 0%, $middle-percent: 50%, $end-percent: 100%) {
192 | background-image: linear-gradient(to bottom, $start-color $start-percent, $middle-color $middle-percent, $end-color $end-percent);
193 | background-repeat: repeat-x;
194 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#{ie-hex-str($start-color)}", endColorstr="#{ie-hex-str($end-color)}", GradientType=0); // IE9 and down
195 | }
196 |
197 | /// 跨浏览器的渐变背景,水平渐变,自左而右
198 | ///
199 | /// @group 外观
200 | /// @name gradient-horizontal-threeColor
201 | /// @param {Color} $start-color [#555] - 渐变的开始颜色
202 | /// @param {Color} $middle-color [#444] - 渐变的中间颜色
203 | /// @param {Color} $end-color [#333] - 渐变的结束颜色
204 | /// @param {Number} $start-percent [0%] - 渐变的开始位置,需要以百分号为单位
205 | /// @param {Number} $start-percent [50%] - 渐变的中间位置,需要以百分号为单位
206 | /// @param {Number} $end-percent [100%] - 渐变的结束位置,需要以百分号为单位
207 | @mixin gradient-horizontal-threeColor($start-color: #555, $middle-color: #444, $end-color: #333, $start-percent: 0%, $middle-percent: 50%, $end-percent: 100%) {
208 | background-image: linear-gradient(to right, $start-color $start-percent, $middle-color $middle-percent, $end-color $end-percent);
209 | background-repeat: repeat-x;
210 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#{ie-hex-str($start-color)}", endColorstr="#{ie-hex-str($end-color)}", GradientType=1); // IE9 and down
211 | }
212 |
213 | /// 基于渐变实现的垂直方向点画线
214 | /// @throw 如果要实现的是一个物理像素粗细的线,建议放在一个单独的 DOM 上,方便加上 scale 来实现,否则就不需要顾虑直接用到任意 DOM 上即可,加上 background-position 控制位置,background-color / background-position 等属性必须在该 mixin 的 include 之后开始写。
215 | ///
216 | /// @group 外观
217 | /// @name gradient-vertical-dashed-line
218 | /// @param {Number} $dash-dot-width [2px] - 点画线里点的尺寸
219 | /// @param {Number} $dash-space-width [2px] - 点画线里点与点间隔尺寸
220 | /// @param {Color} $dash-dot-color [$common_color_separator] - 点画线里点的颜色
221 | /// @param {Number} $line-width [1px] - 点画线粗细
222 | @mixin gradient-vertical-dashed-line($dash-dot-width: 2px, $dash-space-width: 2px, $dash-dot-color: $common_color_separator, $line-width: 1px) {
223 | @include gradient-vertical-threeColor($start-color: $dash-dot-color, $middle-color: $dash-dot-color, $end-color: transparent, $start-percent: 0, $middle-percent: $dash-dot-width, $end-percent: $dash-dot-width);
224 | background-size: $line-width ($dash-dot-width + $dash-space-width);
225 | background-repeat: repeat-y;
226 | }
227 |
228 | /// 基于渐变实现的水平方向点画线
229 | /// @throw 如果要实现的是一个物理像素粗细的线,建议放在一个单独的 DOM 上,方便加上 scale 来实现,否则就不需要顾虑直接用到任意 DOM 上即可,加上 background-position 控制位置,background-color / background-position 等属性必须在该 mixin 的 include 之后开始写。
230 | ///
231 | /// @group 外观
232 | /// @name gradient-horizontal-dashed-line
233 | /// @param {Number} $dash-dot-width [2px] - 点画线里点的尺寸
234 | /// @param {Number} $dash-space-width [2px] - 点画线里点与点间隔尺寸
235 | /// @param {Color} $dash-dot-color [$common_color_separator] - 点画线里点的颜色
236 | /// @param {Number} $line-width [1px] - 点画线粗细
237 | @mixin gradient-horizontal-dashed-line($dash-dot-width: 2px, $dash-space-width: 2px, $dash-dot-color: $common_color_separator, $line-width: 1px) {
238 | @include gradient-horizontal-threeColor($start-color: $dash-dot-color, $middle-color: $dash-dot-color, $end-color: transparent, $start-percent: 0, $middle-percent: $dash-dot-width, $end-percent: $dash-dot-width);
239 | background-size: ($dash-dot-width + $dash-space-width) $line-width;
240 | background-repeat: repeat-x;
241 | }
242 |
--------------------------------------------------------------------------------
/qmui/mixin/_mixin.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /**
3 | * _mixin.scss
4 | * @author Kayo
5 | * @date 2014-10-31
6 | *
7 | */
8 |
9 | // 平台与浏览器适配相关方法
10 | @import "adaptation";
11 |
12 | // 常用工具方法
13 | @import "tool/tool";
14 |
--------------------------------------------------------------------------------
/qmui/mixin/tool/_calculate.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | @use "sass:math";
4 |
5 | ////
6 | /// 辅助数值计算的工具方法
7 | /// @author Kayo
8 | /// @group 数值计算
9 | /// @date 2015-08-23
10 | ////
11 |
12 | /// 获取 CSS 长度值属性(例如:margin,padding,border-width 等)在某个方向的值
13 | ///
14 | /// @name getLengthDirectionValue
15 | /// @param {String} $property - 记录着长度值的 SASS 变量
16 | /// @param {String} $direction - 需要获取的方向,可选值为 top,right,bottom,left,horizontal,vertical,其中 horizontal 和 vertical 分别需要长度值的左右或上下方向值相等,否则会报 Warning。
17 | /// @example
18 | /// // UI 界面的一致性往往要求相似外观的组件保持距离、颜色等元素统一,例如:
19 | /// // 搜索框和普通输入框分开两种结构处理,但希望搜索框的搜索 icon 距离左边的空白与
20 | /// // 普通输入框光标距离左边的空白保持一致,就需要获取普通输入框的 padding-left
21 | /// $textField_padding: 4px 5px;
22 | /// .dm_textField {
23 | /// padding: $textField_padding;
24 | /// }
25 | /// .dm_searchInput {
26 | /// position: relative;
27 | /// ...
28 | /// }
29 | /// .dm_searchInput_icon {
30 | /// position: absolute;
31 | /// left: getLengthDirectionValue($textField_padding, left);
32 | /// ...
33 | /// }
34 | @function getLengthDirectionValue($property, $direction) {
35 | // 声明变量
36 | $top: 0;
37 | $right: 0;
38 | $bottom: 0;
39 | $left: 0;
40 | // 获取 $property 列表值中值的个数,从而判断是哪种 CSS length 的写法
41 | $propertyLength: length($property);
42 | @if $propertyLength == 1 {
43 | $top: $property;
44 | $right: $property;
45 | $bottom: $property;
46 | $left: $property;
47 | } @else if $propertyLength == 2 {
48 | $top: nth($property, 1);
49 | $right: nth($property, 2);
50 | $bottom: nth($property, 1);
51 | $left: nth($property, 2);
52 | } @else if $propertyLength == 3 {
53 | $top: nth($property, 1);
54 | $right: nth($property, 2);
55 | $bottom: nth($property, 3);
56 | $left: nth($property, 2);
57 | } @else if $propertyLength == 4 {
58 | $top: nth($property, 1);
59 | $right: nth($property, 2);
60 | $bottom: nth($property, 3);
61 | $left: nth($property, 4);
62 | } @else {
63 | @return 0;
64 | }
65 |
66 | // 根据参数中的方向值输出需要的结果
67 | @if $direction == top {
68 | @return $top;
69 | } @else if $direction == right {
70 | @return $right;
71 | } @else if $direction == bottom {
72 | @return $bottom;
73 | } @else if $direction == left {
74 | @return $left;
75 | } @else if $direction == horizontal {
76 | @if $left != $right {
77 | @warn "左边(#{$left})与右边(#{$right})的值并不相等,不应该直接使用 horizontal 这个方向";
78 | }
79 | @return $left;
80 | } @else if $direction == vertical {
81 | @if $top != $bottom {
82 | @warn "上边(#{$top})与下边(#{$bottom})的值并不相等,不应该直接使用 vertical 这个方向";
83 | }
84 | @return $top;
85 | } @else {
86 | @return 0;
87 | }
88 | }
89 |
90 | /// 获取两个 CSS 长度值的中间值并取整,通常可用于子元素在父元素中需要居中时计算两者高度差
91 | ///
92 | /// @name getLengthMaxIntegerCenter
93 | /// @param {Number | String} $parent - 较大的长度值
94 | /// @param {Number | String} $child - 较小的长度值
95 | @function getLengthMaxIntegerCenter($parent, $child) {
96 | $center: math.div($parent - $child, 2);
97 | // 注意这里的取整使用 ceil 而不是 floor 并不是随意写的,这是模拟现代浏览器对于小数点长度值的表现而定的。
98 | // 例如,margin-top: 10.5px 在现代浏览器中会表现为 margin-top: 11px 而不是 margin-top: 10px
99 | // 又例如,margin-top: -10.5px 在现代浏览器的表现等同于 margin-top: -10px 而不是 margin-top: -11px
100 | // 即小数长度值会被当成不小于该小数的下一个整数去处理,也就是 ceil 的效果。所以不要随意改成 floor,其他长度值方法也应该如此处理
101 | @return ceil($center);
102 | }
103 |
104 | /// 获取数值的n次幂的值
105 | ///
106 | /// @name pow
107 | /// @param {Number} $number - 底数
108 | /// @param {Number} $pow - 幂数
109 | /// @example
110 | /// pow(10, 5) => 100000
111 | /// pow(10, -1) => 0.1
112 | @function pow($number, $pow) {
113 | $result: 1;
114 | @if $pow > 0 {
115 | @for $i from 1 through $pow {
116 | $result: $result * $number;
117 | }
118 | } @else if $pow < 0 {
119 | @for $i from $pow to 0 {
120 | $result: $result / $number;
121 | }
122 | }
123 | @return $result;
124 | }
125 |
126 | /// 获取数值的开平方值
127 | ///
128 | /// @name sqrt
129 | /// @param {Number} $number - 待开平方的数值
130 | /// @example
131 | /// sqrt(2) => 1.414214
132 | @function sqrt($num) {
133 | $temp:1;
134 | @while abs($temp - $num / $temp) > 1e-6 {
135 | $temp: ($temp + $num / $temp) / 2;
136 | }
137 | @return $temp;
138 | }
139 |
140 | /// 将数值格式化为指定小数位数的数字。
141 | ///
142 | /// @name toFixed
143 | /// @param {Number} $number - 待格式化的数值
144 | /// @param {Number} $precision [0] - 精确度(精确到小数点后几位)
145 | /// @param {String} $type [round] - 格式化方式("round":"四舍五入","floor":"向下取整","ceil":"向上取整")
146 | /// @example
147 | /// toFixed(3.1415926535898) => 3.14
148 | /// toFixed(3.1415926535898, 4, floor) => 3.1415
149 | /// toFixed(3.1415926535898, 4, ceil) => 3.1416
150 | /// toFixed(-3.1415926535898, 4, floor) => -3.1416
151 | /// toFixed(-3.1415926535898, 4, ceil) => -3.1415
152 | /// toFixed(3.1415926535898px) => 3.14px
153 | @function toFixed($number, $precision: 0, $type: round) {
154 | $result: null;
155 | @if $type == round {
156 | $result: round($number * pow(10, $precision)) / pow(10, $precision);
157 | } @else if $type == floor {
158 | $result: floor($number * pow(10, $precision)) / pow(10, $precision);
159 | } @else if $type == ceil {
160 | $result: ceil($number * pow(10, $precision)) / pow(10, $precision);
161 | } @else {
162 | @warn "type参数输入有误,请选择输入'round'、'floor'、'ceil'其中一个";
163 | $result: $number;
164 | }
165 | @return $result;
166 | }
167 |
168 | /// 阶乘计算
169 | ///
170 | /// @name factorial
171 | /// @param {Number} $number - 待进行阶乘计算的数值
172 | /// @example
173 | /// factorial(4) => 4 * 3 * 2 * 1 => 24
174 | @function factorial($number) {
175 | $value: 1;
176 | @if $number > 0 {
177 | @for $i from 1 through $number {
178 | $value: $value * $i;
179 | }
180 | }
181 | @return $value;
182 | }
183 |
184 | /// 获取 π 的值(11位小数精度)
185 | ///
186 | /// @name pi
187 | @function pi() {
188 | @return 3.14159265359;
189 | }
190 |
191 | /// 通过角度计算弧度
192 | ///
193 | /// @name rad
194 | /// @param {Number} $angle - 需要被转换为弧度的角度值
195 | /// @example
196 | /// rad(180deg) -> 3.14159
197 | /// rad(45deg) -> 0.7854
198 | @function rad($angle) {
199 | $unit: unit($angle);
200 | $unitless: $angle / ($angle * 0 + 1);
201 | @if $unit == deg {
202 | $unitless: $unitless / 180 * pi();
203 | }
204 | @return $unitless;
205 | }
206 |
207 | /// 计算 sin 三角函数
208 | ///
209 | /// @name sin
210 | /// @param {Number} $angle - 需要进行 sin 计算的角度值
211 | /// @example
212 | /// sin(45deg) -> 0.70711
213 | /// sin(90deg) -> 1
214 | @function sin($angle) {
215 | $sin: 0;
216 | $angle: rad($angle);
217 | @for $i from 0 through 10 {
218 | $sin: $sin + pow(-1, $i) * pow($angle, (2 * $i + 1)) / factorial(2 * $i + 1);
219 | }
220 | @return $sin;
221 | }
222 |
223 | /// 计算 cos 三角函数
224 | ///
225 | /// @name cos
226 | /// @param {Number} $angle - 需要进行 cos 计算的角度值
227 | /// @example
228 | /// cos(45deg) -> 0.70711
229 | /// cos(90deg) -> 0
230 | @function cos($angle) {
231 | $cos: 0;
232 | $angle: rad($angle);
233 | @for $i from 0 through 10 {
234 | $cos: $cos + pow(-1, $i) * pow($angle, 2 * $i) / factorial(2 * $i);
235 | }
236 | @return $cos;
237 | }
238 |
239 | /// 计算 tan 三角函数
240 | ///
241 | /// @name tan
242 | /// @param {Number} $angle - 需要进行 tan 计算的角度值
243 | /// @example
244 | /// tan(45deg) -> 1
245 | /// tan(50deg) -> 1.19175
246 | @function tan($angle) {
247 | @return sin($angle) / cos($angle);
248 | }
249 |
--------------------------------------------------------------------------------
/qmui/mixin/tool/_effect.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | @use "sass:math";
4 |
5 | ////
6 | /// 辅助编写样式效果的工具方法
7 | /// @author Kayo
8 | /// @group 样式特效
9 | /// @date 2015-08-23
10 | ////
11 |
12 | /// 产生正方形的宽高
13 | ///
14 | /// @name square
15 | /// @param {Measure} $length - 宽高的长度
16 | @mixin square($length) {
17 | width: $length;
18 | height: $length;
19 | }
20 |
21 | /// 利用 absolute 把指定元素水平垂直居中布局,适用于已知元素宽高的情况下
22 | ///
23 | /// @name absoluteCenter
24 | /// @param {Measure} $width - 元素的宽度
25 | /// @param {Measure} $height - 元素的高度
26 | @mixin absoluteCenter($width, $height) {
27 | position: absolute;
28 | left: 50%;
29 | top: 50%;
30 | margin: math.div(-$height, 2) 0 0 math.div(-$width, 2);
31 | }
32 |
33 | %triangleCommonStyle {
34 | display: block;
35 | content: " ";
36 | width: 0;
37 | line-height: 0;
38 | font-size: 0;
39 | border-style: solid;
40 | border-color: transparent;
41 | }
42 |
43 | /// CSS Border 三角形
44 | ///
45 | /// @name triangle
46 | /// @param {Measure} $width - 三角形的底边的宽
47 | /// @param {Measure} $height - 三角形的高
48 | /// @param {String} $direction - 三角形的方向(即与底边相对的顶点指向的方向)
49 | /// @param {Color} $borderColor - 三角形的边框色
50 | /// @throw 由于方法内包含了有 $width / 2 的计算,因此如果 $width 的值为奇数,则实际上计算出的三角形会偏小,建议 $width 不要使用奇数。
51 | @mixin triangle($width, $height, $direction, $borderColor) {
52 | @extend %triangleCommonStyle;
53 | /* 向上小三角 */
54 | @if $direction == top {
55 | border-width: $height math.div($width, 2);
56 | border-top: 0;
57 | border-bottom-color: $borderColor;
58 | }
59 | /* 向下小三角 */
60 | @else if $direction == bottom {
61 | border-width: $height math.div($width, 2);
62 | border-bottom: 0;
63 | border-top-color: $borderColor;
64 | }
65 | /* 向左小三角 */
66 | @else if $direction == left {
67 | border-width: math.div($width, 2) $height;
68 | border-left: 0;
69 | border-right-color: $borderColor;
70 | }
71 | /* 向右小三角 */
72 | @else if $direction == right {
73 | border-width: math.div($width, 2) $height;
74 | border-right: 0;
75 | border-left-color: $borderColor;
76 | }
77 | }
78 |
79 | /// 用以生成十字架图标
80 | ///
81 | /// @name cross
82 | /// @param {Measure} $crossLength [26px] - 十字架的大小
83 | /// @param {Measure} $crossLineThickness [2px] - 十字架线条的粗细
84 | /// @param {Color} $crossLineColor [#2685d2] - 十字架的颜色
85 | @mixin cross($crossLength: 26px, $crossLineThickness: 2px, $crossLineColor: $common_color_link) {
86 | position: relative;
87 | @include square($crossLength);
88 | &:before,
89 | &:after {
90 | content: "";
91 | font-size: 0;
92 | line-height: 0;
93 | position: absolute;
94 | background-color: $crossLineColor;
95 | }
96 | &:before {
97 | left: getLengthMaxIntegerCenter($crossLength, $crossLineThickness);
98 | top: 0;
99 | width: $crossLineThickness;
100 | height: 100%;
101 | }
102 | &:after {
103 | left: 0;
104 | top: getLengthMaxIntegerCenter($crossLength, $crossLineThickness);
105 | width: 100%;
106 | height: $crossLineThickness;
107 | }
108 | }
109 |
110 | /// 使得指定的元素产生 Block Formatting Contexts 或 hasLayout
111 | ///
112 | /// @name bfc
113 | @mixin bfc {
114 | overflow: hidden;
115 | zoom: 1;
116 | }
117 |
118 | // borderStyleForOnePixel 是 onePixelBorder 内部使用的方法
119 | @mixin borderStyleForOnePixel($direction: all, $color:#dedede) {
120 | border-style: solid;
121 | border-color: $color;
122 | @if $direction == all {
123 | border-width: 1px;
124 | } @else if $direction == top {
125 | border-width: 1px 0 0 0;
126 | } @else if $direction == bottom {
127 | border-width: 0 0 1px 0;
128 | } @else if $direction == left {
129 | border-width: 0 0 0 1px;
130 | } @else if $direction == right {
131 | border-width: 0 1px 0 0;
132 | } @else if $direction == horizontal {
133 | border-width: 0 1px;
134 | } @else if $direction == vertical {
135 | border-width: 1px 0;
136 | } @else if $direction == none {
137 | border-width: 0;
138 | }
139 | }
140 |
141 | /// 在移动设备上生成 1px 宽的边框,direction 支持 all, top, bottom, left, right, horizontal, vertical, none 8个 direction 值,position 支持 outside 和 inside 两个值
142 | ///
143 | /// @name onePixelBorder
144 | /// @param {String} $direction [all] - 边框的方向,支持 all(所有方向),top(上边框),right(右边框),bottom(下边框),left(左边框),horizontal(左右边框),vertical(上下边框),none(无边框)
145 | /// @param {Color} $color - 边框的颜色
146 | /// @param {String} $position [outside] - 边框的位置,支持 outside 和 inside
147 | /// @param {Number} $borderRadius [0] - 边框的圆角
148 | /// @param {Number} $offset 水平缩进值
149 | /// @throw 在多倍屏下,本方法会利用元素的 ::after 做效果,因此需要注意使用了该方法后 ::after 则尽量避免添加样式,以免影响效果
150 | @mixin onePixelBorder($direction: all, $color: $common_color_border, $position: outside, $borderRadius: 0, $borderStyle: solid, $offset: 0) {
151 | @include borderStyleForOnePixel($direction, $color);
152 | border-radius: $borderRadius;
153 | border-style: $borderStyle;
154 | @include screenResolution(2) {
155 | position: relative;
156 | border: 0;
157 | &:after {
158 | content: "";
159 | position: absolute;
160 | top: 0;
161 | left: $offset;
162 | @if $offset == 0 {
163 | width: 200%;
164 | } @else {
165 | width: calc(200% - #{$offset * 2 * 2});
166 | }
167 | height: 200%;
168 | border-radius: $borderRadius * 2;
169 | @include borderStyleForOnePixel($direction, $color);
170 | border-style: $borderStyle;
171 | transform: scale(.5);
172 | transform-origin: 0 0;
173 | @if $position == inside {
174 | box-sizing: border-box;
175 | }
176 | pointer-events: none;
177 | }
178 | }
179 | @include screenResolution(3) {
180 | &:after {
181 | @if $offset == 0 {
182 | width: 300%;
183 | } @else {
184 | width: calc(300% - #{$offset * 3 * 2});
185 | }
186 | height: 300%;
187 | border-radius: $borderRadius * 3;
188 | transform: scale(math.div(1, 3));
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/qmui/mixin/tool/_enhance.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 |
3 | ////
4 | /// 原生增强,补充 SASS 原生语法缺少的能力
5 | /// @author Kayo
6 | /// @group Sass 原生增强
7 | /// @date 2016-06-18
8 | ////
9 |
10 | /// 字符串 replace 方法,用于在字符串中用一些字符替换另一些字符
11 | ///
12 | /// @name str-replace
13 | /// @param {String} $string - 需要进行查找的字符串
14 | /// @param {String} $search - 规定需要被替换的子字符串
15 | /// @param {String} $replace [''] - 替换文本
16 | /// @example
17 | /// str-replace("QMUI Web", " Web") => "QMUI"
18 | /// str-replace("QMUI Web", "Web", "iOS") => "QMUI iOS"
19 | /// str-replace("QMUI Web", "Web", "Android") => "QMUI Android"
20 | @function str-replace($string, $search, $replace: "") {
21 | $index: str-index($string, $search);
22 |
23 | @if $index {
24 | @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
25 | }
26 |
27 | @return $string;
28 | }
29 |
30 | /// 加亮颜色(以百分比的形式加入加色)
31 | ///
32 | /// @param {Color} $color - 需要被加亮的颜色值
33 | /// @param {Number} $percentage - 需要增加的白色的百分比
34 | @function tint($color, $percentage) {
35 | @return mix(white, $color, $percentage);
36 | }
37 |
38 | /// 加暗颜色(以百分比的形式加入黑色)
39 | ///
40 | /// @param {Color} $color - 需要被加暗的颜色值
41 | /// @param {Number} $percentage - 需要增加的黑色的百分比
42 | @function shade($color, $percentage) {
43 | @return mix(black, $color, $percentage);
44 | }
45 |
--------------------------------------------------------------------------------
/qmui/mixin/tool/_tool.scss:
--------------------------------------------------------------------------------
1 | @charset "utf-8";
2 | /**
3 | * _tool.scss 常用工具方法
4 | * @author Kayo
5 | * @date 2015-08-23
6 | *
7 | */
8 |
9 | // 辅助编写样式效果的工具方法
10 | @import "effect";
11 |
12 | // 辅助数值计算的工具方法
13 | @import "calculate";
14 |
15 | // 原生增强,补充 SASS 原生语法缺少的能力
16 | @import "enhance";
17 |
--------------------------------------------------------------------------------
/stylelint.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Stylelint Config for `QMUI Web`
3 | *
4 | * https://stylelint.io/
5 | */
6 |
7 | module.exports = {
8 | 'ignoreFiles': [''],
9 | 'plugins': [
10 | 'stylelint-wechat-work-css'
11 | ],
12 | 'rules': {
13 | /**
14 | * Plungins
15 | */
16 | // @include 黑名单
17 | 'wechat-work/unused-mixins':
18 | [
19 | '/^transition/',
20 | '/^transform/',
21 | '/^translate/',
22 | '/^scale/',
23 | '/^rotate/',
24 | '/^animation/',
25 | 'box-sizing',
26 | 'box_sizing',
27 | 'inlineBlock',
28 | 'box-shadow',
29 | 'box_shadow',
30 | 'opacity',
31 | 'keyframes'
32 | ],
33 | 'wechat-work/comments-in-header': false, // 文件头部需要有注释 @date 和 @author,QMUI Web 源码中使用的是 SassDoc 的注释格式,不被识别,因此设置为 false
34 | 'wechat-work/selector-namespace-follow-filename': [true, {
35 | 'fileDirWhiteList': ['widget', 'qmui'],
36 | 'filenameWhitelist': []
37 | }], // 业务CSS 的命名空间需要跟随文件名(关于命名空间请浏览器:http://qmuiteam.com/web/page/codeNorm.html#qui_htmlAndCSSNorm)
38 | 'wechat-work/unused-nested-selector-namespace': true, // 不建议在嵌套中使用 qui_ 开头的类
39 |
40 | /**
41 | * Color
42 | */
43 | 'color-hex-length': 'short', // 指定十六进制颜色是否使用缩写
44 | // 禁止使用颜色名称
45 | 'color-named': ['never', {
46 | 'ignore': ['inside-function']
47 | }],
48 |
49 | /**
50 | * Function
51 | */
52 | 'function-comma-space-after': 'always', // 函数内的内容,逗号之后要求有一个空格或禁止有空白
53 |
54 | /**
55 | * Number
56 | */
57 | 'number-leading-zero': 'never', // 禁止书写小数的前导 0
58 | 'number-no-trailing-zeros': true, // 禁止数字中的拖尾 0
59 |
60 | /**
61 | * String
62 | */
63 | 'string-quotes': 'double', // 字符串用双引号
64 |
65 | /**
66 | * Length
67 | */
68 | 'length-zero-no-unit': true, // 长度值为0时,禁止带单位
69 |
70 | /**
71 | * Block
72 | */
73 | 'block-opening-brace-space-before': 'always', // 尖括号前必须有空格
74 |
75 | /**
76 | * Selector
77 | */
78 | 'selector-max-id': 0, // 选择器不能为 id
79 | 'selector-combinator-space-after': 'always', // 在关系选择符之后要求有一个空格或禁止有空白
80 | 'selector-max-compound-selectors': 4, // 限制复合选择器的数量
81 | // 选择器命名规范
82 | 'selector-class-pattern': ['^((?!js)[a-z][a-zA-Z0-9]*)(_[a-zA-Z0-9]+)*$', {
83 | 'resolveNestedSelectors': true
84 | }],
85 |
86 | /**
87 | * Declaration
88 | */
89 | 'declaration-colon-space-after': 'always', // 在冒号之后要求有一个空格或禁止有空白
90 | 'declaration-block-semicolon-newline-after': 'always', // 在声明块的分号之后要求有一个换行符或禁止有空白
91 | // 不能用的规则,目前有 border: none; 建议为 border: 0;或 border: 0 none;
92 | 'declaration-property-value-blacklist': {
93 | 'border': ['none']
94 | },
95 |
96 | /**
97 | * Property
98 | */
99 | 'property-no-vendor-prefix': true, // 禁止使用带浏览器前缀的属性
100 |
101 | /**
102 | * Value
103 | */
104 | 'value-no-vendor-prefix': true, // 禁止使用带浏览器前缀的属性值
105 | 'value-list-comma-space-after': 'always', // 在属性值的内容中,逗号之后要求有一个空格或禁止有空白
106 |
107 | /**
108 | * At-rule
109 | */
110 | 'at-rule-no-vendor-prefix': true, // 禁止 at(@) 规则使用浏览器前缀
111 | 'keyframe-declaration-no-important': true, // 禁止在 keyframe 声明中使用 !important
112 |
113 | /**
114 | * Rule
115 | */
116 | // 除特殊情况,规则前必须有空行
117 | 'rule-empty-line-before': ['always', {
118 | 'ignore': ['after-comment', 'inside-block']
119 | }],
120 |
121 | /**
122 | * Comment
123 | */
124 | 'comment-no-empty': true, // 禁止空注释,CSS 规则内注释不受限制
125 | 'comment-whitespace-inside': 'always', // 注释前后需要有空格
126 |
127 | /**
128 | * General / Sheet
129 | */
130 | 'max-nesting-depth': 4, // 限制允许嵌套的深度
131 | 'indentation': 4 // 缩进,4 spaces
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/workflow/Mix.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 声明插件以及配置文件的依赖
17 | const plugins = require('gulp-load-plugins')({
18 | rename: {
19 | 'gulp-file-include': 'include',
20 | 'gulp-merge-link': 'merge',
21 | 'gulp-better-sass-inheritance': 'sassInheritance'
22 | }
23 | });
24 | const packageInfo = require('../package.json');
25 | const browserSync = require('browser-sync').create();
26 | const reload = browserSync.reload;
27 | const colors = require('ansi-colors');
28 | const defaultsDeep = require('lodash/defaultsDeep');
29 | const util = new (require('./Util'))();
30 |
31 | let configDefault;
32 | let configUser = {};
33 |
34 | // 读取项目配置表
35 | try {
36 | configDefault = require('../../qmui.config.js');
37 | } catch (error) {
38 | util.log(colors.red('QMUI Config: ') + '找不到项目配置表,请按照 http://qmuiteam.com/web/index.html 的说明进行项目配置');
39 | }
40 |
41 | try {
42 | configUser = require('../../qmui.config.user.js');
43 | } catch (error) {
44 | // 没有个人用户配置,无需额外处理
45 | }
46 |
47 | // 创建 common 对象
48 | class Mix {
49 | constructor() {
50 | this.plugins = plugins;
51 | this.config = defaultsDeep(configUser, configDefault);
52 | this.packageInfo = packageInfo;
53 | this.browserSync = browserSync;
54 | this.reload = reload;
55 |
56 | this.timeFormat = new (require('./TimeFormat'))();
57 | this.util = util;
58 |
59 | this.tasks = {};
60 | }
61 |
62 | /**
63 | * 增加任务说明的接口。
64 | * @param {String} name QMUI 工作流中任务的名字。
65 | * @param {String} description 任务的简短介绍。
66 | * @param {Object} options 任务的参数。
67 | * @returns {undefined}
68 | */
69 | addTaskDescription(name, description, options) {
70 | this.tasks[name] = {
71 | description: description,
72 | options: options
73 | };
74 | }
75 |
76 | /**
77 | * 获取所有任务。
78 | * @returns {Array} 返回 QMUI 工作流中所有任务。
79 | */
80 | getAllTask() {
81 | return this.tasks;
82 | }
83 |
84 | /**
85 | * 获取单个任务的信息。
86 | * @param {String} name 需要获取的 QMUI 工作流任务的名字。
87 | * @returns {Object} 返回 QMUI 工作流中某个指定的任务。
88 | */
89 | getTask(name) {
90 | return this.tasks[name];
91 | }
92 | }
93 |
94 |
95 | module.exports = Mix;
96 |
--------------------------------------------------------------------------------
/workflow/TimeFormat.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | const padStart = require('lodash/padStart');
16 |
17 | // 工具方法
18 | class TimeFormat {
19 | checkDateFormat(date) {
20 | return padStart(date.toString(), 2, '0');
21 | }
22 |
23 | getCurrentTime() {
24 | const time = new Date();
25 | return `${this.checkDateFormat(time.getHours())}:${this.checkDateFormat(time.getMinutes())}:${this.checkDateFormat(time.getSeconds())}`;
26 | }
27 | }
28 |
29 | module.exports = TimeFormat;
30 |
--------------------------------------------------------------------------------
/workflow/Util.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | const beeper = require('beeper');
16 | const colors = require('ansi-colors');
17 | const fancyLog = require('fancy-log');
18 | const argv = require('yargs').argv;
19 | const supportsColor = require('color-support');
20 |
21 | /**
22 | * 创建工具类,放置工具方法。
23 | */
24 | class Util {
25 | constructor() {
26 | this.beep = beeper;
27 | this.colors = colors;
28 | }
29 |
30 |
31 | addColor(str, type) {
32 | if (supportsColor() && (typeof argv.color === 'undefined' || argv.color)) {
33 | if (type === 'warn') {
34 | return this.colors.yellow(str);
35 | } else if (type === 'error') {
36 | return this.colors.red(str);
37 | } else if (type === 'info') {
38 | return this.colors.gray(str);
39 | }
40 | return this.colors.green(str);
41 | }
42 | return str;
43 | }
44 |
45 | log(tag, content) {
46 | if (arguments.length > 1) {
47 | fancyLog(this.addColor(`QMUI ${tag}: `, 'log') + content);
48 | } else {
49 | fancyLog(arguments[0]);
50 | }
51 | }
52 |
53 | warn(tag, content) {
54 | if (arguments.length > 1) {
55 | fancyLog(this.addColor(`QMUI ${tag}: `, 'warn') + content);
56 | } else {
57 | fancyLog(arguments[0]);
58 | }
59 | }
60 |
61 | error(tag, content) {
62 | if (arguments.length > 1) {
63 | fancyLog(this.addColor(`QMUI ${tag}: `, 'error') + content);
64 | } else {
65 | fancyLog(arguments[0]);
66 | }
67 | }
68 | }
69 |
70 | module.exports = Util;
71 |
--------------------------------------------------------------------------------
/workflow/basicTasks/clean.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 清理多余文件
17 | const del = require('del');
18 |
19 | module.exports = (gulp, mix) => {
20 |
21 | const taskName = 'clean';
22 |
23 | gulp.task(taskName, (done) => {
24 | // force: true 即允许 del 控制本目录以外的文件
25 | del(mix.config.cleanFileType, {force: true});
26 | mix.util.log('Clean', `清理所有的 ${mix.config.cleanFileType.join(', ')} 文件`);
27 |
28 | done();
29 | });
30 |
31 | // 任务说明
32 | mix.addTaskDescription(taskName, '清理多余文件(清理内容在 config.json 中配置)');
33 | };
34 |
--------------------------------------------------------------------------------
/workflow/basicTasks/include.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 模板 include 命令,解释被 include 的内容并输出独立的 HTML 文件
17 | const path = require('path');
18 |
19 | module.exports = (gulp, mix) => {
20 |
21 | const taskName = 'include';
22 |
23 | gulp.task(taskName, (done) => {
24 |
25 | const _condition = (file) => {
26 | const fileName = path.basename(file.path);
27 | return !fileName.match(/^_/);
28 | };
29 |
30 | gulp.src(mix.config.paths.htmlSourcePath)
31 | .pipe(mix.plugins.plumber({
32 | errorHandler: (error) => {
33 | mix.util.error('Include', error);
34 | mix.util.beep();
35 | }
36 | }))
37 | .pipe(mix.plugins.include({
38 | prefix: mix.config.template.includePrefix // 模板函数的前缀
39 | }))
40 | .pipe(mix.plugins.if(_condition, gulp.dest(mix.config.paths.htmlResultPath)));
41 |
42 | mix.util.log('Include', `根据 include 标签合并后输出新文件到 ${mix.config.paths.htmlResultPath}`);
43 |
44 | done();
45 | });
46 |
47 | // 任务说明
48 | mix.addTaskDescription(taskName, '执行模板 include 编译(建议调用 watch 任务自动监控文件变化并调用)');
49 | };
50 |
--------------------------------------------------------------------------------
/workflow/basicTasks/list.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 | /**
3 | * Tencent is pleased to support the open source community by making QMUI Web available.
4 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
5 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
6 | * with the License. You may obtain a copy of the License at
7 | *
8 | * http://opensource.org/licenses/MIT
9 | *
10 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
11 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12 | * either express or implied. See the License for the specific language governing permissions and
13 | * limitations under the License.
14 | */
15 |
16 | const calculateMargin = require('../calculateMargin.js');
17 |
18 | // 显示 QMUI Web 的版本号
19 | module.exports = (gulp, mix) => {
20 |
21 | const taskName = 'list';
22 |
23 | gulp.task(taskName, (done) => {
24 | const marginData = calculateMargin(mix.tasks);
25 | const margin = marginData.margin;
26 | const optionsBuffer = marginData.hasOptions ? ' --' : '';
27 |
28 | console.log('');
29 | console.log('Usage');
30 | console.log(' gulp [TASK] [OPTIONS...]');
31 | console.log('');
32 | console.log('Available tasks');
33 |
34 | Object.keys(mix.getAllTask()).forEach((name) => {
35 |
36 | const help = mix.getTask(name);
37 |
38 | const args = [' ', mix.util.colors.cyan(name)];
39 |
40 | args.push(new Array(margin - name.length + 1 + optionsBuffer.length).join(' '));
41 |
42 | if (help.description) {
43 | args.push(help.description);
44 | }
45 |
46 | if (help.options) {
47 | const options = Object.keys(help.options);
48 | options.forEach((option) => {
49 | const optText = help.options[option];
50 | args.push('\n ' + optionsBuffer + mix.util.colors.cyan(option) + ' ');
51 |
52 | args.push(new Array(margin - option.length + 1).join(' '));
53 | args.push(optText);
54 | });
55 | }
56 |
57 | console.log.apply(console, args);
58 | });
59 |
60 | console.log('');
61 |
62 | done();
63 | });
64 |
65 | // 任务说明
66 | mix.addTaskDescription(taskName, 'QMUI 内置工作流帮助菜单');
67 | };
68 |
--------------------------------------------------------------------------------
/workflow/basicTasks/merge.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 合并变更文件
17 | const path = require('path');
18 | const argv = require('yargs').argv;
19 | const through = require('through2');
20 | const minimatch = require('minimatch');
21 |
22 | module.exports = (gulp, mix) => {
23 |
24 | const taskName = 'merge';
25 |
26 | const mergeReference = (rules) => {
27 | // 基于 https://github.com/aDaiCode/gulp-merge-link
28 | rules = rules || [];
29 |
30 | const linkRegex = / /g;
31 | const scriptRegex = /';
35 |
36 | const getReference = (reg, contents) => {
37 | let result;
38 | const references = [];
39 | /* eslint-disable no-cond-assign */
40 | while (result = reg.exec(contents)) {
41 | references.push({
42 | match: result[0],
43 | url: result[1].trim().replace(/^\.\//, '')
44 | });
45 | }
46 | /* eslint-enable no-cond-assign */
47 | return references;
48 | };
49 |
50 | const getTemplate = (url) => {
51 | const isScript = (/\.js$/).test(url);
52 | if (isScript) {
53 | return scriptTemplate(url);
54 | }
55 | return linkTemplate(url);
56 | };
57 |
58 | return through.obj((file, encoding, callback) => {
59 | if (file.isNull() || file.isStream()) {
60 | return callback(null, file);
61 | }
62 |
63 | let contents = String(file.contents);
64 | let references = [];
65 | const replaceList = [];
66 | const flag = {};
67 |
68 | // 获取所有引用
69 | references = references.concat(getReference(linkRegex, contents)).concat(getReference(scriptRegex, contents));
70 |
71 | // 循环所有引用,检测是否需要进行处理
72 | for (const key in references) {
73 | const reference = references[key];
74 |
75 | for (const targetUrl in rules) {
76 | // 把引用与传入的合并规则进行对比,把命中规则的引用进行合并处理
77 | if (!rules.hasOwnProperty(targetUrl)) {
78 | break;
79 | }
80 | const sourceUrls = rules[targetUrl];
81 |
82 | const sourceUrlFound = sourceUrls.find((sourceUrl) => {
83 | sourceUrl = sourceUrl.trim().replace(/^\.\//, '');
84 |
85 | return minimatch(reference.url, sourceUrl);
86 | });
87 |
88 | if (sourceUrlFound) {
89 | replaceList.push({
90 | match: reference.match,
91 | replace: flag[targetUrl] ? '' : getTemplate(targetUrl)
92 | });
93 |
94 | flag[targetUrl] = true;
95 | break;
96 | }
97 | }
98 | }
99 |
100 | if (argv.debug) {
101 | mix.util.log('Merge', file.path);
102 | }
103 |
104 | replaceList.forEach((replace) => {
105 | contents = contents.replace(replace.match, replace.replace);
106 | });
107 |
108 | file.contents = Buffer.from(contents);
109 |
110 | return callback(null, file);
111 | });
112 | };
113 |
114 | gulp.task(taskName, (done) => {
115 | // 读取合并规则并保存起来
116 | let mergeRule;
117 | try {
118 | mergeRule = require('../../../qmui.merge.rule.js');
119 | } catch (error) {
120 | mix.util.error('Merge', '没有找到合并规则文件,请按照 http://qmuiteam.com/web/scaffold.html#qui_scaffoldMerge 的说明进行合并规则配置');
121 | }
122 |
123 | const replaceProjectParentDirectory = (source) => {
124 | // 转换为以项目根目录为开头的路径形式
125 | const projectParentDirectory = path.resolve('../../..');
126 | return source.replace(projectParentDirectory, '').replace(/^[\\/]/, '');
127 | };
128 |
129 | // 合并文件
130 | for (const sourceFile in mergeRule) {
131 | // 后面变更文件时,需要的是每个文件在 HTML 中书写的路径,即相对模板文件的路径
132 | // 但对合并文件,即 concat 来说,需要的是文件相对 qmui_web 目录的路径,因此需要对合并的结果以及来源文件手工加上一个 '../'
133 |
134 | const resultFile = `../${sourceFile}`; // 合并的结果加上 '../'
135 | const resultFileName = path.basename(resultFile);
136 | const resultFilePath = path.dirname(resultFile);
137 | const value = mergeRule[sourceFile]; // 来源文件原始路径获取
138 |
139 | const childFiles = [];
140 | let childFilesString = ''; // 用于在 Log 中显示
141 |
142 | // 遍历来源文件并给每个文件加上 '../'
143 | for (let index = 0; index < value.length; index++) {
144 | const childFilesRelative = `../${value[index]}`;
145 | childFiles.push(childFilesRelative);
146 |
147 | // 拼接源文件名用于 Log 中显示
148 | if (index === 0) {
149 | childFilesString = replaceProjectParentDirectory(path.resolve(childFilesRelative));
150 | } else {
151 | childFilesString = `${childFilesString}, ${replaceProjectParentDirectory(path.resolve(childFilesRelative))}`;
152 | }
153 | }
154 |
155 | const condition = (file) => file.path.toString().indexOf('.js') !== -1;
156 |
157 | gulp.src(childFiles)
158 | .pipe(mix.plugins.plumber({
159 | errorHandler: (error) => {
160 | mix.util.error('Merge', error);
161 | mix.util.beep();
162 | }
163 | }))
164 | .pipe(mix.plugins.concat(resultFileName))
165 | .pipe(mix.plugins.if(condition, mix.plugins.uglify(), mix.plugins.cleanCss({compatibility: 'ie8'})))
166 | .pipe(gulp.dest(resultFilePath));
167 |
168 | mix.util.log('Merge', `文件 ${childFilesString} 合并压缩为 ${replaceProjectParentDirectory(path.resolve(path.join(resultFilePath, resultFileName)))}`);
169 | }
170 | // 变更文件引用路径
171 | gulp.src(mix.config.paths.htmlResultPath + '/**/*.html')
172 | .pipe(mergeReference(mergeRule))
173 | .pipe(mix.plugins.htmlmin({
174 | removeComments: true,
175 | collapseWhitespace: true
176 | }))
177 | .pipe(gulp.dest(mix.config.paths.htmlResultPath));
178 | mix.util.log('Merge', '文件合并变更已完成');
179 |
180 | done();
181 | });
182 |
183 | // 任务说明
184 | mix.addTaskDescription(taskName, '合并变更文件');
185 | };
186 |
--------------------------------------------------------------------------------
/workflow/basicTasks/proxy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // proxy 监视文件改动并重新载入
17 | module.exports = (gulp, mix) => {
18 |
19 | gulp.task('proxy', (done) => {
20 |
21 | const showLog = () => {
22 | if (mix.config.browserSync.browserSyncShowLog) {
23 | return 'info';
24 | }
25 | return 'silent';
26 | };
27 |
28 | mix.browserSync.init({
29 | open: 'external',
30 | proxy: mix.config.browserSync.browserSyncProxy,
31 | port: mix.config.browserSync.browserSyncPort,
32 | host: mix.config.browserSync.browserSyncHost,
33 | logLevel: showLog(),
34 | logPrefix: mix.util.addColor(mix.timeFormat.getCurrentTime(), 'info'),
35 | startPath: mix.config.browserSync.browserSyncStartPath
36 | });
37 | gulp.watch(mix.config.browserSync.browserSyncWatchPath).on('all', mix.reload);
38 |
39 | done();
40 | });
41 | };
42 |
--------------------------------------------------------------------------------
/workflow/basicTasks/readToolMethod.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | const fs = require('fs');
16 | const sassdoc = require('sassdoc');
17 | const isEqual = require('lodash/isEqual');
18 |
19 | // 读取含有工具方法的 Sass 文件列表(Sass 文件需要以 Sassdoc 格式编写注释),并将工具名称集输出为 JS 文件
20 | // 传入 Sass 文件列表,以及待输出的 JS 文件地址
21 | module.exports = (gulp) => {
22 |
23 | gulp.task('readToolMethod', (done) => {
24 |
25 | sassdoc.parse('./qmui/mixin').then((data) => {
26 | if (data.length > 0) {
27 | // 按 group 把数组重新整理成二维数组
28 | const result = [];
29 | let currentGroup = null,
30 | currentGroupArray = null;
31 | for (let itemIndex = 0; itemIndex < data.length; itemIndex++) {
32 | const item = data[itemIndex];
33 | if (item.group.toString() !== 'abandon') {
34 | // 排除已废弃的工具方法
35 |
36 | // 由于 IE8- 下 default 为属性的保留关键字,会引起错误,因此这里要把参数中这个 default 的 key 从数据里改名
37 | if (item.parameter) {
38 | for (let parameterIndex = 0; parameterIndex < item.parameter.length; parameterIndex++) {
39 | const paraItem = item.parameter[parameterIndex];
40 | if (paraItem.hasOwnProperty('default')) {
41 | paraItem.defaultValue = paraItem.default;
42 | delete paraItem.default;
43 | }
44 | }
45 | }
46 |
47 | if (!isEqual(item.group, currentGroup)) {
48 | currentGroup = item.group;
49 | currentGroupArray = [];
50 | result.push(currentGroupArray);
51 | } else {
52 | currentGroupArray = result[result.length - 1];
53 | }
54 | currentGroupArray.push(item);
55 | }
56 | }
57 | result.reverse();
58 |
59 | // 准备把数组写入到指定文件中
60 |
61 | const _outputPath = '../../data/qmui_method.js';
62 |
63 | // 写入文件
64 | fs.writeFileSync(_outputPath, `var comments = ${JSON.stringify(result)};`, 'utf8');
65 | }
66 | });
67 |
68 | done();
69 | });
70 | };
71 |
--------------------------------------------------------------------------------
/workflow/basicTasks/reload.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 刷新浏览器
17 | module.exports = (gulp, mix) => {
18 |
19 | gulp.task('reload', (done) => {
20 | mix.reload();
21 | done();
22 | });
23 | };
24 |
--------------------------------------------------------------------------------
/workflow/basicTasks/sass.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 进行 Sass 编译以及雪碧图处理
17 | const argv = require('yargs').argv;
18 | const lazysprite = require('postcss-lazysprite');
19 | const svgSprite = require('postcss-svg-sprite');
20 | const autoprefixer = require('autoprefixer');
21 |
22 | module.exports = (gulp, mix) => {
23 | const lazySpriteConfig = {
24 | cssSeparator: '_',
25 | imagePath: mix.config.paths.imagesSourcePath,
26 | stylesheetRelative: mix.config.paths.styleResultPath,
27 | stylesheetInput: '../project/',
28 | spritePath: mix.config.paths.imagesResultPath,
29 | smartUpdate: typeof mix.config.needsLazyspriteSmartUpdate !== 'undefined' ? mix.config.needsLazyspriteSmartUpdate : true,
30 | nameSpace: `${mix.config.prefix}_`,
31 | retinaInfix: '_',
32 | outputExtralCSS: true
33 | };
34 | const svgSpriteConfig = {
35 | imagePath: mix.config.paths.imagesSourcePath,
36 | spriteOutput: mix.config.paths.imagesResultPath,
37 | styleOutput: mix.config.paths.styleResultPath,
38 | nameSpace: `${mix.config.prefix}_`
39 | };
40 | const styleResultPath = mix.config.paths.styleResultPath;
41 | if (argv.debug) {
42 | lazySpriteConfig.logLevel = 'debug';
43 | }
44 |
45 | const sassTaskName = 'sass';
46 | const sassWithCacheTaskName = 'sassWithCache';
47 |
48 | const sassOptionWithCache = () => ({since: gulp.lastRun(sassWithCacheTaskName)});
49 |
50 | const sassHandle = (options) => {
51 | options = options || (() => ({}));
52 | return () =>
53 | gulp.src('../project/**/*.scss', options())
54 | .pipe(mix.plugins.sassInheritance({base: '../project/'}))
55 | .pipe(mix.plugins.if(Boolean(argv.debug), mix.plugins.debug({title: 'Sass Debug:'})))
56 | .pipe(mix.plugins.if(mix.config.needsSourceMaps, mix.plugins.sourcemaps.init()))
57 | .pipe(mix.plugins.dartSass({
58 | errLogToConsole: true,
59 | indentWidth: 4,
60 | precision: 6,
61 | outputStyle: 'expanded'
62 | }).on('error', mix.plugins.dartSass.logError))
63 | .pipe(mix.plugins.postcss([lazysprite(lazySpriteConfig), svgSprite(svgSpriteConfig), autoprefixer({
64 | browsers: ['defaults', 'last 5 versions', '> 5% in CN', 'not ie <= 8', 'iOS > 8']
65 | })]))
66 | .pipe(mix.plugins.if(mix.config.needsSourceMaps, mix.plugins.sourcemaps.write('./maps'))) // Source Maps 的 Base 输出目录为 style 输出的目录
67 | .pipe(gulp.dest(styleResultPath))
68 | };
69 |
70 | gulp.task(sassWithCacheTaskName, sassHandle(sassOptionWithCache));
71 |
72 | gulp.task(sassTaskName, sassHandle());
73 |
74 | // 任务说明
75 | mix.addTaskDescription(sassTaskName, '进行 Sass 编译以及雪碧图处理(框架自带 Watch 机制监听 Sass 和图片变化后自行编译,不建议手工调用本方法)');
76 | };
77 |
--------------------------------------------------------------------------------
/workflow/basicTasks/server.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // server 监视文件改动并重新载入
17 | module.exports = (gulp, mix) => {
18 |
19 | gulp.task('server', (done) => {
20 |
21 | const showLog = () => {
22 | if (mix.config.browserSync.browserSyncShowLog) {
23 | return 'info';
24 | }
25 | return 'silent';
26 | };
27 |
28 | mix.browserSync.init({
29 | server: {
30 | // 静态路径根目录
31 | baseDir: mix.config.paths.htmlResultPath,
32 | // 设置路由
33 | routes: mix.config.browserSync.browserSyncServerRoute
34 | },
35 | logLevel: showLog(),
36 | logPrefix: mix.util.addColor(mix.timeFormat.getCurrentTime(), 'info'),
37 | startPath: mix.config.browserSync.browserSyncStartPath,
38 | port: mix.config.browserSync.browserSyncPort
39 | });
40 | gulp.watch(mix.config.browserSync.browserSyncWatchPath).on('all', mix.reload);
41 |
42 | done();
43 | });
44 | };
45 |
--------------------------------------------------------------------------------
/workflow/basicTasks/version.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 显示 QMUI Web 的版本号
17 | module.exports = (gulp, mix) => {
18 |
19 | const taskName = 'version';
20 |
21 | gulp.task(taskName, (done) => {
22 | mix.util.log('当前项目运行的 QMUI Web 版本号: ' + mix.util.colors.green(mix.packageInfo.version));
23 | done();
24 | });
25 |
26 | // 任务说明
27 | mix.addTaskDescription(taskName, '显示 QMUI Web 的版本信息');
28 | };
29 |
--------------------------------------------------------------------------------
/workflow/calculateMargin.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 | /**
16 | * This file was adapted from chmontgomery's gulp-help.
17 | * The original file can be found at: https://github.com/chmontgomery/gulp-help/blob/master/lib/calculate-margin.js.
18 | *
19 | * @param {object} tasksObj - common.tasks
20 | *
21 | * returns:
22 | * margin - length of longest basicTasks / options name
23 | * hasOptions - true if any basicTasks has option(s)
24 | *
25 | * @returns {{margin: number, hasOptions: boolean}} 返回一个合适的缩进距离。
26 | */
27 |
28 | module.exports = (tasksObj) => {
29 | let hasOptions = false;
30 | const margin = Object.keys(tasksObj).reduce((maxTaskMargin, taskName) => {
31 | let optionsMargin = 0;
32 | let opts;
33 | // if exists, iterate options list to calculate margin for options
34 | if (tasksObj[taskName] && tasksObj[taskName].options) {
35 | const help = tasksObj[taskName] || {options: {}};
36 | opts = Object.keys(help.options).sort();
37 | optionsMargin = opts.reduce((maxOptionMargin, opt) => {
38 | // if, at any time while iterating the tasks array, we also iterate an opts array, set hasOptions flag
39 | hasOptions = true;
40 | return maxOptionMargin > opt.length ? maxOptionMargin : opt.length;
41 | }, 0);
42 | }
43 |
44 | if (!tasksObj[taskName] || maxTaskMargin > taskName.length && maxTaskMargin > optionsMargin) {
45 | return maxTaskMargin;
46 | } else if (optionsMargin > taskName.length) {
47 | return optionsMargin;
48 | }
49 | return taskName.length;
50 | }, 0);
51 | return {
52 | margin: margin,
53 | hasOptions: hasOptions
54 | };
55 | };
56 |
--------------------------------------------------------------------------------
/workflow/initProject.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 创建一个新项目
17 | const fs = require('fs');
18 | const upperFirst = require('lodash/upperFirst');
19 | const mkdirp = require('mkdirp');
20 | const path = require('path');
21 | const os = require('os');
22 |
23 | module.exports = (gulp, mix) => {
24 |
25 | gulp.task('initProject', (done) => {
26 | /**
27 | * 创建一个新项目
28 | * 第一步:获取 Project 文件夹中的基本目录结构和公共通用组件并持有它们,但排除了主 scss 文件 demo.scss
29 | * 第二步:修改持有文件中的 qui_ 前缀为新项目的前缀,新前缀值从 qmui.config.js 中读取;
30 | * 第三步:修改持有文件内容注释中的日期为创建项目时的日期;
31 | * 第四步:修改持有文件内容注释中的作者为执行创建项目命令的人(名称从系统账户用户名中获取);
32 | * 第五步:把这些持有的文件复制到上一层目录;
33 | * 第六步:获取主 scss 文件 demo.scss ,并更新其中的 _qmui.scss 的引用路径(因为 demo.scss 被复制到上一层);
34 | * 第七步:重命名 demo.scss,新名称从 qmui.config.js 中读取;
35 | * 第八步:把 demo.scss 复制到上一层目录;
36 | * 第九步:按配置表创建图片目录;
37 | * 第十步:执行 Sass 编译任务,打开浏览器,并打开新复制的 demo.html;
38 | */
39 |
40 | // 需要遍历的文件
41 | const sourceArr = ['project/**/*'];
42 | // 额外排除 demo.scss,后面单独重命名再拷贝
43 | sourceArr.push('!project/demo.scss');
44 | sourceArr.push('!project/_var.scss');
45 |
46 | // 获取当天的日期,并统一格式为 'yyyy-mm-dd',替换掉 demo 注释中的文件创建日期
47 | // gulp-replace 的正则引擎似乎对 $ 和 ^ 不支持,只能忽略开头和结尾的判断
48 | const dateRegex = /[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))/g;
49 | const currentDate = new Date();
50 | const currentYear = currentDate.getFullYear();
51 | const currentMonth = mix.timeFormat.checkDateFormat(currentDate.getMonth() + 1);
52 | const currentDay = mix.timeFormat.checkDateFormat(currentDate.getDate());
53 | const formattingDate = `${currentYear}-${currentMonth}-${currentDay}`;
54 | const targetQMUIStylePath = `../${path.resolve('.').replace(/\\/g, '/').split('/').pop()}/qmui/_qmui`;
55 | const targetQMUICalculatePath = `../${path.resolve('.').replace(/\\/g, '/').split('/').pop()}/qmui/mixin/tool/_calculate`;
56 | const authorName = upperFirst(path.basename(os.homedir()));
57 |
58 | // 执行创建项目的任务
59 | gulp.src(sourceArr)
60 | .pipe(mix.plugins.replace('qui_', mix.config.prefix + '_'))
61 | .pipe(mix.plugins.replace(dateRegex, formattingDate))
62 | .pipe(mix.plugins.replace(/@author .*([\r\n])/, '@author ' + authorName + '$1'))
63 | .pipe(gulp.dest('../project'));
64 |
65 | gulp.src(['project/demo.scss'])
66 | .pipe(mix.plugins.replace('../qmui/_qmui', targetQMUIStylePath))
67 | .pipe(mix.plugins.replace('demo.scss', mix.config.resultCssFileName))
68 | .pipe(mix.plugins.replace(dateRegex, formattingDate))
69 | .pipe(mix.plugins.replace(/@author .*([\r\n])/, '@author ' + authorName + '$1'))
70 | .pipe(mix.plugins.rename(mix.config.resultCssFileName))
71 | .pipe(gulp.dest('../project'));
72 |
73 | gulp.src(['project/_var.scss'])
74 | .pipe(mix.plugins.replace('../qmui/mixin/tool/_calculate', targetQMUICalculatePath))
75 | .pipe(mix.plugins.replace(dateRegex, formattingDate))
76 | .pipe(mix.plugins.replace(/@author .*([\r\n])/, '@author ' + authorName + '$1'))
77 | .pipe(gulp.dest('../project'));
78 |
79 | // 创建公共图片目录
80 | if (!fs.existsSync(mix.config.paths.imagesSourcePath)) {
81 | mkdirp(mix.config.paths.imagesSourcePath);
82 | }
83 |
84 | // 创建独立图片目录
85 | const independentImagesSourcePath = mix.config.paths.imagesSourcePath + mix.config.paths.independentImagesDirectory;
86 | if (!fs.existsSync(independentImagesSourcePath)) {
87 | mkdirp(independentImagesSourcePath);
88 | }
89 |
90 | mix.util.log('Create Project', '项目创建完毕,接下来会按配置执行一次 Default Task');
91 |
92 | done();
93 | });
94 |
95 | // 执行创建新项目任务
96 | const taskName = 'init';
97 |
98 | gulp.task(taskName, gulp.series('initProject', 'default'));
99 |
100 | // 任务说明
101 | mix.addTaskDescription(taskName, '创建一个新项目');
102 | };
103 |
--------------------------------------------------------------------------------
/workflow/start.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // Gulp 服务入口
17 | const argv = require('yargs').argv;
18 | const spawn = require('child_process').spawn;
19 | const os = require('os');
20 |
21 | module.exports = (gulp, mix) => {
22 |
23 | // 判断 browserSync 的值是否正确
24 | if (mix.config.browserSync.browserSyncMod !== 'server' && mix.config.browserSync.browserSyncMod !== 'proxy' && mix.config.browserSync.browserSyncMod !== 'close') {
25 | gulp.task('main', (done) => {
26 | mix.util.error('Config', `Config 中的 browserSyncMod 仅支持 ${mix.plugins.util.colors.yellow('server')}, ${mix.plugins.util.colors.yellow('proxy')}, ${mix.plugins.util.colors.yellow('close')} 三个值`);
27 | done();
28 | });
29 | } else {
30 | // 常规启动任务
31 | const mainTasks = ['include', 'sass', 'watch'];
32 |
33 | // 根据 broserSync 的类型加入对应的任务
34 | if (mix.config.browserSync.browserSyncMod === 'server' || mix.config.browserSync.browserSyncMod === 'proxy') {
35 | mainTasks.push(mix.config.browserSync.browserSyncMod);
36 | }
37 |
38 | // 加入用户自定义任务
39 | if (mix.config.customTasks) {
40 | Object.keys(mix.config.customTasks).forEach((customTaskName) => {
41 | mainTasks.push(customTaskName);
42 | });
43 | }
44 |
45 | gulp.task('main', gulp.series(mainTasks));
46 | }
47 |
48 | const taskName = 'default';
49 |
50 | if (os.platform() === 'linux' || os.platform() === 'darwin') {
51 |
52 | gulp.task('start', (done) => {
53 | if (argv.debug) {
54 | mix.util.log('Debug: ', 'QMUI 进入 Debug 模式');
55 | }
56 |
57 | let mainTaskProcess; // 记录当前 gulp 运行时的进程
58 |
59 | const restart = () => {
60 | if (mainTaskProcess) {
61 | mainTaskProcess.kill();
62 | }
63 |
64 | const mainTask = ['main'];
65 | if (typeof argv.color !== 'undefined' && !argv.color) {
66 | mainTask.push('--no-color');
67 | }
68 | mainTaskProcess = spawn('gulp', mainTask, {stdio: 'inherit'});
69 | };
70 |
71 | gulp.watch('package.json').on('all', () => {
72 | mix.util.log('');
73 | mix.util.warn('Update', '检测到 QMUI Web 的 npm 包,为了避免出现错误,建议你停止目前的 gulp,请使用 npm install 命令更新后再启动 gulp');
74 | mix.util.beep(3);
75 | });
76 |
77 | gulp.watch(['gulpfile.js', 'workflow', 'workflow/**/*']).on('all', () => {
78 | mix.util.log('');
79 | if (argv.debug) {
80 | mix.util.warn('Debug', '目前为 Debug 模式,检测到工作流源码有被更新,将自动重启 gulp');
81 | mix.util.beep(3);
82 | restart();
83 | } else {
84 | mix.util.warn('Update', '检测到工作流源码有被更新,建议你停止目前的 gulp 任务,再重新启动 gulp,以载入最新的代码。如果 npm 包也需要更新,请先更新 npm 包再重启 gulp');
85 | mix.util.beep(3);
86 |
87 | }
88 | });
89 |
90 | // 获取第一次进入时 gulp 的进程
91 | const mainTask = ['main'];
92 | if (argv.debug) {
93 | mainTask.push('--debug');
94 | }
95 | if (typeof argv.color !== 'undefined' && !argv.color) {
96 | mainTask.push('--no-color');
97 | }
98 | mainTaskProcess = spawn('gulp', mainTask, {stdio: 'inherit'});
99 |
100 | done();
101 | });
102 |
103 | // 默认任务
104 | gulp.task(taskName, gulp.parallel('start'));
105 | } else {
106 | gulp.task(taskName, gulp.parallel('main'));
107 | }
108 |
109 | // 任务说明
110 | mix.addTaskDescription(taskName, '默认任务,自动执行一次 include 和 sass 任务,并调用 watch 任务', {
111 | 'debug': 'debug 模式下 gulpfile.js 有变动时会自动重启 default 任务'
112 | });
113 | };
114 |
--------------------------------------------------------------------------------
/workflow/watch.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Tencent is pleased to support the open source community by making QMUI Web available.
3 | * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
4 | * Licensed under the MIT License (the "License"); you may not use this file except in compliance
5 | * with the License. You may obtain a copy of the License at
6 | *
7 | * http://opensource.org/licenses/MIT
8 | *
9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is
10 | * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
11 | * either express or implied. See the License for the specific language governing permissions and
12 | * limitations under the License.
13 | */
14 |
15 |
16 | // 文件监控
17 | const path = require('path');
18 | const pngquant = require('imagemin-pngquant');
19 | const md5 = require('js-md5');
20 |
21 | // 逻辑变量
22 | let justAddedImage = [],
23 | justBeforeAddedImage = []; // 记录压缩的图片
24 |
25 | module.exports = (gulp, mix) => {
26 |
27 | const taskName = 'watch';
28 |
29 | gulp.task(taskName, (done) => {
30 |
31 | mix.util.log('Watch', 'QMUI 进入自动监听');
32 |
33 | // 图片管理(图片文件夹操作同步以及图片文件自动压缩)
34 |
35 | // 公共方法
36 | const imageMinOnSameDir = (dir) => {
37 | gulp.src(dir)
38 | .pipe(mix.plugins.plumber({
39 | errorHandler: (error) => {
40 | mix.util.error('Min Image', error);
41 | mix.util.beep();
42 | }
43 | }))
44 | .pipe(mix.plugins.imagemin({
45 | progressive: true,
46 | svgoPlugins: [{removeViewBox: false}],
47 | use: [pngquant()]
48 | }))
49 | .pipe(gulp.dest(path.dirname(dir)));
50 | };
51 |
52 | // 独立图片部分
53 |
54 | // 自动同步独立图片文件夹的操作
55 | const independentImagesSourcePath = mix.config.paths.imagesSourcePath + mix.config.paths.independentImagesDirectory;
56 | const independentImagesResultPath = mix.config.paths.imagesResultPath + mix.config.paths.independentImagesDirectory;
57 | let shouldOutputEmptyLineForSyncImage;
58 |
59 | // 如果有需要,则在执行同步图片任务之前输出一个空行
60 | const outputEmptyForSyncImageIfNeed = () => {
61 | if (shouldOutputEmptyLineForSyncImage) {
62 | mix.util.log('');
63 | shouldOutputEmptyLineForSyncImage = false;
64 | }
65 | };
66 |
67 | const independentImages = (independentDone) => {
68 | shouldOutputEmptyLineForSyncImage = true;
69 | mix.plugins.fileSync(independentImagesSourcePath, independentImagesResultPath, {
70 | ignore: ['.DS_Store', '.svn', '.git'],
71 | beforeAddFileCallback (_fullPathSrc) {
72 | const absoluteMinImageFilePath = path.resolve(_fullPathSrc);
73 | const absoluteMinImageFilePathMd5 = md5(absoluteMinImageFilePath);
74 |
75 | if (!justBeforeAddedImage.includes(absoluteMinImageFilePathMd5)) {
76 | // 这里为了避免发生“增加图片到 public/images 或修改 public/images 原有的图片,触发压缩图片,因此图片又被修改,再次触发压缩图片”的情况发生,
77 | // 做了一个判断,压缩一张图片时会标记下来,当再次发生图片改变时会判断这张图片是否为刚刚压缩过的图片,如果是则不执行该次压缩图片的逻辑
78 | // 如果不是,则说明准备处理另一张图片了,这时清空标记,进入下一张图片的处理
79 | justBeforeAddedImage.push(absoluteMinImageFilePathMd5);
80 | outputEmptyForSyncImageIfNeed();
81 | mix.util.log('Min Image', `对 ${absoluteMinImageFilePath} 进行图片压缩`);
82 | imageMinOnSameDir(absoluteMinImageFilePath);
83 | }
84 | },
85 | beforeUpdateFileCallback (_fullPathSrc) {
86 | const absoluteMinImageFilePath = path.resolve(_fullPathSrc);
87 | const absoluteMinImageFilePathMd5 = md5(absoluteMinImageFilePath);
88 |
89 | if (!justBeforeAddedImage.includes(absoluteMinImageFilePathMd5)) {
90 | justBeforeAddedImage.push(absoluteMinImageFilePathMd5);
91 | outputEmptyForSyncImageIfNeed();
92 | mix.util.log('Min Image', `对 ${absoluteMinImageFilePath} 进行图片压缩`);
93 | imageMinOnSameDir(absoluteMinImageFilePath);
94 | } else {
95 | justBeforeAddedImage = justBeforeAddedImage.filter((item) => item !== absoluteMinImageFilePathMd5);
96 | }
97 | },
98 | addFileCallback (_fullPathSrc, _fullPathDist) {
99 | const absoluteMinImageFilePath = path.resolve(_fullPathDist);
100 | const absoluteMinImageFilePathMd5 = md5(absoluteMinImageFilePath);
101 |
102 | if (!justAddedImage.includes(absoluteMinImageFilePathMd5)) {
103 | justAddedImage.push(absoluteMinImageFilePathMd5);
104 | outputEmptyForSyncImageIfNeed();
105 | mix.util.log('Sync Image', `同步增加文件到 ${absoluteMinImageFilePath}`);
106 | }
107 | },
108 | deleteFileCallback (_fullPathSrc, _fullPathDist) {
109 | outputEmptyForSyncImageIfNeed();
110 | mix.util.log('Sync Image', `同步删除文件 ${path.resolve(_fullPathDist)}`);
111 | },
112 | updateFileCallback (_fullPathSrc, _fullPathDist) {
113 | const absoluteMinImageFilePath = path.resolve(_fullPathDist);
114 | const absoluteMinImageFilePathMd5 = md5(absoluteMinImageFilePath);
115 |
116 | if (!justAddedImage.includes(absoluteMinImageFilePathMd5)) {
117 | justAddedImage.push(absoluteMinImageFilePathMd5);
118 | outputEmptyForSyncImageIfNeed();
119 | mix.util.log('Sync Image', `同步更新文件到 ${absoluteMinImageFilePath}`);
120 | } else {
121 | justAddedImage = justAddedImage.filter((item) => item !== absoluteMinImageFilePathMd5);
122 | }
123 | }
124 | });
125 |
126 | independentDone();
127 | };
128 |
129 | if (mix.config.needsImagesMinAndSync) {
130 | // 压缩独立图片并同步独立图片到 public 目录
131 | gulp.watch([independentImagesSourcePath, `${independentImagesSourcePath}/**/*`], gulp.series(independentImages));
132 | }
133 |
134 | // 雪碧图与样式处理
135 | // 监控雪碧图原图和样式,如果有改动,会触发样式编译以及雪碧图生成
136 |
137 | // 普通雪碧图与样式监听
138 | const styleWatchFiles = ['../project/**/*.scss'];
139 | const styleWatchTasks = ['sassWithCache'];
140 | if (mix.config.browserSync.browserSyncMod === 'server' || mix.config.browserSync.browserSyncMod === 'proxy') {
141 | styleWatchTasks.push('reload');
142 | }
143 | const styleWatch = gulp.watch(styleWatchFiles, gulp.series(styleWatchTasks));
144 | styleWatch.on('all', () => {
145 | mix.util.log('');
146 | mix.util.log('Sass', '进行样式编译');
147 | });
148 |
149 | const imageWatchFiles = [`${mix.config.paths.imagesSourcePath}/*/*.*`, `!${independentImagesSourcePath}`, `!${independentImagesSourcePath}**/*`];
150 | const imageSpriteTasks = ['sass'];
151 | if (mix.config.browserSync.browserSyncMod === 'server' || mix.config.browserSync.browserSyncMod === 'proxy') {
152 | imageSpriteTasks.push('reload');
153 | }
154 | const imageSpriteWatch = gulp.watch(imageWatchFiles, gulp.series(imageSpriteTasks));
155 | imageSpriteWatch.on('all', () => {
156 | mix.util.log('');
157 | mix.util.log('Sass', '进行样式编译');
158 | });
159 |
160 | // 压缩雪碧图
161 | if (mix.config.needsImagesMinAndSync) {
162 | const minImageWatcher = gulp.watch(mix.config.paths.imagesResultPath + '/*.*');
163 | minImageWatcher.on('all', (event, filePath) => {
164 | const minImageFile = filePath;
165 | const minImageFilePathMd5 = md5(minImageFile);
166 | // 这里为了避免发生“增加图片到 public/images 或修改 public/images 原有的图片,触发压缩图片,因此图片又被修改,再次触发压缩图片”的情况发生,
167 | // 做了一个判断,压缩一张图片时会标记下来,当再次发生图片改变时会判断这张图片是否为刚刚压缩过的图片,如果是则不执行该次压缩图片的逻辑
168 | // 如果不是,则说明准备处理另一张图片了,这时清空标记,进入下一张图片的处理
169 | if (event !== 'unlink' && !justAddedImage.includes(minImageFilePathMd5)) {
170 |
171 | justAddedImage.push(minImageFilePathMd5);
172 |
173 | mix.util.log('Min Image', `对 ${minImageFile} 进行图片压缩`);
174 | imageMinOnSameDir(minImageFile);
175 |
176 | } else if (justAddedImage.includes(minImageFilePathMd5)) {
177 | justAddedImage = justAddedImage.filter((item) => item !== minImageFilePathMd5);
178 | }
179 | });
180 | }
181 |
182 | // 模板自动 include
183 | if (mix.config.template.openIncludeFunction) {
184 | const includeWatcher = gulp.watch(mix.config.paths.htmlSourcePath, gulp.series('include'));
185 | includeWatcher.on('all', (event, filePath) => {
186 | mix.util.log('');
187 | mix.util.log('Include', 'Template ' + filePath + ' was ' + event);
188 | });
189 | }
190 |
191 | done();
192 | });
193 |
194 | // 任务说明
195 | mix.addTaskDescription(taskName, '文件监控,自动执行基本的工作流,包括 Sass 自动编译,雪碧图处理,模板 include 自动编译,图片文件夹操作同步以及图片文件自动压缩');
196 | };
197 |
--------------------------------------------------------------------------------