├── .babelrc
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── docs
└── navigation-bar.gif
├── gulpfile.js
├── package.json
├── src
├── index.js
├── index.json
├── index.wxml
└── index.wxss
├── test
└── utils.js
└── tools
├── build.js
├── checkcomponents.js
├── config.js
├── demo
├── app.js
├── app.json
├── app.wxss
├── package.json
├── pages
│ ├── demo1
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo10
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo2
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo3
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo4
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo5
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo6
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo7
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo8
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ ├── demo9
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
│ └── index
│ │ ├── index.js
│ │ ├── index.json
│ │ ├── index.wxml
│ │ └── index.wxss
└── project.config.json
├── test
└── helper.js
└── utils.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | ["module-resolver", {
4 | "root": ["./src"],
5 | "alias": {}
6 | }]
7 | ],
8 | "presets": [
9 | ["env", {"loose": true, "modules": "commonjs"}]
10 | ]
11 | }
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'extends': [
3 | 'airbnb-base',
4 | 'plugin:promise/recommended'
5 | ],
6 | 'parserOptions': {
7 | 'ecmaVersion': 9,
8 | 'ecmaFeatures': {
9 | 'jsx': false
10 | },
11 | 'sourceType': 'module'
12 | },
13 | 'env': {
14 | 'es6': true,
15 | 'node': true,
16 | 'jest': true
17 | },
18 | 'plugins': [
19 | 'import',
20 | 'node',
21 | 'promise'
22 | ],
23 | 'rules': {
24 | 'arrow-parens': 'off',
25 | 'comma-dangle': [
26 | 'error',
27 | 'only-multiline'
28 | ],
29 | 'complexity': ['error', 10],
30 | 'func-names': 'off',
31 | 'global-require': 'off',
32 | 'handle-callback-err': [
33 | 'error',
34 | '^(err|error)$'
35 | ],
36 | 'import/no-unresolved': [
37 | 'error',
38 | {
39 | 'caseSensitive': true,
40 | 'commonjs': true,
41 | 'ignore': ['^[^.]']
42 | }
43 | ],
44 | 'import/prefer-default-export': 'off',
45 | 'linebreak-style': 'off',
46 | 'no-catch-shadow': 'error',
47 | 'no-continue': 'off',
48 | 'no-div-regex': 'warn',
49 | 'no-else-return': 'off',
50 | 'no-param-reassign': 'off',
51 | 'no-plusplus': 'off',
52 | 'no-shadow': 'off',
53 | 'no-multi-assign': 'off',
54 | 'no-underscore-dangle': 'off',
55 | 'node/no-deprecated-api': 'error',
56 | 'node/process-exit-as-throw': 'error',
57 | 'object-curly-spacing': [
58 | 'error',
59 | 'never'
60 | ],
61 | 'operator-linebreak': [
62 | 'error',
63 | 'after',
64 | {
65 | 'overrides': {
66 | ':': 'before',
67 | '?': 'before'
68 | }
69 | }
70 | ],
71 | 'prefer-arrow-callback': 'off',
72 | 'prefer-destructuring': 'off',
73 | 'prefer-template': 'off',
74 | 'quote-props': [
75 | 1,
76 | 'as-needed',
77 | {
78 | 'unnecessary': true
79 | }
80 | ],
81 | 'semi': [
82 | 'error',
83 | 'never'
84 | ]
85 | },
86 | 'globals': {
87 | 'window': true,
88 | 'document': true,
89 | 'App': true,
90 | 'Page': true,
91 | 'Component': true,
92 | 'Behavior': true,
93 | 'wx': true,
94 | 'getApp': true,
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | package-lock.json
4 |
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | miniprogram_dist
12 | miniprogram_dev
13 | node_modules
14 | coverage
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .DS_Store
3 | package-lock.json
4 |
5 | logs
6 | *.log
7 | npm-debug.log*
8 | yarn-debug.log*
9 | yarn-error.log*
10 |
11 | test
12 | tools
13 | docs
14 | miniprogram_dev
15 | node_modules
16 | coverage
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 wechat-miniprogram
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 小程序组件 navigation-bar
2 |
3 | 小程序自定义导航栏适配 完美解决内容上下不居中问题
4 |
5 | ## Navigation
6 |
7 | Navigation 是小程序的顶部导航组件,当页面配置 navigationStyle 设置为 custom 的时候可以使用此组件替代原生导航栏
8 |
9 | ## 引入组件
10 |
11 | ### 方式一 (建议)
12 |
13 | ```bash
14 | npm install miniprograms-navigation-bar --save
15 | ```
16 |
17 | - 点击开发者工具中的菜单栏:工具 --> 构建 npm
18 | 
19 | - 勾选“使用 npm 模块”选项
20 | 
21 | 在 page.json 中引入组件
22 |
23 | ```bash
24 | {
25 | "usingComponents": {
26 | "navBar": "miniprograms-navigation-bar"
27 | }
28 | }
29 | ```
30 |
31 | > 备注:如发现一些编译问题,请重启开发工具试试
32 |
33 | ### 方式二
34 |
35 | 复制代码过去,在 page.json 中引入组件,注意引入的路径
36 |
37 | ```bash
38 | {
39 | "usingComponents": {
40 | "navBar": "/components/index"
41 | }
42 | }
43 | ```
44 |
45 | ## 示例代码
46 |
47 | ```bash
48 |
49 |
50 | ```
51 |
52 | ## 属性列表
53 |
54 | | 属性 | 类型 | 默认值 | 必填 | 说明 |
55 | | ------------------ | ------------ | ---------- | ---- | ----------------------------------------------------------------------------------------- |
56 | | ext-class | string | | 否 | 添加在组件内部结构的 class,可用于修改组件内部的样式 |
57 | | title | string | | 否 | 导航标题,如果不提供,则名为 center 的 slot 有效 |
58 | | background | string | #ffffff | 否 | 导航背景色 |
59 | | backgroundColorTop | string | background | 否 | 导航下拉下方背景色,详细参考下方 |
60 | | color | string | #000000 | 否 | 导航字体颜色 |
61 | | iconTheme | string | black | 否 | 主题图标和字体颜色,当背景色为深色时,可以设置'white' |
62 | | back | boolean | false | 否 | 是否显示返回按钮,默认点击按钮会执行 navigateBack,如果为 false,则名为 left 的 slot 有效 |
63 | | home | boolean | false | 否 | 是否显示 home 按钮,执行方法自定义,或者看例子 |
64 | | searchBar | boolean | false | 否 | 是否显示搜索框,默认点击按钮会执行 bindsearch,如果为 false,则名为 center 的 slot 有效 |
65 | | searchText | string | 点我搜索 | 否 | 搜索框文字 |
66 | | bindhome | eventhandler | | 否 | 在 home 为 true 时,点击 home 按钮触发此事件 |
67 | | bindback | venthandler | | 否 | 在 back 为 true 时,点击 back 按钮触发此事件,detail 包含 delta |
68 | | bindsearch | eventhandler | | 否 | 在 searchBar 为 true 时,点击 search 按钮触发此事件 |
69 |
70 | 注:backgroundColorTop[见 issue 问题](https://github.com/lingxiaoyi/Taro-navigation-bar/issues/15)
71 |
72 | ## Slot
73 |
74 | | 名称 | 描述 |
75 | | ------ | ------------------------------------------------------------ |
76 | | left | 左侧 slot,在 back 按钮位置显示,当 back 为 false 的时候有效 |
77 | | center | 标题 slot,在标题位置显示,当 title 为空的时候有效 |
78 | | right | 在导航的右侧显示 |
79 |
80 | ## 注意
81 |
82 | - iconTheme 设置为 white 的时候,一定要记得自己去 json 文件设置"navigationBarTextStyle": "white"
83 | - 跳转搜索页面,在 Android 机子会出现文字被键盘弹起顶出 input 框,解决方案页面设置一个死的高度不要高于 windowHeight - navheight 例子中是设置 500px
84 | - input 框文字抖动问题我是借鉴别人写的,可以最大限度减小文字抖动的大小,提升用户体验
85 | - title searchBar slot="right" 如果全部有内容,是这样的先后显示顺序.
86 | - 默认配置满足不了功能的,请使用 slot 功能,见例子 1 6 7
87 | - 由于本人精力有限,只测试了常规的 20 多款手机.如有哪种机型出现问题,请备注机型和小程序版本库.本人会以最快方式解决问题.
88 | - 有什么 bug 和建议,还有功能上的问题请提 pr
89 |
90 | ## 后续
91 |
92 | - 其他功能,规划中,或者留言联系方式微信 zhijunxh
93 | - 还需要其他样子的例子请留言,如果功能比较重要和主流的话,我会考虑第一时间添加
94 |
95 | ## 备注
96 |
97 | - 渐变和动态修改背景色请参考例子 10,ios 机子会出现滚动渐变颜色加载出来不能消失的问题,暂时没想到解决办法,这是微信浏览器渲染的问题,社区里面相关问题[地址](https://developers.weixin.qq.com/community/develop/doc/0000acae7649d80541b896ca957000)
98 | - getMenuButtonBoundingClientRect 胶囊按钮信息获取不到或者此方法报错问题已修复 [获取菜单报错,需要捕捉异常和兼容版本](https://github.com/lingxiaoyi/navigation-bar/issues/14)[导航栏渲染不出来](https://github.com/lingxiaoyi/navigation-bar/issues/13)
99 |
100 | ## 测试信息
101 |
102 | | 手机型号 | 胶囊位置信息 | statusBarHeight | 测试情况 |
103 | | ---------------------- | ------------------- | :-------------: | -------- |
104 | | iPhoneX | 80 32 281 369 48 88 | 44 | 通过 |
105 | | iPhone8 plus | 56 32 320 408 24 88 | 20 | 通过 |
106 | | iphone7 | 56 32 281 368 24 87 | 20 | 通过 |
107 | | iPhone6 plus | 56 32 320 408 24 88 | 20 | 通过 |
108 | | iPhone6 | 56 32 281 368 24 87 | 20 | 通过 |
109 | | HUAWEI SLA-AL00 | 64 32 254 350 32 96 | 24 | 通过 |
110 | | HUAWEI VTR-AL00 | 64 32 254 350 32 96 | 24 | 通过 |
111 | | HUAWEI EVA-AL00 | 64 32 254 350 32 96 | 24 | 通过 |
112 | | HUAWEI EML-AL00 | 68 32 254 350 36 96 | 29 | 通过 |
113 | | HUAWEI VOG-AL00 | 65 32 254 350 33 96 | 25 | 通过 |
114 | | HUAWEI ATU-TL10 | 64 32 254 350 32 96 | 24 | 通过 |
115 | | HUAWEI SMARTISAN OS105 | 64 32 326 422 32 96 | 24 | 通过 |
116 | | XIAOMI MI6 | 59 28 265 352 31 87 | 23 | 通过 |
117 | | XIAOMI MI4LTE | 60 32 254 350 28 96 | 20 | 通过 |
118 | | XIAOMI MIX3 | 74 32 287 383 42 96 | 35 | 通过 |
119 | | REDMI NOTE3 | 64 32 254 350 32 96 | 24 | 通过 |
120 | | REDMI NOTE4 | 64 32 254 350 32 96 | 24 | 通过 |
121 | | REDMI NOTE3 | 55 28 255 351 27 96 | 20 | 通过 |
122 | | REDMI 5plus | 67 32 287 383 35 96 | 28 | 通过 |
123 | | MEIZU M571C | 65 32 254 350 33 96 | 25 | 通过 |
124 | | MEIZU M6 NOTE | 62 32 254 350 30 96 | 22 | 通过 |
125 | | MEIZU MX4 PRO | 62 32 278 374 30 96 | 22 | 通过 |
126 | | OPPO A33 | 65 32 254 350 33 96 | 26 | 通过 |
127 | | OPPO R11 | 58 32 254 350 26 96 | 18 | 通过 |
128 | | VIVO Y55 | 64 32 254 350 32 96 | 24 | 通过 |
129 | | HONOR BLN-AL20 | 64 32 254 350 32 96 | 24 | 通过 |
130 | | HONOR NEM-AL10 | 59 28 265 352 31 87 | 24 | 通过 |
131 | | HONOR BND-AL10 | 64 32 254 350 32 96 | 24 | 通过 |
132 | | HONOR duk-al20 | 64 32 254 350 32 96 | 24 | 通过 |
133 | | SAMSUNG SM-G9550 | 64 32 305 401 32 96 | 24 | 通过 |
134 | | 360 1801-A01 | 64 32 254 350 32 96 | 24 | 通过 |
135 |
136 | ~
137 | 创作不易,如果对你有帮助,请给个星星 star✨✨ 谢谢
138 | ~
139 |
--------------------------------------------------------------------------------
/docs/navigation-bar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/docs/navigation-bar.gif
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const clean = require('gulp-clean');
3 |
4 | const config = require('./tools/config');
5 | const BuildTask = require('./tools/build');
6 | const id = require('./package.json').name || 'miniprogram-custom-component';
7 |
8 | // build task instance
9 | new BuildTask(id, config.entry);
10 |
11 | // clean the generated folders and files
12 | gulp.task('clean', gulp.series(() => {
13 | return gulp.src(config.distPath, { read: false, allowEmpty: true })
14 | .pipe(clean())
15 | }, done => {
16 | if (config.isDev) {
17 | return gulp.src(config.demoDist, { read: false, allowEmpty: true })
18 | .pipe(clean());
19 | }
20 |
21 | done();
22 | }));
23 | // watch files and build
24 | gulp.task('watch', gulp.series(`${id}-watch`));
25 | // build for develop
26 | gulp.task('dev', gulp.series(`${id}-dev`));
27 | // build for publish
28 | gulp.task('default', gulp.series(`${id}-default`));
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "miniprograms-navigation-bar",
3 | "version": "2.0.1",
4 | "description": "miniprogram custom component 可自定义返回事件 返回home 搜索框 自定义左中右",
5 | "main": "miniprogram_dist/index.js",
6 | "scripts": {
7 | "dev": "gulp dev --develop",
8 | "watch": "gulp watch --develop --watch",
9 | "build": "gulp",
10 | "dist": "npm run build",
11 | "clean-dev": "gulp clean --develop",
12 | "clean": "gulp clean",
13 | "test": "jest ./test/* --silent --bail",
14 | "coverage": "jest ./test/* --coverage --bail",
15 | "lint": "eslint \"src/**/*.js\"",
16 | "lint-tools": "eslint \"tools/**/*.js\" --rule \"import/no-extraneous-dependencies: false\""
17 | },
18 | "keywords": [
19 | "weapp",
20 | "miniprogram",
21 | "navbar",
22 | "navigation-bar",
23 | "navigationbar",
24 | "小程序"
25 | ],
26 | "miniprogram": "miniprogram_dist",
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/lingxiaoyi/miniprograms-navigation-bar"
30 | },
31 | "author": "lingxiaoyi",
32 | "license": "MIT",
33 | "devDependencies": {
34 | "babel-core": "^6.26.3",
35 | "babel-loader": "^7.1.5",
36 | "babel-plugin-module-resolver": "^3.1.1",
37 | "babel-preset-env": "^1.7.0",
38 | "colors": "^1.3.1",
39 | "eslint": "^5.3.0",
40 | "eslint-loader": "^2.1.0",
41 | "gulp": "^4.0.0",
42 | "gulp-clean": "^0.4.0",
43 | "gulp-if": "^2.0.2",
44 | "gulp-install": "^1.1.0",
45 | "gulp-less": "^3.5.0",
46 | "gulp-rename": "^1.4.0",
47 | "gulp-sourcemaps": "^2.6.4",
48 | "j-component": "git+https://github.com/JuneAndGreen/j-component.git",
49 | "jest": "^23.5.0",
50 | "through2": "^2.0.3",
51 | "webpack": "^4.16.5",
52 | "webpack-node-externals": "^1.7.2",
53 | "eslint-config-airbnb-base": "13.1.0",
54 | "eslint-plugin-import": "^2.14.0",
55 | "eslint-plugin-node": "^7.0.1",
56 | "eslint-plugin-promise": "^3.8.0"
57 | },
58 | "dependencies": {},
59 | "jest": {
60 | "testEnvironment": "jsdom",
61 | "testURL": "https://jest.test",
62 | "collectCoverageFrom": [
63 | "src/**/*.js"
64 | ],
65 | "moduleDirectories": [
66 | "node_modules",
67 | "src"
68 | ]
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | Component({
2 | options: {
3 | multipleSlots: true,
4 | addGlobalClass: true
5 | },
6 | properties: {
7 | extClass: {
8 | type: String,
9 | value: ''
10 | },
11 | background: {
12 | type: String,
13 | value: 'rgba(255, 255, 255, 1)',
14 | observer: '_showChange'
15 | },
16 | backgroundColorTop: {
17 | type: String,
18 | value: 'rgba(255, 255, 255, 1)',
19 | observer: '_showChangeBackgroundColorTop'
20 | },
21 | color: {
22 | type: String,
23 | value: 'rgba(0, 0, 0, 1)'
24 | },
25 | title: {
26 | type: String,
27 | value: ''
28 | },
29 | searchText: {
30 | type: String,
31 | value: '点我搜索'
32 | },
33 | searchBar: {
34 | type: Boolean,
35 | value: false
36 | },
37 | back: {
38 | type: Boolean,
39 | value: false
40 | },
41 | home: {
42 | type: Boolean,
43 | value: false
44 | },
45 | iconTheme: {
46 | type: String,
47 | value: 'black'
48 | },
49 | /* animated: {
50 | type: Boolean,
51 | value: true
52 | },
53 | show: {
54 | type: Boolean,
55 | value: true,
56 | observer: '_showChange'
57 | }, */
58 | delta: {
59 | type: Number,
60 | value: 1
61 | }
62 | },
63 | created() {
64 | this.getSystemInfo()
65 | },
66 | attached() {
67 | this.setStyle() // 设置样式
68 | },
69 | data: {},
70 | pageLifetimes: {
71 | show() {
72 | if (getApp().globalSystemInfo.ios) {
73 | this.getSystemInfo()
74 | this.setStyle() // 设置样式1
75 | }
76 | },
77 | hide() {}
78 | },
79 | methods: {
80 | setStyle(life) {
81 | const {
82 | statusBarHeight,
83 | navBarHeight,
84 | capsulePosition,
85 | navBarExtendHeight,
86 | ios,
87 | windowWidth
88 | } = getApp().globalSystemInfo
89 | const {back, home, title} = this.data
90 | const rightDistance = windowWidth - capsulePosition.right // 胶囊按钮右侧到屏幕右侧的边距
91 | const leftWidth = windowWidth - capsulePosition.left // 胶囊按钮左侧到屏幕右侧的边距
92 |
93 | const navigationbarinnerStyle = [
94 | `color: ${this.data.color}`,
95 | `background: ${this.data.background}`,
96 | `height:${navBarHeight + navBarExtendHeight}px`,
97 | `padding-top:${statusBarHeight}px`,
98 | `padding-right:${leftWidth}px`,
99 | `padding-bottom:${navBarExtendHeight}px`
100 | ].join(';')
101 | let navBarLeft = []
102 | if ((back && !home) || (!back && home)) {
103 | navBarLeft = [`width:${capsulePosition.width}px`, `height:${capsulePosition.height}px`].join(';')
104 | } else if ((back && home) || title) {
105 | navBarLeft = [
106 | `width:${capsulePosition.width}px`,
107 | `height:${capsulePosition.height}px`,
108 | `margin-left:${rightDistance}px`
109 | ].join(';')
110 | } else {
111 | navBarLeft = ['width:auto', 'margin-left:0px'].join(';')
112 | }
113 | if (life === 'created') {
114 | this.data = {
115 | navigationbarinnerStyle,
116 | navBarLeft,
117 | navBarHeight,
118 | capsulePosition,
119 | navBarExtendHeight,
120 | ios
121 | }
122 | } else {
123 | this.setData({
124 | navigationbarinnerStyle,
125 | navBarLeft,
126 | navBarHeight,
127 | capsulePosition,
128 | navBarExtendHeight,
129 | ios
130 | })
131 | }
132 | },
133 | _showChange() {
134 | this.setStyle()
135 | },
136 | // 返回事件
137 | back() {
138 | this.triggerEvent('back', {delta: this.data.delta})
139 | },
140 | home() {
141 | this.triggerEvent('home', {})
142 | },
143 | search() {
144 | this.triggerEvent('search', {})
145 | },
146 | checkRect(rect) { // 胶囊信息4种任一属性为0返回true
147 | return !rect.width || !rect.top || !rect.left || !rect.height
148 | },
149 | getMenuButtonBoundingClientRect(systemInfo) {
150 | const ios = !!(systemInfo.system.toLowerCase().search('ios') + 1)
151 | let rect
152 | try {
153 | rect = wx.getMenuButtonBoundingClientRect ? wx.getMenuButtonBoundingClientRect() : null
154 | if (rect === null) {
155 | throw new Error('getMenuButtonBoundingClientRect error')
156 | }
157 | // 取值为0的情况 有可能width不为0 top为0的情况
158 | if (this.checkRect(rect)) {
159 | throw new Error('getMenuButtonBoundingClientRect error')
160 | }
161 | } catch (error) {
162 | let gap = '' // 胶囊按钮上下间距 使导航内容居中
163 | let width = 96 // 胶囊的宽度
164 | if (systemInfo.platform === 'android') {
165 | gap = 8
166 | width = 96
167 | } else if (systemInfo.platform === 'devtools') {
168 | if (ios) {
169 | gap = 5.5 // 开发工具中ios手机
170 | } else {
171 | gap = 7.5 // 开发工具中android和其他手机
172 | }
173 | } else {
174 | gap = 4
175 | width = 88
176 | }
177 | if (!systemInfo.statusBarHeight) {
178 | // 开启wifi的情况下修复statusBarHeight值获取不到
179 | systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20
180 | }
181 | rect = {
182 | // 获取不到胶囊信息就自定义重置一个
183 | bottom: systemInfo.statusBarHeight + gap + 32,
184 | height: 32,
185 | left: systemInfo.windowWidth - width - 10,
186 | right: systemInfo.windowWidth - 10,
187 | top: systemInfo.statusBarHeight + gap,
188 | width
189 | }
190 | }
191 | return rect
192 | },
193 | getSystemInfo() {
194 | const app = getApp()
195 | if (app.globalSystemInfo && !app.globalSystemInfo.ios) {
196 | return app.globalSystemInfo
197 | } else {
198 | const systemInfo = wx.getSystemInfoSync()
199 | const ios = !!(systemInfo.system.toLowerCase().search('ios') + 1)
200 | const rect = this.getMenuButtonBoundingClientRect(systemInfo)
201 |
202 | let navBarHeight = ''
203 | if (!systemInfo.statusBarHeight) {
204 | systemInfo.statusBarHeight = systemInfo.screenHeight - systemInfo.windowHeight - 20
205 | navBarHeight = (function () {
206 | const gap = rect.top - systemInfo.statusBarHeight
207 | return 2 * gap + rect.height
208 | }())
209 |
210 | systemInfo.statusBarHeight = 0
211 | systemInfo.navBarExtendHeight = 0 // 下方扩展4像素高度 防止下方边距太小
212 | } else {
213 | navBarHeight = (function () {
214 | const gap = rect.top - systemInfo.statusBarHeight
215 | return systemInfo.statusBarHeight + 2 * gap + rect.height
216 | }())
217 | if (ios) {
218 | systemInfo.navBarExtendHeight = 4 // 下方扩展4像素高度 防止下方边距太小
219 | } else {
220 | systemInfo.navBarExtendHeight = 0
221 | }
222 | }
223 | systemInfo.navBarHeight = navBarHeight // 导航栏高度不包括statusBarHeight
224 | systemInfo.capsulePosition = rect
225 | systemInfo.ios = ios // 是否ios
226 |
227 | app.globalSystemInfo = systemInfo // 将信息保存到全局变量中,后边再用就不用重新异步获取了
228 |
229 | // console.log('systemInfo', systemInfo);
230 | return systemInfo
231 | }
232 | }
233 | }
234 | })
235 |
--------------------------------------------------------------------------------
/src/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true,
3 | "usingComponents": {}
4 | }
5 |
--------------------------------------------------------------------------------
/src/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {{title}}
24 |
25 |
26 |
27 | {{searchText}}
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/index.wxss:
--------------------------------------------------------------------------------
1 | view,
2 | text,
3 | scroll-view,
4 | input,
5 | button,
6 | image,
7 | cover-view {
8 | box-sizing: border-box;
9 | }
10 | page {
11 | --height: 44px; /* 4*2+32 */
12 | --right: 97px; /* 10+87 */
13 | --navBarExtendHeight: 4px;
14 | box-sizing: border-box;
15 | }
16 | .lxy-nav-bar .ios {
17 | --height: 44px; /* 4*2+32 */
18 | --right: 97px; /* 10+87 */
19 | --navBarExtendHeight: 4px;
20 | box-sizing: border-box;
21 | }
22 | .lxy-nav-bar .android {
23 | --height: 48px; /* 8*2+32 */
24 | --right: 96px; /* 10+87 */
25 | --navBarExtendHeight: 4px;
26 | box-sizing: border-box;
27 | }
28 | .lxy-nav-bar .devtools {
29 | --height: 42px; /* 5*2+32 */
30 | --right: 88px; /* 10+87 */
31 | --navBarExtendHeight: 4px;
32 | box-sizing: border-box;
33 | }
34 | .lxy-nav-bar__inner {
35 | position: fixed;
36 | top: 0;
37 | left: 0;
38 | z-index: 5001;
39 | height: var(--height);
40 | display: flex;
41 | align-items: center;
42 | padding-right: var(--right);
43 | width: 100%;
44 | padding-bottom: var(--navBarExtendHeight);
45 | }
46 | .lxy-nav-bar__inner .lxy-nav-bar__left {
47 | position: relative;
48 | width: var(--right);
49 | height: 32px;
50 | /* padding-left: 10px; */
51 | display: flex;
52 | align-items: center;
53 | }
54 | .lxy-nav-bar__buttons {
55 | height: 100%;
56 | width: 100%;
57 | display: flex;
58 | align-items: center;
59 | border-radius: 16px;
60 | border: 1rpx solid rgba(204, 204, 204, 0.6);
61 | position: relative;
62 | }
63 | .lxy-nav-bar__buttons.android {
64 | border: 1rpx solid rgba(234, 234, 234, 0.6);
65 | }
66 | .lxy-nav-bar__buttons::after {
67 | position: absolute;
68 | content: '';
69 | width: 1rpx;
70 | height: 18.4px;
71 | background: rgba(204, 204, 204, 0.6);
72 | left: 50%;
73 | top: 50%;
74 | transform: translate(-50%, -50%);
75 | }
76 | .lxy-nav-bar__buttons.android::after {
77 | background: rgba(234, 234, 234, 0.6);
78 | }
79 | .lxy-nav-bar__button {
80 | width: 50%;
81 | height: 100%;
82 | display: flex;
83 | font-size: 12px;
84 | background-repeat: no-repeat;
85 | background-position: center center;
86 | background-size: 1em 2em;
87 | }
88 |
89 | .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback:active,
90 | .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome:active {
91 | opacity: 0.5;
92 | }
93 | .lxy-nav-bar__inner .lxy-nav-bar__center {
94 | font-size: 17px;
95 | line-height: 17px;
96 | text-align: center;
97 | position: relative;
98 | flex: 1;
99 | display: -webkit-box;
100 | display: -webkit-flex;
101 | display: flex;
102 | align-items: center;
103 | justify-content: center;
104 | padding-left: 10px;
105 | }
106 | .lxy-nav-bar__inner .lxy-nav-bar__center .lxy-nav-bar__center-title {
107 | margin-top: -2px;
108 | }
109 | .lxy-nav-bar__inner .lxy-nav-bar__loading {
110 | font-size: 0;
111 | }
112 | .lxy-nav-bar__inner .lxy-nav-bar__loading .lxy-loading {
113 | margin-left: 0;
114 | }
115 | .lxy-nav-bar__inner .lxy-nav-bar__right {
116 | margin-right: 10px;
117 | }
118 | .lxy-nav-bar__placeholder {
119 | height: var(--height);
120 | background: #f8f8f8;
121 | position: relative;
122 | z-index: 50;
123 | }
124 |
125 | .lxy-nav-bar-search {
126 | width: 100%;
127 | height: 100%;
128 | display: flex;
129 | justify-content: center;
130 | align-items: center;
131 | width: 100%;
132 | height: 32px;
133 | border-radius: 16px;
134 | position: relative;
135 | background: #f6f6f6;
136 | }
137 |
138 | .lxy-nav-bar-search__input {
139 | height: 100%;
140 | display: flex;
141 | align-items: center;
142 | color: #999;
143 | font-size: 15px;
144 | line-height: 15px;
145 | }
146 | .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback {
147 | background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E");
148 | }
149 | .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback.white {
150 | background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z' fill='%23ffffff'/%3E%3C/svg%3E");
151 | }
152 | .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome {
153 | background-image: url("data:image/svg+xml,%3Csvg t='1565752242401' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='4326' width='48' height='48'%3E%3Cpath d='M931.148 451.25L591.505 97.654c-21.106-21.953-49.313-34.034-79.551-34.034-30.235 0-58.448 12.081-79.554 34.034L92.76 451.25c-35.049 36.498-30.536 68.044-24.742 81.222 4.13 9.35 18.07 35.05 58.231 35.05h49.78v272.016c0 61.756 44.342 119.906 107.357 119.906h144.587v-287.87c0-30.866-4.675-48.062 26.848-48.062h114.268c31.52 0 26.845 17.196 26.845 48.061v287.872h144.588c63.013 0 107.358-58.15 107.358-119.906V567.523h49.782c40.16 0 54.1-25.7 58.229-35.05 5.793-13.18 10.306-44.726-24.743-81.224z m-33.486 60.28h-105.77v328.007c0 30.865-19.877 63.917-51.37 63.917h-88.6V671.572c0-61.761-19.79-104.05-82.832-104.05H454.821c-63.045 0-82.836 42.289-82.836 104.05v231.883h-88.599c-31.495 0-51.37-33.052-51.37-63.917V511.529H126.25c-0.984 0-1.888-3.852-2.708-3.907 1.94-3.388 5.276-11.975 10.825-17.743l339.671-353.35c10.142-10.578 24.467-17.057 38.353-16.924 13.888-0.133 27.342 6.346 37.483 16.923L889.54 489.88c5.549 5.768 8.885 14.355 10.825 17.743-0.818 0.055-1.72 3.907-2.704 3.907z' fill='%23000000' p-id='4327'%3E%3C/path%3E%3C/svg%3E");
154 | background-size: 22px 22px;
155 | }
156 | .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome.white {
157 | background-image: url("data:image/svg+xml,%3Csvg t='1565752242401' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='4326' width='48' height='48'%3E%3Cpath d='M931.148 451.25L591.505 97.654c-21.106-21.953-49.313-34.034-79.551-34.034-30.235 0-58.448 12.081-79.554 34.034L92.76 451.25c-35.049 36.498-30.536 68.044-24.742 81.222 4.13 9.35 18.07 35.05 58.231 35.05h49.78v272.016c0 61.756 44.342 119.906 107.357 119.906h144.587v-287.87c0-30.866-4.675-48.062 26.848-48.062h114.268c31.52 0 26.845 17.196 26.845 48.061v287.872h144.588c63.013 0 107.358-58.15 107.358-119.906V567.523h49.782c40.16 0 54.1-25.7 58.229-35.05 5.793-13.18 10.306-44.726-24.743-81.224z m-33.486 60.28h-105.77v328.007c0 30.865-19.877 63.917-51.37 63.917h-88.6V671.572c0-61.761-19.79-104.05-82.832-104.05H454.821c-63.045 0-82.836 42.289-82.836 104.05v231.883h-88.599c-31.495 0-51.37-33.052-51.37-63.917V511.529H126.25c-0.984 0-1.888-3.852-2.708-3.907 1.94-3.388 5.276-11.975 10.825-17.743l339.671-353.35c10.142-10.578 24.467-17.057 38.353-16.924 13.888-0.133 27.342 6.346 37.483 16.923L889.54 489.88c5.549 5.768 8.885 14.355 10.825 17.743-0.818 0.055-1.72 3.907-2.704 3.907z' fill='%23ffffff' p-id='4327'%3E%3C/path%3E%3C/svg%3E");
158 | background-size: 22px 22px;
159 | }
160 | .lxy-nav-bar-search__icon {
161 | width: 22px;
162 | height: 22px;
163 | display: flex;
164 | align-items: center;
165 | justify-content: center;
166 | background-image: url("data:image/svg+xml,%3Csvg t='1565691512239' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='1240' width='48' height='48'%3E%3Cpath d='M819.2 798.254545L674.909091 653.963636c46.545455-48.872727 74.472727-114.036364 74.472727-186.181818 0-151.272727-123.345455-274.618182-274.618182-274.618182-151.272727 0-274.618182 123.345455-274.618181 274.618182 0 151.272727 123.345455 274.618182 274.618181 274.618182 65.163636 0 128-23.272727 174.545455-62.836364l144.290909 144.290909c2.327273 2.327273 6.981818 4.654545 11.636364 4.654546s9.309091-2.327273 11.636363-4.654546c6.981818-6.981818 6.981818-18.618182 2.327273-25.6zM235.054545 467.781818c0-132.654545 107.054545-239.709091 239.709091-239.709091 132.654545 0 239.709091 107.054545 239.709091 239.709091 0 132.654545-107.054545 239.709091-239.709091 239.709091-132.654545 0-239.709091-107.054545-239.709091-239.709091z' fill='%23999999' p-id='1241'%3E%3C/path%3E%3C/svg%3E");
167 | background-repeat: no-repeat;
168 | background-size: cover;
169 | }
170 |
--------------------------------------------------------------------------------
/test/utils.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../tools/test/helper')
2 |
--------------------------------------------------------------------------------
/tools/build.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const gulp = require('gulp')
4 | const clean = require('gulp-clean')
5 | const less = require('gulp-less')
6 | const rename = require('gulp-rename')
7 | const gulpif = require('gulp-if')
8 | const sourcemaps = require('gulp-sourcemaps')
9 | const webpack = require('webpack')
10 | const gulpInstall = require('gulp-install')
11 |
12 | const config = require('./config')
13 | const checkComponents = require('./checkcomponents')
14 | const _ = require('./utils')
15 |
16 | const wxssConfig = config.wxss || {}
17 | const srcPath = config.srcPath
18 | const distPath = config.distPath
19 |
20 | /**
21 | * get wxss stream
22 | */
23 | function wxss(wxssFileList) {
24 | if (!wxssFileList.length) return false
25 |
26 | return gulp.src(wxssFileList, {cwd: srcPath, base: srcPath})
27 | .pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.init()))
28 | .pipe(gulpif(wxssConfig.less, less({paths: [srcPath]})))
29 | .pipe(rename({extname: '.wxss'}))
30 | .pipe(gulpif(wxssConfig.less && wxssConfig.sourcemap, sourcemaps.write('./')))
31 | .pipe(_.logger(wxssConfig.less ? 'generate' : undefined))
32 | .pipe(gulp.dest(distPath))
33 | }
34 |
35 | /**
36 | * get js stream
37 | */
38 | function js(jsFileMap, scope) {
39 | const webpackConfig = config.webpack
40 | const webpackCallback = (err, stats) => {
41 | if (!err) {
42 | // eslint-disable-next-line no-console
43 | console.log(stats.toString({
44 | assets: true,
45 | cached: false,
46 | colors: true,
47 | children: false,
48 | errors: true,
49 | warnings: true,
50 | version: true,
51 | modules: false,
52 | publicPath: true,
53 | }))
54 | } else {
55 | // eslint-disable-next-line no-console
56 | console.log(err)
57 | }
58 | }
59 |
60 | webpackConfig.entry = jsFileMap
61 | webpackConfig.output.path = distPath
62 |
63 | if (scope.webpackWatcher) {
64 | scope.webpackWatcher.close()
65 | scope.webpackWatcher = null
66 | }
67 |
68 | if (config.isWatch) {
69 | scope.webpackWatcher = webpack(webpackConfig).watch({
70 | ignored: /node_modules/,
71 | }, webpackCallback)
72 | } else {
73 | webpack(webpackConfig).run(webpackCallback)
74 | }
75 | }
76 |
77 | /**
78 | * copy file
79 | */
80 | function copy(copyFileList) {
81 | if (!copyFileList.length) return false
82 |
83 | return gulp.src(copyFileList, {cwd: srcPath, base: srcPath})
84 | .pipe(_.logger())
85 | .pipe(gulp.dest(distPath))
86 | }
87 |
88 | /**
89 | * install packages
90 | */
91 | function install() {
92 | return gulp.series(async () => {
93 | const demoDist = config.demoDist
94 | const demoPackageJsonPath = path.join(demoDist, 'package.json')
95 | const packageJson = _.readJson(path.resolve(__dirname, '../package.json'))
96 | const dependencies = packageJson.dependencies || {}
97 |
98 | await _.writeFile(demoPackageJsonPath, JSON.stringify({dependencies}, null, '\t')) // write dev demo's package.json
99 | }, () => {
100 | const demoDist = config.demoDist
101 | const demoPackageJsonPath = path.join(demoDist, 'package.json')
102 |
103 | return gulp.src(demoPackageJsonPath)
104 | .pipe(gulpInstall({production: true}))
105 | })
106 | }
107 |
108 | class BuildTask {
109 | constructor(id, entry) {
110 | if (!entry) return
111 |
112 | this.id = id
113 | this.entries = Array.isArray(config.entry) ? config.entry : [config.entry]
114 | this.copyList = Array.isArray(config.copy) ? config.copy : []
115 | this.componentListMap = {}
116 | this.cachedComponentListMap = {}
117 |
118 | this.init()
119 | }
120 |
121 | init() {
122 | const id = this.id
123 |
124 | /**
125 | * clean the dist folder
126 | */
127 | gulp.task(`${id}-clean-dist`, () => gulp.src(distPath, {read: false, allowEmpty: true}).pipe(clean()))
128 |
129 | /**
130 | * copy demo to the dev folder
131 | */
132 | let isDemoExists = false
133 | gulp.task(`${id}-demo`, gulp.series(async () => {
134 | const demoDist = config.demoDist
135 |
136 | isDemoExists = await _.checkFileExists(path.join(demoDist, 'project.config.json'))
137 | }, done => {
138 | if (!isDemoExists) {
139 | const demoSrc = config.demoSrc
140 | const demoDist = config.demoDist
141 |
142 | return gulp.src('**/*', {cwd: demoSrc, base: demoSrc})
143 | .pipe(gulp.dest(demoDist))
144 | }
145 |
146 | return done()
147 | }))
148 |
149 | /**
150 | * install packages for dev
151 | */
152 | gulp.task(`${id}-install`, install())
153 |
154 | /**
155 | * check custom components
156 | */
157 | gulp.task(`${id}-component-check`, async () => {
158 | const entries = this.entries
159 | const mergeComponentListMap = {}
160 | for (let i = 0, len = entries.length; i < len; i++) {
161 | let entry = entries[i]
162 | entry = path.join(srcPath, `${entry}.json`)
163 | // eslint-disable-next-line no-await-in-loop
164 | const newComponentListMap = await checkComponents(entry)
165 |
166 | _.merge(mergeComponentListMap, newComponentListMap)
167 | }
168 |
169 | this.cachedComponentListMap = this.componentListMap
170 | this.componentListMap = mergeComponentListMap
171 | })
172 |
173 | /**
174 | * write json to the dist folder
175 | */
176 | gulp.task(`${id}-component-json`, done => {
177 | const jsonFileList = this.componentListMap.jsonFileList
178 |
179 | if (jsonFileList && jsonFileList.length) {
180 | return copy(this.componentListMap.jsonFileList)
181 | }
182 |
183 | return done()
184 | })
185 |
186 | /**
187 | * copy wxml to the dist folder
188 | */
189 | gulp.task(`${id}-component-wxml`, done => {
190 | const wxmlFileList = this.componentListMap.wxmlFileList
191 |
192 | if (wxmlFileList &&
193 | wxmlFileList.length &&
194 | !_.compareArray(this.cachedComponentListMap.wxmlFileList, wxmlFileList)) {
195 | return copy(wxmlFileList)
196 | }
197 |
198 | return done()
199 | })
200 |
201 | /**
202 | * generate wxss to the dist folder
203 | */
204 | gulp.task(`${id}-component-wxss`, done => {
205 | const wxssFileList = this.componentListMap.wxssFileList
206 |
207 | if (wxssFileList &&
208 | wxssFileList.length &&
209 | !_.compareArray(this.cachedComponentListMap.wxssFileList, wxssFileList)) {
210 | return wxss(wxssFileList, srcPath, distPath)
211 | }
212 |
213 | return done()
214 | })
215 |
216 | /**
217 | * generate js to the dist folder
218 | */
219 | gulp.task(`${id}-component-js`, done => {
220 | const jsFileList = this.componentListMap.jsFileList
221 |
222 | if (jsFileList &&
223 | jsFileList.length &&
224 | !_.compareArray(this.cachedComponentListMap.jsFileList, jsFileList)) {
225 | js(this.componentListMap.jsFileMap, this)
226 | }
227 |
228 | return done()
229 | })
230 |
231 | /**
232 | * copy resources to dist folder
233 | */
234 | gulp.task(`${id}-copy`, gulp.parallel(done => {
235 | const copyList = this.copyList
236 | const copyFileList = copyList.map(dir => path.join(dir, '**/*.!(wxss)'))
237 |
238 | if (copyFileList.length) return copy(copyFileList)
239 |
240 | return done()
241 | }, done => {
242 | const copyList = this.copyList
243 | const copyFileList = copyList.map(dir => path.join(dir, '**/*.wxss'))
244 |
245 | if (copyFileList.length) return wxss(copyFileList, srcPath, distPath)
246 |
247 | return done()
248 | }))
249 |
250 | /**
251 | * watch json
252 | */
253 | gulp.task(`${id}-watch-json`, () => gulp.watch(this.componentListMap.jsonFileList, {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-check`, gulp.parallel(`${id}-component-wxml`, `${id}-component-wxss`, `${id}-component-js`, `${id}-component-json`))))
254 |
255 | /**
256 | * watch wxml
257 | */
258 | gulp.task(`${id}-watch-wxml`, () => {
259 | this.cachedComponentListMap.wxmlFileList = null
260 | return gulp.watch(this.componentListMap.wxmlFileList, {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-wxml`))
261 | })
262 |
263 | /**
264 | * watch wxss
265 | */
266 | gulp.task(`${id}-watch-wxss`, () => {
267 | this.cachedComponentListMap.wxssFileList = null
268 | return gulp.watch('**/*.wxss', {cwd: srcPath, base: srcPath}, gulp.series(`${id}-component-wxss`))
269 | })
270 |
271 | /**
272 | * watch resources
273 | */
274 | gulp.task(`${id}-watch-copy`, () => {
275 | const copyList = this.copyList
276 | const copyFileList = copyList.map(dir => path.join(dir, '**/*'))
277 | const watchCallback = filePath => copy([filePath])
278 |
279 | return gulp.watch(copyFileList, {cwd: srcPath, base: srcPath})
280 | .on('change', watchCallback)
281 | .on('add', watchCallback)
282 | .on('unlink', watchCallback)
283 | })
284 |
285 | /**
286 | * watch demo
287 | */
288 | gulp.task(`${id}-watch-demo`, () => {
289 | const demoSrc = config.demoSrc
290 | const demoDist = config.demoDist
291 | const watchCallback = filePath => gulp.src(filePath, {cwd: demoSrc, base: demoSrc})
292 | .pipe(gulp.dest(demoDist))
293 |
294 | return gulp.watch('**/*', {cwd: demoSrc, base: demoSrc})
295 | .on('change', watchCallback)
296 | .on('add', watchCallback)
297 | .on('unlink', watchCallback)
298 | })
299 |
300 | /**
301 | * watch installed packages
302 | */
303 | gulp.task(`${id}-watch-install`, () => gulp.watch(path.resolve(__dirname, '../package.json'), install()))
304 |
305 | /**
306 | * build custom component
307 | */
308 | gulp.task(`${id}-build`, gulp.series(`${id}-clean-dist`, `${id}-component-check`, gulp.parallel(`${id}-component-wxml`, `${id}-component-wxss`, `${id}-component-js`, `${id}-component-json`, `${id}-copy`)))
309 |
310 | gulp.task(`${id}-watch`, gulp.series(`${id}-build`, `${id}-demo`, `${id}-install`, gulp.parallel(`${id}-watch-wxml`, `${id}-watch-wxss`, `${id}-watch-json`, `${id}-watch-copy`, `${id}-watch-install`, `${id}-watch-demo`)))
311 |
312 | gulp.task(`${id}-dev`, gulp.series(`${id}-build`, `${id}-demo`, `${id}-install`))
313 |
314 | gulp.task(`${id}-default`, gulp.series(`${id}-build`))
315 | }
316 | }
317 |
318 | module.exports = BuildTask
319 |
--------------------------------------------------------------------------------
/tools/checkcomponents.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const _ = require('./utils')
4 | const config = require('./config')
5 |
6 | const srcPath = config.srcPath
7 |
8 | /**
9 | * get json path's info
10 | */
11 | function getJsonPathInfo(jsonPath) {
12 | const dirPath = path.dirname(jsonPath)
13 | const fileName = path.basename(jsonPath, '.json')
14 | const relative = path.relative(srcPath, dirPath)
15 | const fileBase = path.join(relative, fileName)
16 |
17 | return {
18 | dirPath, fileName, relative, fileBase
19 | }
20 | }
21 |
22 | /**
23 | * check included components
24 | */
25 | const checkProps = ['usingComponents', 'componentGenerics']
26 | async function checkIncludedComponents(jsonPath, componentListMap) {
27 | const json = _.readJson(jsonPath)
28 | if (!json) throw new Error(`json is not valid: "${jsonPath}"`)
29 |
30 | const {dirPath, fileName, fileBase} = getJsonPathInfo(jsonPath)
31 |
32 | for (let i = 0, len = checkProps.length; i < len; i++) {
33 | const checkProp = checkProps[i]
34 | const checkPropValue = json[checkProp] || {}
35 | const keys = Object.keys(checkPropValue)
36 |
37 | for (let j = 0, jlen = keys.length; j < jlen; j++) {
38 | const key = keys[j]
39 | let value = typeof checkPropValue[key] === 'object' ? checkPropValue[key].default : checkPropValue[key]
40 | if (!value) continue
41 |
42 | value = _.transformPath(value, path.sep)
43 |
44 | // check relative path
45 | const componentPath = `${path.join(dirPath, value)}.json`
46 | // eslint-disable-next-line no-await-in-loop
47 | const isExists = await _.checkFileExists(componentPath)
48 | if (isExists) {
49 | // eslint-disable-next-line no-await-in-loop
50 | await checkIncludedComponents(componentPath, componentListMap)
51 | }
52 | }
53 | }
54 |
55 | // checked
56 | componentListMap.wxmlFileList.push(`${fileBase}.wxml`)
57 | componentListMap.wxssFileList.push(`${fileBase}.wxss`)
58 | componentListMap.jsonFileList.push(`${fileBase}.json`)
59 | componentListMap.jsFileList.push(`${fileBase}.js`)
60 |
61 | componentListMap.jsFileMap[fileBase] = `${path.join(dirPath, fileName)}.js`
62 | }
63 |
64 | module.exports = async function (entry) {
65 | const componentListMap = {
66 | wxmlFileList: [],
67 | wxssFileList: [],
68 | jsonFileList: [],
69 | jsFileList: [],
70 |
71 | jsFileMap: {}, // for webpack entry
72 | }
73 |
74 | const isExists = await _.checkFileExists(entry)
75 | if (!isExists) {
76 | const {dirPath, fileName, fileBase} = getJsonPathInfo(entry)
77 |
78 | componentListMap.jsFileList.push(`${fileBase}.js`)
79 | componentListMap.jsFileMap[fileBase] = `${path.join(dirPath, fileName)}.js`
80 |
81 | return componentListMap
82 | }
83 |
84 | await checkIncludedComponents(entry, componentListMap)
85 |
86 | return componentListMap
87 | }
88 |
--------------------------------------------------------------------------------
/tools/config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const webpack = require('webpack')
4 | const nodeExternals = require('webpack-node-externals')
5 |
6 | const isDev = process.argv.indexOf('--develop') >= 0
7 | const isWatch = process.argv.indexOf('--watch') >= 0
8 | const demoSrc = path.resolve(__dirname, './demo')
9 | const demoDist = path.resolve(__dirname, '../miniprogram_dev')
10 | const src = path.resolve(__dirname, '../src')
11 | const dev = path.join(demoDist, 'components')
12 | const dist = path.resolve(__dirname, '../miniprogram_dist')
13 |
14 | module.exports = {
15 | entry: ['index'],
16 |
17 | isDev,
18 | isWatch,
19 | srcPath: src,
20 | distPath: isDev ? dev : dist,
21 |
22 | demoSrc,
23 | demoDist,
24 |
25 | wxss: {
26 | less: false, // compile wxss with less
27 | sourcemap: false, // source map for less
28 | },
29 |
30 | webpack: {
31 | mode: 'production',
32 | output: {
33 | filename: '[name].js',
34 | libraryTarget: 'commonjs2',
35 | },
36 | target: 'node',
37 | externals: [nodeExternals()], // ignore node_modules
38 | module: {
39 | rules: [{
40 | test: /\.js$/i,
41 | use: [
42 | 'babel-loader',
43 | 'eslint-loader'
44 | ],
45 | exclude: /node_modules/
46 | }],
47 | },
48 | resolve: {
49 | modules: [src, 'node_modules'],
50 | extensions: ['.js', '.json'],
51 | },
52 | plugins: [
53 | new webpack.DefinePlugin({}),
54 | new webpack.optimize.LimitChunkCountPlugin({maxChunks: 1}),
55 | ],
56 | optimization: {
57 | minimize: false,
58 | },
59 | // devtool: 'nosources-source-map', // source map for js
60 | performance: {
61 | hints: 'warning',
62 | assetFilter: assetFilename => assetFilename.endsWith('.js')
63 | }
64 | },
65 | copy: ['./wxml', './wxss', './wxs', './images'],
66 | }
67 |
--------------------------------------------------------------------------------
/tools/demo/app.js:
--------------------------------------------------------------------------------
1 | // app.js
2 | App({
3 | onLaunch() {
4 | }
5 | // globalData: {}
6 | })
7 |
--------------------------------------------------------------------------------
/tools/demo/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 | "pages/index/index",
4 | "pages/demo1/index",
5 | "pages/demo2/index",
6 | "pages/demo3/index",
7 | "pages/demo4/index",
8 | "pages/demo5/index",
9 | "pages/demo6/index",
10 | "pages/demo7/index",
11 | "pages/demo8/index",
12 | "pages/demo9/index",
13 | "pages/demo10/index"
14 | ],
15 | "window": {
16 | "backgroundTextStyle": "light",
17 | "navigationBarBackgroundColor": "#fff",
18 | "navigationBarTitleText": "自定义导航栏",
19 | "navigationBarTextStyle": "black",
20 | "navigationStyle": "custom"
21 | },
22 | "usingComponents": {
23 | "navBar": "/components/index"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tools/demo/app.wxss:
--------------------------------------------------------------------------------
1 | /* page {
2 | background-color: #e5e5e5;
3 | } */
4 | .main {
5 | /* prettier-ignore */
6 | height: 800PX;
7 | background: #eeeeee;
8 | padding-top: 100rpx;
9 | font-size: 30rpx;
10 | line-height: 60rpx;
11 | /* prettier-ignore */
12 | padding: 30rpx;
13 | text-align: center;
14 | }
15 | .main .p.active {
16 | color: #087ad8;
17 | text-align: left;
18 | }
19 | .main .p.active:active {
20 | color: #0397fa;
21 | }
22 |
23 | /**app.wxss**/
24 | .container {
25 | height: 100%;
26 | display: -webkit-box;
27 | display: -webkit-flex;
28 | display: -ms-flexbox;
29 | display: flex;
30 | -webkit-box-orient: vertical;
31 | -webkit-box-direction: normal;
32 | -webkit-flex-direction: column;
33 | -ms-flex-direction: column;
34 | flex-direction: column;
35 | -webkit-box-align: center;
36 | -webkit-align-items: center;
37 | -ms-flex-align: center;
38 | align-items: center;
39 | -webkit-box-pack: justify;
40 | -webkit-justify-content: space-between;
41 | -ms-flex-pack: justify;
42 | justify-content: space-between;
43 | padding: 200rpx 0;
44 | -webkit-box-sizing: border-box;
45 | box-sizing: border-box;
46 | }
47 |
48 | .main {
49 | /* prettier-ignore */
50 | height: 400PX;
51 | height: 100vh;
52 | background: #eeeeee;
53 | padding-top: 100rpx;
54 | font-size: 30rpx;
55 | line-height: 60rpx;
56 | padding: 30rpx;
57 | text-align: center;
58 | }
59 |
60 | .lxy-nav-bar-search {
61 | display: -webkit-box;
62 | display: -webkit-flex;
63 | display: -ms-flexbox;
64 | display: flex;
65 | -webkit-box-pack: center;
66 | -webkit-justify-content: center;
67 | -ms-flex-pack: center;
68 | justify-content: center;
69 | -webkit-box-align: center;
70 | -webkit-align-items: center;
71 | -ms-flex-align: center;
72 | align-items: center;
73 | width: 100%;
74 | /* prettier-ignore */
75 | height: 32PX;
76 | /* prettier-ignore */
77 | font-size: 16PX;
78 | /* prettier-ignore */
79 | border-radius: 16PX;
80 | /* prettier-ignore */
81 | padding-left: 5PX;
82 | position: relative;
83 | background: #f6f6f6;
84 | }
85 |
86 | .srch-ipt {
87 | /* prettier-ignore */
88 | height: 32PX;
89 | text-align: left;
90 | /* prettier-ignore */
91 | font-size: 15PX;
92 | }
93 |
94 | .ipt-placeholder {
95 | /* prettier-ignore */
96 | font-size: 15PX;
97 | color: #bebebe;
98 | text-align: left;
99 | }
100 |
101 | .lxy-nav-bar-search__input {
102 | flex: 1;
103 | }
104 |
105 | .lxy-nav-bar-search__icon {
106 | /* prettier-ignore */
107 | width: 22PX;
108 | /* prettier-ignore */
109 | height: 22PX;
110 | display: -webkit-box;
111 | display: -webkit-flex;
112 | display: -ms-flexbox;
113 | display: flex;
114 | -webkit-box-align: center;
115 | -webkit-align-items: center;
116 | -ms-flex-align: center;
117 | align-items: center;
118 | -webkit-box-pack: center;
119 | -webkit-justify-content: center;
120 | -ms-flex-pack: center;
121 | justify-content: center;
122 | background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAADBklEQVRoQ+2YS3IaMRCGu7UQ7MINjE9gcgLjEyTcADZoZuXcIPYJglczYgOcINwg+ATBJwg5gZMlLNSpdiTXZMpV1mOo4KpRFQuXZ1r99d9qdQ/CG1/4xv2HFuB/K9gq0CqQGIE2hRIDmPx6IwosFovefr+/BICPANDnHyL2iWhjPVwLIe6n0+k22eOagSQAdvxwOFwDwCcA6FnbPwFgV9lnAADv+G8GIqLbPM8dWDJPNIDWmqO9sI7fG2OW3W53PZlMftW9ms/nA2PMGBHHFmYtpZy89GwoURSA1poj/gUAfgPAWCm19tnYptoSET8AwNYYM8rzvKqWj5l/ngkGKIpiLIRYENFDp9MZxkTR2WAIKeVVjA1HEQTAqUBE31OcdxtXINZKqVFw6O0LQQBaa5a7Z4wZpErP+5dlOUPEa2PMJM/zZQyEN0AlYrdKqZuYzerv2Cq2I6LHLMvOY2x6A5RluUXEnlKK63xjqxKYkW8xqG7uBVAURV8I8QMAVkopLoWNrVTbvgBPlQcAoqL0Gi2ry/dDTBp5AWitOec/I+L7Y7QDZVmu+W5QSnn5E5xCDiBmg9eiz/9Pse9FnLLBSQEYY66abMQcnNaam7vLGIV9FeDG7euxDrG9ICGmRHsB2Avn8ZhllIjusizjJjFoeQHYg8YyX0gpz1Oar7p3qRXOG6AoiqEQ4hsANN1K8AX5oJQaBoU+spl7OmxN3Qeu/qcUB28FGNi20wxBqankUic2951aQQD2LDxVJCLaCSFGMTeza6N5rsiyjGfm6BUMUIHg/p0AYCalvPM52HyOEJFbkud8T5kF2JcogEo68Sx8xmogIvczq7oitgTzDMzK8Y/XrZRytt/vN4h4kQIRDeA0t/08N3tn1TywUPXZYWWMuXHTnB3ykyCSAZzT9oBzanBOPztORDwIbaSUm5fSLBWiMYDoU8gfl/5+2YtS4iQAGD4W4mQA6hC+jeNJAVQg+Ovd0mfIPzmA0LPUAoRGrOnnWwWajmiovVaB0Ig1/XyrQNMRDbX3B3I3pUA0WyZuAAAAAElFTkSuQmCC);
123 | background-repeat: no-repeat;
124 | background-size: cover;
125 | }
126 |
127 | .location {
128 | /* prettier-ignore */
129 | font-size: 14PX;
130 | /* prettier-ignore */
131 | width: 52PX;
132 | height: 100%;
133 | display: -webkit-box;
134 | display: -webkit-flex;
135 | display: -ms-flexbox;
136 | display: flex;
137 | -webkit-box-align: center;
138 | -webkit-align-items: center;
139 | -ms-flex-align: center;
140 | align-items: center;
141 | color: #fff;
142 | position: relative;
143 | /* prettier-ignore */
144 | padding-left: 10PX;
145 | }
146 |
147 | .location .icon {
148 | width: 0;
149 | height: 0;
150 | /* prettier-ignore */
151 | border-left: 5PX solid transparent;
152 | /* prettier-ignore */
153 | border-right: 5PX solid transparent;
154 | /* prettier-ignore */
155 | border-top: 5PX solid #fff;
156 | /* prettier-ignore */
157 | margin-left: 2.5PX;
158 | }
159 |
160 | .location .btn {
161 | position: absolute;
162 | left: 0;
163 | top: 0;
164 | opacity: 0;
165 | width: 100%;
166 | height: 100%;
167 | }
168 |
--------------------------------------------------------------------------------
/tools/demo/package.json:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo1/index.js:
--------------------------------------------------------------------------------
1 | // index.js
2 | // 获取应用实例
3 | // const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick() {
8 | wx.showModal({
9 | title: '你点击了返回',
10 | content: '是否确认放回',
11 | success: e => {
12 | if (e.confirm) {
13 | const pages = getCurrentPages()
14 | if (pages.length >= 2) {
15 | wx.navigateBack({
16 | delta: 1
17 | })
18 | } else {
19 | wx.navigateTo({
20 | url: '/pages/index/index'
21 | })
22 | }
23 | }
24 | }
25 | })
26 | },
27 | handlerGohomeClick() {
28 | wx.navigateTo({
29 | url: '/pages/index/index'
30 | })
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo1/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo1/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 搜索页面
12 |
13 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo1/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo1/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo10/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: { background: 'rgba(255,255,255,1)' },
7 | handlerGobackClick() {
8 | wx.showModal({
9 | title: '你点击了返回',
10 | content: '是否确认放回',
11 | success: e => {
12 | if (e.confirm) {
13 | const pages = getCurrentPages();
14 | if (pages.length >= 2) {
15 | wx.navigateBack({
16 | delta: 1
17 | });
18 | } else {
19 | wx.navigateTo({
20 | url: '/pages/index/index'
21 | });
22 | }
23 | }
24 | }
25 | });
26 | },
27 | onPageScroll(e) {
28 | let opciaty = e.scrollTop / 130;
29 | if (opciaty >= 1) {
30 | opciaty = 1;
31 | } else if (opciaty <= 0) {
32 | opciaty = 0;
33 | }
34 | console.log('opciaty', opciaty);
35 | this.setData({ background: `rgba(255,0,0,${opciaty})` });
36 | },
37 | handlerGohomeClick() {
38 | wx.navigateTo({
39 | url: '/pages/index/index'
40 | });
41 | }
42 | });
43 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo10/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo10/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | 搜索页面
12 |
13 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo10/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo10/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo2/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick(delta) {
8 | const pages = getCurrentPages();
9 | if (pages.length >= 2) {
10 | wx.navigateBack({
11 | delta: delta
12 | });
13 | } else {
14 | wx.navigateTo({
15 | url: '/pages/index/index'
16 | });
17 | }
18 | },
19 | handlerGohomeClick() {
20 | wx.navigateTo({
21 | url: '/pages/index/index'
22 | });
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo2/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo2/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 有返回和home的页面
5 |
6 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo2/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo2/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo3/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick() {
8 | wx.showModal({
9 | title: '你点击了返回',
10 | content: '是否确认放回',
11 | success: e => {
12 | if (e.confirm) {
13 | const pages = getCurrentPages();
14 | if (pages.length >= 2) {
15 | wx.navigateBack({
16 | delta: 1
17 | });
18 | } else {
19 | wx.navigateTo({
20 | url: '/pages/index/index'
21 | });
22 | }
23 | }
24 | }
25 | });
26 | },
27 | handlerGohomeClick() {
28 | wx.navigateTo({
29 | url: '/pages/index/index'
30 | });
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo3/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo3/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 详情页
5 |
6 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo3/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo3/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo4/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick(delta) {
8 | const pages = getCurrentPages();
9 | if (pages.length >= 2) {
10 | wx.navigateBack({
11 | delta: delta
12 | });
13 | } else {
14 | wx.navigateTo({
15 | url: '/pages/index/index'
16 | });
17 | }
18 | },
19 | handlerGohomeClick() {
20 | wx.navigateTo({
21 | url: '/pages/index/index'
22 | });
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo4/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTextStyle": "white",
3 | "navigationStyle": "custom",
4 | "backgroundColor": "#f8f8f8",
5 | "backgroundColorTop": "#00000",
6 | "backgroundColorBottom": "#f8f8f8",
7 | "usingComponents": {}
8 | }
9 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo4/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 深色背景详情页
5 |
6 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo4/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo4/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo5/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick() {
8 | wx.showModal({
9 | title: '你点击了返回',
10 | content: '是否确认放回',
11 | success: e => {
12 | if (e.confirm) {
13 | const pages = getCurrentPages();
14 | if (pages.length >= 2) {
15 | wx.navigateBack({
16 | delta: 1
17 | });
18 | } else {
19 | wx.navigateTo({
20 | url: '/pages/index/index'
21 | });
22 | }
23 | }
24 | }
25 | });
26 | },
27 | handlerGohomeClick() {
28 | wx.navigateTo({
29 | url: '/pages/index/index'
30 | });
31 | }
32 | });
33 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo5/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTextStyle": "white",
3 | "navigationStyle": "custom",
4 | "backgroundColor": "#f8f8f8",
5 | "backgroundColorTop": "#00000",
6 | "backgroundColorBottom": "#f8f8f8",
7 | "usingComponents": {}
8 | }
9 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo5/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 深色背景详情页包括返回和home按钮
5 |
6 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo5/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo5/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo6/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick(delta) {
8 | const pages = getCurrentPages();
9 | if (pages.length >= 2) {
10 | wx.navigateBack({
11 | delta: delta
12 | });
13 | } else {
14 | wx.navigateTo({
15 | url: '/pages/index/index'
16 | });
17 | }
18 | },
19 | handlerGohomeClick() {
20 | wx.navigateTo({
21 | url: '/pages/index/index'
22 | });
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo6/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTextStyle": "white",
3 | "navigationStyle": "custom",
4 | "backgroundColor": "#f8f8f8",
5 | "backgroundColorTop": "#00000",
6 | "backgroundColorBottom": "#f8f8f8",
7 | "usingComponents": {}
8 | }
9 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo6/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 上海
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 点击跳转自定义左侧栏目带自定义搜索框
16 |
17 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo6/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo6/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo7/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | search() {
8 | wx.navigateTo({
9 | url: '/pages/demo/demo1'
10 | });
11 | },
12 | handlerGobackClick() {
13 | wx.showModal({
14 | title: '你点击了返回',
15 | content: '是否确认放回',
16 | success: e => {
17 | if (e.confirm) {
18 | const pages = getCurrentPages();
19 | if (pages.length >= 2) {
20 | wx.navigateBack({
21 | delta: 1
22 | });
23 | } else {
24 | wx.navigateTo({
25 | url: '/pages/index/index'
26 | });
27 | }
28 | }
29 | }
30 | });
31 | },
32 | handlerGohomeClick() {
33 | wx.navigateTo({
34 | url: '/pages/index/index'
35 | });
36 | }
37 | });
38 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo7/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTextStyle": "white",
3 | "navigationStyle": "custom",
4 | "backgroundColor": "#f8f8f8",
5 | "backgroundColorTop": "#00000",
6 | "backgroundColorBottom": "#f8f8f8",
7 | "usingComponents": {}
8 | }
9 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo7/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 上海
5 |
6 |
7 |
8 |
9 | 深色背景详情页
10 |
11 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo7/index.wxss:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lingxiaoyi/miniprograms-navigation-bar/271725f003c2e7296df4125cd800610c4afcbb4b/tools/demo/pages/demo7/index.wxss
--------------------------------------------------------------------------------
/tools/demo/pages/demo8/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick(delta) {
8 | const pages = getCurrentPages();
9 | if (pages.length >= 2) {
10 | wx.navigateBack({
11 | delta: delta
12 | });
13 | } else {
14 | wx.navigateTo({
15 | url: '/pages/index/index'
16 | });
17 | }
18 | },
19 | handlerGohomeClick() {
20 | wx.navigateTo({
21 | url: '/pages/index/index'
22 | });
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo8/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo8/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 自定义样式和修改默认图标
5 |
6 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo8/index.wxss:
--------------------------------------------------------------------------------
1 | .lxy-navbar-extclass .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_goback {
2 | background-image: url("data:image/svg+xml,%3Csvg t='1565943723828' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3607' width='48' height='48'%3E%3Cpath d='M861.7 447.5c-14.9-35-36.1-66.4-63.1-93.4-38.2-38.3-85.4-64.9-139-78.3-23.9-6-48.6-8.1-73.3-8.1H336.8v-83.3c0-12.1-14.1-18.8-23.5-11.2l-162 131.3c-13.7 11.1-13.7 32 0 43.1L313.4 479c9.4 7.6 23.5 0.9 23.5-11.2v-83.3h245.3c92.6 0 175.8 67.3 185.1 159.4 10.7 105.8-73 195.8-176.7 195.8h-337c-32.3 0-58.4 26.2-58.4 58.4 0 32.3 26.2 58.4 58.4 58.4h332.8c24.6 0 49.4-2.1 73.3-8.1 53.6-13.4 100.7-40.1 139-78.3 27-27 48.2-58.4 63.1-93.4 15.5-36.4 23.3-75 23.3-114.7-0.1-39.5-8-78.1-23.4-114.5z m0 0' fill='%23000000' p-id='3608'%3E%3C/path%3E%3C/svg%3E");
3 | background-size: 22px 22px;
4 | }
5 |
6 | .lxy-navbar-extclass .lxy-nav-bar__inner .lxy-nav-bar__left .lxy-nav-bar__btn_gohome {
7 | background-image: url("data:image/svg+xml,%3Csvg t='1565943334961' class='icon' viewBox='0 0 1024 1024' version='1.1' xmlns='http://www.w3.org/2000/svg' p-id='3467' width='48' height='48'%3E%3Cpath d='M426.666667 597.333333h170.666666v298.666667h-170.666666z' fill='%23000000' p-id='3468'%3E%3C/path%3E%3Cpath d='M871.253333 434.346667L542.293333 98.133333a42.666667 42.666667 0 0 0-60.586666 0l-328.96 336.64A85.333333 85.333333 0 0 0 128 495.786667V853.333333a85.333333 85.333333 0 0 0 80.64 85.333334H341.333333v-384a42.666667 42.666667 0 0 1 42.666667-42.666667h256a42.666667 42.666667 0 0 1 42.666667 42.666667v384h132.693333A85.333333 85.333333 0 0 0 896 853.333333v-357.546666a88.32 88.32 0 0 0-24.746667-61.44z' fill='%23000000' p-id='3469'%3E%3C/path%3E%3C/svg%3E");
8 | background-size: 22px 22px;
9 | }
10 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo9/index.js:
--------------------------------------------------------------------------------
1 | //index.js
2 | //获取应用实例
3 | //const app = getApp();
4 |
5 | Page({
6 | data: {},
7 | handlerGobackClick(delta) {
8 | const pages = getCurrentPages();
9 | if (pages.length >= 2) {
10 | wx.navigateBack({
11 | delta: delta
12 | });
13 | } else {
14 | wx.navigateTo({
15 | url: '/pages/index/index'
16 | });
17 | }
18 | },
19 | handlerGohomeClick() {
20 | wx.navigateTo({
21 | url: '/pages/index/index'
22 | });
23 | }
24 | });
25 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo9/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "navigationBarTextStyle": "white",
3 | "navigationStyle": "custom",
4 | "backgroundColor": "#f8f8f8",
5 | "backgroundColorTop": "#00000",
6 | "backgroundColorBottom": "#f8f8f8",
7 | "usingComponents": {}
8 | }
9 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo9/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 此处为图片背景
7 |
8 |
--------------------------------------------------------------------------------
/tools/demo/pages/demo9/index.wxss:
--------------------------------------------------------------------------------
1 | .main22 {
2 | /* prettier-ignore */
3 | height: 400PX;
4 | height: 100vh;
5 | background: #eeeeee;
6 | font-size: 30rpx;
7 | line-height: 60rpx;
8 | text-align: center;
9 | }
10 | .nav {
11 | height: 0;
12 | }
13 |
14 | .img {
15 | width: 100%;
16 | height: 500px;
17 | line-height: 500px;
18 | color: #fff;
19 | background: linear-gradient(280deg, #fff 0%, red 50%, blue 80%);
20 | }
21 |
--------------------------------------------------------------------------------
/tools/demo/pages/index/index.js:
--------------------------------------------------------------------------------
1 | // index.js
2 | // 获取应用实例
3 | // const app = getApp();
4 |
5 | Page({
6 | data: {
7 | loading: false,
8 | color: '#000',
9 | background: '#fff',
10 | show: true,
11 | animated: false
12 | },
13 |
14 | onLoad() {},
15 | search() {
16 | wx.navigateTo({
17 | url: '/pages/demo1/index'
18 | })
19 | }
20 | })
21 |
--------------------------------------------------------------------------------
/tools/demo/pages/index/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {}
3 | }
4 |
--------------------------------------------------------------------------------
/tools/demo/pages/index/index.wxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 例子1: 点击跳转搜索页
6 |
7 |
8 | 例子2: 点击跳转左侧有返回 home的详情页面
9 |
10 |
11 | 例子3: 点击跳转详情页
12 |
13 |
14 | 例子4: 点击跳转详情页,深背景色,白色主题
15 |
16 |
17 | 例子5: 点击跳转,深背景色,白色主题有home
18 |
19 |
20 | 例子6: 点击跳转自定义左侧栏目带自定义搜索框
21 |
22 |
23 | 例子7: 点击跳转自定义左侧栏目带标题
24 |
25 |
26 | 例子8: 修改自定义样式和图标用法
27 |
28 |
29 | 例子9: 导航头部透明,不占据位置显示
30 |
31 |
32 | 例子10: 渐变色动态修改导航颜色
33 |
34 |
35 |
--------------------------------------------------------------------------------
/tools/demo/pages/index/index.wxss:
--------------------------------------------------------------------------------
1 | .lxy-nav-bar-search__input {
2 | display: block;
3 | flex: none;
4 | }
5 |
--------------------------------------------------------------------------------
/tools/demo/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "项目配置文件",
3 | "packOptions": {
4 | "ignore": []
5 | },
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": true,
9 | "postcss": true,
10 | "minified": true,
11 | "newFeature": true,
12 | "coverView": true,
13 | "nodeModules": true,
14 | "autoAudits": false,
15 | "checkInvalidKey": true,
16 | "checkSiteMap": true,
17 | "uploadWithSourceMap": true,
18 | "babelSetting": {
19 | "ignore": [],
20 | "disablePlugins": [],
21 | "outputPath": ""
22 | }
23 | },
24 | "compileType": "miniprogram",
25 | "libVersion": "2.7.7",
26 | "appid": "wx76fe9d97c50feaa5",
27 | "projectname": "navigationBar",
28 | "debugOptions": {
29 | "hidedInDevtools": []
30 | },
31 | "isGameTourist": false,
32 | "simulatorType": "wechat",
33 | "simulatorPluginLibVersion": {},
34 | "condition": {
35 | "search": {
36 | "current": -1,
37 | "list": []
38 | },
39 | "conversation": {
40 | "current": -1,
41 | "list": []
42 | },
43 | "plugin": {
44 | "current": -1,
45 | "list": []
46 | },
47 | "game": {
48 | "currentL": -1,
49 | "list": []
50 | },
51 | "miniprogram": {
52 | "current": 1,
53 | "list": [
54 | {
55 | "id": -1,
56 | "name": "pages/demo8/index",
57 | "pathName": "pages/demo8/index",
58 | "query": "",
59 | "scene": null
60 | },
61 | {
62 | "id": -1,
63 | "name": "pages/demo1/index",
64 | "pathName": "pages/demo1/index",
65 | "scene": null
66 | }
67 | ]
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/tools/test/helper.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | const jComponent = require('j-component')
4 |
5 | const config = require('../config')
6 | const _ = require('../utils')
7 |
8 | const srcPath = config.srcPath
9 | const componentMap = {}
10 | let nowLoad = null
11 |
12 | /**
13 | * register custom component
14 | */
15 | global.Component = options => {
16 | const component = nowLoad
17 | const definition = Object.assign({
18 | template: component.wxml,
19 | usingComponents: component.json.usingComponents,
20 | tagName: component.tagName,
21 | }, options)
22 |
23 | component.id = jComponent.register(definition)
24 | }
25 |
26 | /**
27 | * register behavior
28 | */
29 | global.Behavior = options => jComponent.behavior(options)
30 |
31 | /**
32 | * register global components
33 | */
34 | // eslint-disable-next-line semi-style
35 | ;[
36 | 'view', 'scroll-view', 'swiper', 'movable-view', 'cover-view', 'cover-view',
37 | 'icon', 'text', 'rich-text', 'progress',
38 | 'button', 'checkbox', 'form', 'input', 'label', 'picker', 'picker', 'picker-view', 'radio', 'slider', 'switch', 'textarea',
39 | 'navigator', 'function-page-navigator',
40 | 'audio', 'image', 'video', 'camera', 'live-player', 'live-pusher',
41 | 'map',
42 | 'canvas',
43 | 'open-data', 'web-view', 'ad'
44 | ].forEach(name => {
45 | jComponent.register({
46 | id: name,
47 | tagName: `wx-${name}`,
48 | template: ''
49 | })
50 | })
51 |
52 | /**
53 | * Touch polyfill
54 | */
55 | class Touch {
56 | constructor(options = {}) {
57 | this.clientX = 0
58 | this.clientY = 0
59 | this.identifier = 0
60 | this.pageX = 0
61 | this.pageY = 0
62 | this.screenX = 0
63 | this.screenY = 0
64 | this.target = null
65 |
66 | Object.keys(options).forEach(key => {
67 | this[key] = options[key]
68 | })
69 | }
70 | }
71 | global.Touch = window.Touch = Touch
72 |
73 | /**
74 | * load component
75 | */
76 | async function load(componentPath, tagName) {
77 | if (typeof componentPath === 'object') {
78 | const definition = componentPath
79 |
80 | return jComponent.register(definition)
81 | }
82 |
83 | const wholePath = path.join(srcPath, componentPath)
84 |
85 | const oldLoad = nowLoad
86 | const component = nowLoad = {}
87 |
88 | component.tagName = tagName
89 | component.wxml = await _.readFile(`${wholePath}.wxml`)
90 | component.wxss = await _.readFile(`${wholePath}.wxss`)
91 | component.json = _.readJson(`${wholePath}.json`)
92 |
93 | if (!component.json) {
94 | throw new Error(`invalid component: ${wholePath}`)
95 | }
96 |
97 | // preload using components
98 | const usingComponents = component.json.usingComponents || {}
99 | const usingComponentKeys = Object.keys(usingComponents)
100 | for (let i = 0, len = usingComponentKeys.length; i < len; i++) {
101 | const key = usingComponentKeys[i]
102 | const usingPath = path.join(path.dirname(componentPath), usingComponents[key])
103 | // eslint-disable-next-line no-await-in-loop
104 | const id = await load(usingPath)
105 |
106 | usingComponents[key] = id
107 | }
108 |
109 | // require js
110 | // eslint-disable-next-line import/no-dynamic-require
111 | require(wholePath)
112 |
113 | nowLoad = oldLoad
114 | componentMap[wholePath] = component
115 |
116 | return component.id
117 | }
118 |
119 | /**
120 | * render component
121 | */
122 | function render(componentId, properties) {
123 | if (!componentId) throw new Error('you need to pass the componentId')
124 |
125 | return jComponent.create(componentId, properties)
126 | }
127 |
128 | /**
129 | * test a dom is similar to the html
130 | */
131 | function match(dom, html) {
132 | if (!(dom instanceof window.Element) || !html || typeof html !== 'string') return false
133 |
134 | // remove some
135 | html = html.trim()
136 | .replace(/(>)[\n\r\s\t]+(<)/g, '$1$2')
137 |
138 | const a = dom.cloneNode()
139 | const b = dom.cloneNode()
140 |
141 | a.innerHTML = dom.innerHTML
142 | b.innerHTML = html
143 |
144 | return a.isEqualNode(b)
145 | }
146 |
147 | /**
148 | * wait for some time
149 | */
150 | function sleep(time = 0) {
151 | return new Promise(resolve => {
152 | setTimeout(() => {
153 | resolve()
154 | }, time)
155 | })
156 | }
157 |
158 | module.exports = {
159 | load,
160 | render,
161 | match,
162 | sleep,
163 | }
164 |
--------------------------------------------------------------------------------
/tools/utils.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 |
4 | // eslint-disable-next-line no-unused-vars
5 | const colors = require('colors')
6 | const through = require('through2')
7 |
8 | /**
9 | * async function wrapper
10 | */
11 | function wrap(func, scope) {
12 | return function (...args) {
13 | if (args.length) {
14 | const temp = args.pop()
15 | if (typeof temp !== 'function') {
16 | args.push(temp)
17 | }
18 | }
19 |
20 | return new Promise(function (resolve, reject) {
21 | args.push(function (err, data) {
22 | if (err) reject(err)
23 | else resolve(data)
24 | })
25 |
26 | func.apply((scope || null), args)
27 | })
28 | }
29 | }
30 |
31 | const accessSync = wrap(fs.access)
32 | const statSync = wrap(fs.stat)
33 | const renameSync = wrap(fs.rename)
34 | const mkdirSync = wrap(fs.mkdir)
35 | const readFileSync = wrap(fs.readFile)
36 | const writeFileSync = wrap(fs.writeFile)
37 |
38 | /**
39 | * transform path segment separator
40 | */
41 | function transformPath(filePath, sep = '/') {
42 | return filePath.replace(/[\\/]/g, sep)
43 | }
44 |
45 | /**
46 | * check file exists
47 | */
48 | async function checkFileExists(filePath) {
49 | try {
50 | await accessSync(filePath)
51 | return true
52 | } catch (err) {
53 | return false
54 | }
55 | }
56 |
57 | /**
58 | * create folder
59 | */
60 | async function recursiveMkdir(dirPath) {
61 | const prevDirPath = path.dirname(dirPath)
62 | try {
63 | await accessSync(prevDirPath)
64 | } catch (err) {
65 | // prevDirPath is not exist
66 | await recursiveMkdir(prevDirPath)
67 | }
68 |
69 | try {
70 | await accessSync(dirPath)
71 |
72 | const stat = await statSync(dirPath)
73 | if (stat && !stat.isDirectory()) {
74 | // dirPath already exists but is not a directory
75 | await renameSync(dirPath, `${dirPath}.bak`) // rename to a file with the suffix ending in '.bak'
76 | await mkdirSync(dirPath)
77 | }
78 | } catch (err) {
79 | // dirPath is not exist
80 | await mkdirSync(dirPath)
81 | }
82 | }
83 |
84 | /**
85 | * read json
86 | */
87 | function readJson(filePath) {
88 | try {
89 | // eslint-disable-next-line import/no-dynamic-require
90 | const content = require(filePath)
91 | delete require.cache[require.resolve(filePath)]
92 | return content
93 | } catch (err) {
94 | return null
95 | }
96 | }
97 |
98 | /**
99 | * read file
100 | */
101 | async function readFile(filePath) {
102 | try {
103 | return await readFileSync(filePath, 'utf8')
104 | } catch (err) {
105 | // eslint-disable-next-line no-console
106 | return console.error(err)
107 | }
108 | }
109 |
110 | /**
111 | * write file
112 | */
113 | async function writeFile(filePath, data) {
114 | try {
115 | await recursiveMkdir(path.dirname(filePath))
116 | return await writeFileSync(filePath, data, 'utf8')
117 | } catch (err) {
118 | // eslint-disable-next-line no-console
119 | return console.error(err)
120 | }
121 | }
122 |
123 | /**
124 | * time format
125 | */
126 | function format(time, reg) {
127 | const date = typeof time === 'string' ? new Date(time) : time
128 | const map = {}
129 | map.yyyy = date.getFullYear()
130 | map.yy = ('' + map.yyyy).substr(2)
131 | map.M = date.getMonth() + 1
132 | map.MM = (map.M < 10 ? '0' : '') + map.M
133 | map.d = date.getDate()
134 | map.dd = (map.d < 10 ? '0' : '') + map.d
135 | map.H = date.getHours()
136 | map.HH = (map.H < 10 ? '0' : '') + map.H
137 | map.m = date.getMinutes()
138 | map.mm = (map.m < 10 ? '0' : '') + map.m
139 | map.s = date.getSeconds()
140 | map.ss = (map.s < 10 ? '0' : '') + map.s
141 |
142 | return reg.replace(/\byyyy|yy|MM|M|dd|d|HH|H|mm|m|ss|s\b/g, $1 => map[$1])
143 | }
144 |
145 | /**
146 | * logger plugin
147 | */
148 | function logger(action = 'copy') {
149 | return through.obj(function (file, enc, cb) {
150 | const type = path.extname(file.path).slice(1).toLowerCase()
151 |
152 | // eslint-disable-next-line no-console
153 | console.log(`[${format(new Date(), 'yyyy-MM-dd HH:mm:ss').grey}] [${action.green} ${type.green}] ${'=>'.cyan} ${file.path}`)
154 |
155 | this.push(file)
156 | cb()
157 | })
158 | }
159 |
160 | /**
161 | * compare arrays
162 | */
163 | function compareArray(arr1, arr2) {
164 | if (!Array.isArray(arr1) || !Array.isArray(arr2)) return false
165 | if (arr1.length !== arr2.length) return false
166 |
167 | for (let i = 0, len = arr1.length; i < len; i++) {
168 | if (arr1[i] !== arr2[i]) return false
169 | }
170 |
171 | return true
172 | }
173 |
174 | /**
175 | * merge two object
176 | */
177 | function merge(obj1, obj2) {
178 | Object.keys(obj2).forEach(key => {
179 | if (Array.isArray(obj1[key]) && Array.isArray(obj2[key])) {
180 | obj1[key] = obj1[key].concat(obj2[key])
181 | } else if (typeof obj1[key] === 'object' && typeof obj2[key] === 'object') {
182 | obj1[key] = Object.assign(obj1[key], obj2[key])
183 | } else {
184 | obj1[key] = obj2[key]
185 | }
186 | })
187 |
188 | return obj1
189 | }
190 |
191 | /**
192 | * get random id
193 | */
194 | let seed = +new Date()
195 | function getId() {
196 | return ++seed
197 | }
198 |
199 | module.exports = {
200 | wrap,
201 | transformPath,
202 |
203 | checkFileExists,
204 | readJson,
205 | readFile,
206 | writeFile,
207 |
208 | logger,
209 | format,
210 | compareArray,
211 | merge,
212 | getId,
213 | }
214 |
--------------------------------------------------------------------------------