├── .gitignore ├── pages ├── my │ ├── index.wxss │ ├── index.json │ ├── index.wxml │ └── index.js ├── detail │ ├── index.wxss │ ├── index.json │ ├── index.wxml │ └── index.js ├── recycle-list │ ├── index.wxss │ ├── index.json │ ├── index.wxml │ └── index.js ├── map │ ├── index.json │ ├── index.wxml │ ├── index.wxss │ └── index.js ├── index │ ├── index.json │ ├── index.wxss │ ├── index.js │ └── index.wxml ├── address-detail_1 │ ├── index.json │ ├── index.wxml │ ├── index.wxss │ └── index.js ├── address-detail_2 │ ├── index.json │ ├── index.wxss │ ├── index.wxml │ └── index.js └── address-detail_3 │ ├── index.json │ ├── index.wxss │ ├── index.wxml │ └── index.js ├── components ├── area-select │ ├── index.wxss │ ├── index.json │ ├── index.wxml │ └── index.js ├── authorize │ ├── mixin.js │ ├── index.json │ ├── index.wxss │ ├── index.wxml │ └── index.js └── navbar │ ├── index.json │ ├── index.wxml │ ├── index.wxss │ └── index.js ├── images └── marker.png ├── common ├── behavior.js ├── emun.js └── globalMixin.js ├── custom-tab-bar ├── index.wxss ├── index.json ├── index.wxml └── index.js ├── sitemap.json ├── api └── user.js ├── utils ├── app.js ├── request.js ├── mixin.js ├── smartWeChat │ ├── README.md │ └── js │ │ └── address_parse.js ├── authLogin.js ├── utils.js └── qqmap-wx-jssdk.min.js ├── gulpfile.js ├── package.json ├── config.js ├── LICENSE ├── app.json ├── app.js ├── project.private.config.json ├── css └── variables.wxss ├── fonts └── iconfont.wxss ├── project.config.json ├── app.wxss └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /miniprogram_npm/ -------------------------------------------------------------------------------- /pages/my/index.wxss: -------------------------------------------------------------------------------- 1 | /* pages/page1/index.wxss */ -------------------------------------------------------------------------------- /pages/detail/index.wxss: -------------------------------------------------------------------------------- 1 | /* pages/detail/index.wxss */ -------------------------------------------------------------------------------- /pages/detail/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /pages/my/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } -------------------------------------------------------------------------------- /pages/detail/index.wxml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/area-select/index.wxss: -------------------------------------------------------------------------------- 1 | /* components/area-select.wxss */ -------------------------------------------------------------------------------- /pages/recycle-list/index.wxss: -------------------------------------------------------------------------------- 1 | /* pages/recycle-list/index.wxss */ -------------------------------------------------------------------------------- /images/marker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KiteWorld/wx_template/HEAD/images/marker.png -------------------------------------------------------------------------------- /pages/my/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | pages/page1/index.wxml -------------------------------------------------------------------------------- /components/authorize/mixin.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | data: { 3 | isShowAuth: false, 4 | authType: "UserInfo", 5 | } 6 | } -------------------------------------------------------------------------------- /common/behavior.js: -------------------------------------------------------------------------------- 1 | export const commomProp = Behavior({ 2 | data: { 3 | activeColor: "#d70039", 4 | mainColor: "#707070", 5 | } 6 | }) -------------------------------------------------------------------------------- /components/authorize/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "van-dialog": "@vant/weapp/dialog/index" 5 | } 6 | } -------------------------------------------------------------------------------- /pages/map/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "navbar": "/components/navbar/index", 4 | "van-search": "@vant/weapp/search/index" 5 | } 6 | } -------------------------------------------------------------------------------- /pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "van-icon": "@vant/weapp/icon/index", 4 | "authorize": "/components/authorize/index" 5 | } 6 | } -------------------------------------------------------------------------------- /components/area-select/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "van-area": "@vant/weapp/area/index", 5 | "van-popup": "@vant/weapp/popup/index" 6 | } 7 | } -------------------------------------------------------------------------------- /components/navbar/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "van-nav-bar": "@vant/weapp/nav-bar/index", 5 | "van-icon": "@vant/weapp/icon/index" 6 | } 7 | } -------------------------------------------------------------------------------- /custom-tab-bar/index.wxss: -------------------------------------------------------------------------------- 1 | .my-tabbar { 2 | --tabbar-height: 100rpx; 3 | --tabbar-item-font-size: 26rpx; 4 | --tabbar-item-icon-size: 44rpx; 5 | --tabbar-item-margin-bottom: 8rpx 6 | } -------------------------------------------------------------------------------- /sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [{ 4 | "action": "allow", 5 | "page": "*" 6 | }] 7 | } -------------------------------------------------------------------------------- /custom-tab-bar/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "van-tabbar": "@vant/weapp/tabbar/index", 5 | "van-tabbar-item": "@vant/weapp/tabbar-item/index" 6 | } 7 | } -------------------------------------------------------------------------------- /pages/recycle-list/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "recycle-view": "miniprogram-recycle-view/recycle-view", 4 | "recycle-item": "miniprogram-recycle-view/recycle-item" 5 | } 6 | } -------------------------------------------------------------------------------- /common/emun.js: -------------------------------------------------------------------------------- 1 | /* 2 | * 枚举值都可以在这里维护。最保险的方式,其实是请求后端的字典接口来获取对应的映射, 3 | * 这样后端改动时,前端就不需要手动维护了。 4 | */ 5 | export const STATUS = { 6 | "1": "待处理", 7 | "2": "处理中", 8 | "3": "已完成", 9 | "4": "已取消", 10 | } -------------------------------------------------------------------------------- /pages/address-detail_1/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "van-field": "@vant/weapp/field/index", 4 | "van-cell": "@vant/weapp/cell/index", 5 | "van-cell-group": "@vant/weapp/cell-group/index", 6 | "area-select": "/components/area-select/index" 7 | } 8 | } -------------------------------------------------------------------------------- /pages/address-detail_2/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "van-field": "@vant/weapp/field/index", 4 | "van-cell": "@vant/weapp/cell/index", 5 | "van-cell-group": "@vant/weapp/cell-group/index", 6 | "area-select": "/components/area-select/index" 7 | } 8 | } -------------------------------------------------------------------------------- /pages/address-detail_3/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "van-field": "@vant/weapp/field/index", 4 | "van-cell": "@vant/weapp/cell/index", 5 | "van-cell-group": "@vant/weapp/cell-group/index", 6 | "area-select": "/components/area-select/index" 7 | } 8 | } -------------------------------------------------------------------------------- /components/area-select/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /pages/my/index.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | data: { 3 | navConfig: { 4 | title: "page2", 5 | isLeftArrow: false 6 | }, 7 | }, 8 | onLoad: function (options) {}, 9 | onReady: function () {}, 10 | onShow: function () { 11 | //初始化tabbar 12 | this.getTabBar().init() 13 | }, 14 | }) -------------------------------------------------------------------------------- /custom-tab-bar/index.wxml: -------------------------------------------------------------------------------- 1 | 3 | {{ 4 | item.text 5 | }} 6 | -------------------------------------------------------------------------------- /components/authorize/index.wxss: -------------------------------------------------------------------------------- 1 | @import "../../app.wxss"; 2 | 3 | .tips-content { 4 | color: #444444; 5 | padding: 40rpx 10rpx 40rpx 20rpx; 6 | } 7 | 8 | .auth-btn { 9 | width: 500rpx !important; 10 | margin-top: 10rpx; 11 | } 12 | 13 | .tips { 14 | text-align: center; 15 | font-size: 28rpx; 16 | margin-bottom: 20rpx; 17 | color: var(--text-color); 18 | } -------------------------------------------------------------------------------- /pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /* pages/page1/index.wxss */ 2 | .btn { 3 | display: inline-block; 4 | width: 250rpx; 5 | height: 100rpx; 6 | text-align: center; 7 | line-height: 100rpx; 8 | /* CSS变量的使用 */ 9 | border: 1rpx solid var(--border-color); 10 | color: var(--text-color); 11 | border-radius: 10rpx; 12 | padding: 0 20rpx; 13 | box-sizing: border-box; 14 | } -------------------------------------------------------------------------------- /common/globalMixin.js: -------------------------------------------------------------------------------- 1 | export const mixin1 = { 2 | data: { 3 | title1: "mixin1", 4 | activeColor: "#d70039", 5 | mainColor: "#707070", 6 | }, 7 | getTitle1() { 8 | console.log("getTitle1", this.data.title1) 9 | } 10 | } 11 | export const mixin2 = { 12 | data: { 13 | title2: "mixin2" 14 | }, 15 | getTitle2() { 16 | console.log("getTitle2", this.data.title2) 17 | } 18 | } -------------------------------------------------------------------------------- /api/user.js: -------------------------------------------------------------------------------- 1 | import request from "../utils/request" 2 | 3 | //这些接口都不是真实的,只是演示怎么使用 4 | //小程序用户登录 5 | export function login(code, data) { 6 | return request.post(`login?code=${code}`, data, { 7 | noAuth: true 8 | }); 9 | } 10 | 11 | //更新用户信息 12 | export function updateUserInfo(data) { 13 | return request.post("user/profile", data); 14 | } 15 | 16 | //更新用户手机号 17 | export function updatePhone(data) { 18 | return request.post("user/phone", data); 19 | } -------------------------------------------------------------------------------- /utils/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | type toast 类型 3 | content 提示文本 4 | duration 提示持续时长,默认 1秒 5 | opt 支持 showToast 中所以属性,注意:相同属性名会覆盖,opt 属性的优先级最高,基本只有需要回调的时候用到 6 | */ 7 | export function toast(icon, title, duration, opt = {}) { 8 | wx.showToast({ 9 | title: title, 10 | icon: icon, 11 | duration: duration || 1000, 12 | ...opt 13 | }) 14 | } 15 | ["success", "error", "loading", "none"].forEach(type => toast[type] = (title, duration, opt) => toast(type, title, 16 | duration, opt)) -------------------------------------------------------------------------------- /components/navbar/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // gulp 配置文件 2 | var gulp = require('gulp'); 3 | 4 | var postcss = require('gulp-postcss'); 5 | 6 | var pxtorem = require('postcss-pxtransform'); 7 | 8 | gulp.task('css', function () { 9 | 10 | var processors = [ 11 | 12 | pxtorem({ 13 | 14 | platform: 'weapp', 15 | 16 | designWidth: 750, 17 | 18 | }) 19 | 20 | ]; 21 | 22 | 23 | //根据自己的 vant 构建路径来 24 | return gulp.src(['miniprogram_npm/@vant/weapp/**/*.wxss']) 25 | .pipe(postcss(processors)) 26 | .pipe(gulp.dest('miniprogram_npm/@vant/weapp/')); 27 | }); -------------------------------------------------------------------------------- /pages/recycle-list/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 长列表前面的内容 7 | 8 | 9 | {{item.title}} 10 | 11 | 12 | 长列表后面的内容 13 | 14 | 15 | -------------------------------------------------------------------------------- /components/authorize/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 微信授权登录 6 | 7 | 获取手机号进行绑定 9 | 10 | 取消 11 | 12 | 13 | 用户信息仅用于XXXXX 14 | -------------------------------------------------------------------------------- /components/navbar/index.wxss: -------------------------------------------------------------------------------- 1 | /* css 变量用于覆盖 vant组件默认的样式 */ 2 | @import "/app.wxss"; 3 | 4 | .my-navbar { 5 | /* background: url(https://kite1874.com/nav_bg.jpg) no-repeat center center; */ 6 | background: var(--nav-bg-color) no-repeat !important; 7 | background-size: 200% !important; 8 | background-position: 50% 0rpx !important; 9 | --nav-bar-background-color: #fff0; 10 | --nav-bar-icon-color: #fff; 11 | --nav-bar-text-color: #fff; 12 | --nav-bar-arrow-size: 40rpx; 13 | --nav-bar-title-text-color: #fff; 14 | --nav-bar-height: 100rpx; 15 | --nav-bar-title-font-size: 38rpx; 16 | z-index: 9999 !important; 17 | } 18 | 19 | .my-navbar::after { 20 | border: none !important; 21 | } 22 | 23 | /* .my-navbar-title { 24 | color: #fff; 25 | } */ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wx_template", 3 | "version": "1.0.0", 4 | "description": "wx_template", 5 | "main": "app.js", 6 | "dependencies": { 7 | "@vant/weapp": "^1.6.8", 8 | "miniprogram-recycle-view": "^0.1.5" 9 | }, 10 | "devDependencies": { 11 | "gulp": "^4.0.2", 12 | "gulp-postcss": "^9.0.1", 13 | "postcss-pxtransform": "^3.3.5" 14 | }, 15 | "scripts": { 16 | "build": "gulp css", 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/KiteWorld/wx_template.git" 22 | }, 23 | "keywords": [ 24 | "wx_template" 25 | ], 26 | "author": "kiteWorld", 27 | "license": "ISC", 28 | "bugs": { 29 | "url": "https://github.com/KiteWorld/wx_template/issues" 30 | }, 31 | "homepage": "https://github.com/KiteWorld/wx_template#readme" 32 | } 33 | -------------------------------------------------------------------------------- /custom-tab-bar/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | commomProp 3 | } from "../common/behavior" 4 | Component({ 5 | behaviors: [commomProp], 6 | properties: {}, 7 | data: { 8 | selected: 0, 9 | "list": [{ 10 | "url": "/pages/index/index", 11 | "icon": "wap-home-o", 12 | "text": "首页" 13 | }, 14 | { 15 | "url": "/pages/my/index", 16 | "icon": "user-circle-o", 17 | "text": "我的" 18 | } 19 | ], 20 | }, 21 | lifetimes: {}, 22 | methods: { 23 | onChange(e) { 24 | const url = this.data.list[e.detail].url 25 | wx.switchTab({ 26 | url: url 27 | }); 28 | this.setData({ 29 | selected: e.detail 30 | }); 31 | }, 32 | init() { 33 | const page = getCurrentPages().pop(); 34 | this.setData({ 35 | selected: this.data.list.findIndex(item => item.url === `/${page.route}`) 36 | }); 37 | } 38 | } 39 | }) -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | // "release" 正式版 "trial" 体验版 "develop" 开发版 2 | const env = wx.getAccountInfoSync().miniProgram.envVersion 3 | 4 | const HTTP_REQUEST_URL = env === "release" ? "https://kite1874.com/api/" : env === "trial" ? "https://kite1874.com/test/api/" : "https://kite1874.com/dev/api/" 5 | 6 | module.exports = { 7 | //请求接口地址,根URL 8 | HTTP_REQUEST_URL, 9 | 10 | // 腾讯位置服务 Key 11 | MAP_KEY: "VP4BZ-MPBCU-LQFVU-BXR22-CFNTS-WLFZN", 12 | 13 | // 请求头 14 | HEADER: { 15 | 'content-type': 'application/json' 16 | }, 17 | // 回话密钥名称 18 | TOKENNAME: 'X-Access-Token', 19 | //用户信息缓存名称 20 | CACHE_USERINFO: 'USERINFO', 21 | //code 22 | CACHE_CODE: "CODE", 23 | //code获取时间戳 24 | CACHE_CODE_TIME: "CODETIME", 25 | //微信官方称code有效时间为五分钟,保险起见设置 4.5 分钟 26 | CODE_EFFECTIVE_TIME: 45000, 27 | //token缓存名称 28 | CACHE_TOKEN: 'TOKEN', 29 | //token获取时间戳 30 | CACHE_TOKEN_TIME: 'CACHE_TOKEN_TIME', 31 | //token有效时间 32 | TOKEN_EFFECTIVE_TIME: 82800000, 33 | } -------------------------------------------------------------------------------- /pages/detail/index.js: -------------------------------------------------------------------------------- 1 | // pages/detail/index.js 2 | Page({ 3 | 4 | /** 5 | * 页面的初始数据 6 | */ 7 | data: { 8 | navConfig: { 9 | title: "detail", 10 | isLeftArrow: true //返回上一页面 11 | }, 12 | }, 13 | 14 | /** 15 | * 生命周期函数--监听页面加载 16 | */ 17 | onLoad: function (options) { 18 | 19 | }, 20 | 21 | /** 22 | * 生命周期函数--监听页面初次渲染完成 23 | */ 24 | onReady: function () { 25 | 26 | }, 27 | 28 | /** 29 | * 生命周期函数--监听页面显示 30 | */ 31 | onShow: function () { 32 | 33 | }, 34 | 35 | /** 36 | * 生命周期函数--监听页面隐藏 37 | */ 38 | onHide: function () { 39 | 40 | }, 41 | 42 | /** 43 | * 生命周期函数--监听页面卸载 44 | */ 45 | onUnload: function () { 46 | 47 | }, 48 | 49 | /** 50 | * 页面相关事件处理函数--监听用户下拉动作 51 | */ 52 | onPullDownRefresh: function () { 53 | 54 | }, 55 | 56 | /** 57 | * 页面上拉触底事件的处理函数 58 | */ 59 | onReachBottom: function () { 60 | 61 | }, 62 | 63 | /** 64 | * 用户点击右上角分享 65 | */ 66 | onShareAppMessage: function () { 67 | 68 | } 69 | }) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 KiteWorld 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 | -------------------------------------------------------------------------------- /pages/index/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | STATUS 3 | } from "../../common/emun" 4 | import { 5 | mixin2 6 | } from "../../common/globalMixin" 7 | 8 | import { 9 | authLogin 10 | } from "../../utils/authLogin.js" 11 | 12 | 13 | Page({ 14 | //局部 mixin 15 | mixins: [mixin2], 16 | data: { 17 | navConfig: { 18 | title: "page1", 19 | isLeftArrow: false 20 | }, 21 | status: "", 22 | authType: "UserInfo", // 不是 UserInfo 时,显示手机号码授权 23 | showAuth: true, 24 | cWidth: 100, //设置canvas宽高,进行压缩 25 | cHeight: 100, 26 | }, 27 | 28 | onLoad: function (options) { 29 | console.log("mixin1.title1", this.data.title1) 30 | console.log("mixin2.title2", this.data.title2) 31 | console.log("mixin2.title2", this.data.activeColor) 32 | this.getTitle1() 33 | this.getTitle2() 34 | const status = 1 35 | this.setData({ 36 | status: STATUS[status] 37 | }) 38 | authLogin(this) 39 | }, 40 | onReady: function () {}, 41 | onShow: function () { 42 | //初始化tabbar 43 | this.getTabBar().init() 44 | }, 45 | 46 | auth() { 47 | authLogin(this) 48 | }, 49 | testToast() { 50 | // 挂在在 wx 全局对象上,方便调用,类似于 vue.propotype 。 51 | wx.$toast.success("toast演示成功") 52 | } 53 | 54 | }) -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | "pages/index/index", 4 | "pages/my/index", 5 | "pages/detail/index", 6 | "pages/map/index", 7 | "pages/address-detail_1/index", 8 | "pages/address-detail_2/index", 9 | "pages/address-detail_3/index", 10 | "pages/recycle-list/index" 11 | ], 12 | "window": { 13 | "backgroundTextStyle": "light", 14 | "navigationBarBackgroundColor": "#fff", 15 | "navigationBarTitleText": "Weixin", 16 | "navigationBarTextStyle": "white", 17 | "navigationStyle": "custom" 18 | }, 19 | "tabBar": { 20 | "custom": true, 21 | "color": "#000000", 22 | "selectedColor": "#2c4999", 23 | "borderStyle": "white", 24 | "list": [ 25 | { 26 | "pagePath": "pages/index/index", 27 | "text": "首页" 28 | }, 29 | { 30 | "pagePath": "pages/my/index", 31 | "text": "我的" 32 | } 33 | ] 34 | }, 35 | "usingComponents": { 36 | "navbar": "/components/navbar/index", 37 | "van-button": "@vant/weapp/button/index" 38 | }, 39 | "plugins": { 40 | "citySelector": { 41 | "version": "1.0.0", 42 | "provider": "wx63ffb7b7894e99ae" 43 | } 44 | }, 45 | "permission": { 46 | "scope.userLocation": { 47 | "desc": "你的位置信息将用于小程序定位" 48 | } 49 | }, 50 | "style": "v2", 51 | "sitemapLocation": "sitemap.json" 52 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //引入 mixin.js,实现 mixin(混入)功能 2 | import "./utils/mixin" 3 | import { 4 | mixin1 5 | } from "./common/globalMixin" 6 | import address_parse from "./utils/smartWeChat/js/address_parse" 7 | 8 | import { 9 | toast 10 | } from "utils/app.js" 11 | 12 | App({ 13 | onLaunch() { 14 | wx.$toast = toast 15 | wx.getSystemInfo({ 16 | success: (res) => { 17 | this.globalData.statusBarHeight = res.statusBarHeight 18 | } 19 | }) 20 | //版本更新提醒 21 | const updateManager = wx.getUpdateManager(); 22 | 23 | updateManager.onCheckForUpdate(function (res) { 24 | // 请求完新版本信息的回调 25 | }) 26 | 27 | updateManager.onUpdateReady(function () { 28 | wx.showModal({ 29 | title: '更新提示', 30 | content: '新版本已经准备好,是否重启应用?', 31 | success: function (res) { 32 | if (res.confirm) { 33 | // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 34 | updateManager.applyUpdate() 35 | } 36 | } 37 | }) 38 | }); 39 | updateManager.onUpdateFailed(() => { 40 | wx.$toast.error("更新版本失败") 41 | }) 42 | }, 43 | smart: function (val) { 44 | return address_parse.method(val || '') 45 | }, 46 | getAddressData: function () { //手动重新挂载数据 47 | address_parse.getData() 48 | }, 49 | globalData: { 50 | userInfo: null, 51 | token: null, 52 | statusBarHeight: 0, 53 | }, 54 | }) 55 | 56 | // 全局 mixins(混入) 57 | wx.mixin(mixin1) -------------------------------------------------------------------------------- /project.private.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "setting": {}, 3 | "condition": { 4 | "plugin": { 5 | "list": [] 6 | }, 7 | "game": { 8 | "list": [] 9 | }, 10 | "gamePlugin": { 11 | "list": [] 12 | }, 13 | "miniprogram": { 14 | "list": [ 15 | { 16 | "name": "pages/map/index", 17 | "pathName": "pages/map/index", 18 | "query": "", 19 | "scene": null 20 | }, 21 | { 22 | "name": "pages/index/index", 23 | "pathName": "pages/index/index", 24 | "query": "", 25 | "scene": null 26 | }, 27 | { 28 | "name": "pages/address-detail_1/index", 29 | "pathName": "pages/address-detail_1/index", 30 | "query": "", 31 | "scene": null 32 | }, 33 | { 34 | "name": "pages/address-detail_2/index", 35 | "pathName": "pages/address-detail_2/index", 36 | "query": "", 37 | "scene": null 38 | }, 39 | { 40 | "name": "pages/address-detail_3/index", 41 | "pathName": "pages/address-detail_3/index", 42 | "query": "", 43 | "scene": null 44 | }, 45 | { 46 | "name": "pages/recycle-list/index", 47 | "pathName": "pages/recycle-list/index", 48 | "query": "", 49 | "scene": null 50 | } 51 | ] 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /components/navbar/index.js: -------------------------------------------------------------------------------- 1 | Component({ 2 | //my-navbar 和 my-navbar-title 对应 van-navbar 的 custom-class 和 title-class 3 | // externalClasses: ['my-navbar', 'my-navbar-title'], 4 | properties: { 5 | navConfig: { 6 | type: Object, 7 | observer(newVal) { 8 | if (this.data.navConfig.onClickLeft) { 9 | this.onClickLeft = this.data.navConfig.onClickLeft 10 | } 11 | if (this.data.navConfig.onClickRight) { 12 | this.onClickRight = this.data.navConfig.onClickRight 13 | } 14 | 15 | } 16 | }, 17 | addHeight: { 18 | type: Number, 19 | value: 0 20 | } 21 | }, 22 | data: { 23 | navH: 0 24 | }, 25 | options: { 26 | styleIsolation: 'shared' 27 | }, 28 | lifetimes: { 29 | attached: function () { 30 | this.setData({ 31 | navH: getApp().globalData.statusBarHeight 32 | }) 33 | }, 34 | ready: function () {}, 35 | }, 36 | /** 37 | * 组件的方法列表 38 | */ 39 | methods: { 40 | onClickLeft() { 41 | const navParam = {} 42 | if (this.data.navConfig.isNavUrl) { 43 | navParam.url = this.data.navConfig.url 44 | wx.navigateTo(navParam) 45 | } else { 46 | if (getCurrentPages().length <= 1) { 47 | console.log("已经没有上一页") 48 | return 49 | } 50 | navParam.detal = this.data.navConfig.detal || 1 51 | wx.navigateBack(navParam) 52 | } 53 | }, 54 | onClickRight() { 55 | 56 | } 57 | } 58 | }) -------------------------------------------------------------------------------- /pages/recycle-list/index.js: -------------------------------------------------------------------------------- 1 | const createRecycleContext = require('miniprogram-recycle-view') 2 | Page({ 3 | data: { 4 | navConfig: { 5 | title: "虚拟列表", 6 | isLeftArrow: true 7 | }, 8 | recycleList: [], 9 | isLoading: false, 10 | deviceWidth: 320, 11 | itemHeight: 150, 12 | num: 0 13 | }, 14 | async onLoad() { 15 | const deviceWidth = (await wx.getSystemInfo()).screenWidth 16 | this.setData({ 17 | deviceWidth 18 | }) 19 | }, 20 | onReady: async function () { 21 | var ctx = createRecycleContext({ 22 | id: 'recycleId', 23 | dataKey: 'recycleList', 24 | page: this, 25 | itemSize: { // 这个参数也可以直接传下面定义的this.itemSizeFunc函数 26 | width: this.data.deviceWidth, 27 | height: this.data.itemHeight 28 | } 29 | }) 30 | this.ctx = ctx 31 | const list = await this.load() 32 | this.ctx.append(list) 33 | }, 34 | itemSizeFunc: function (item, idx) { 35 | return { 36 | width: 162, 37 | height: 182 38 | } 39 | }, 40 | async scrollLower() { 41 | const list = await this.load() 42 | this.ctx.append(list) 43 | }, 44 | async load() { 45 | return new Promise((resolve, reject) => { 46 | setTimeout(() => { 47 | const list = [] 48 | for (let i = 0; i < 20; i++) { 49 | this.data.num++ 50 | list.push({ 51 | id: this.data.num, 52 | title: 'title' + this.data.num 53 | }) 54 | } 55 | resolve(list) 56 | }, 500) 57 | }) 58 | } 59 | }) -------------------------------------------------------------------------------- /pages/address-detail_1/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 14 | 15 | 16 | 17 | 18 | 保存地址 19 | 20 | 21 | 删除 22 | 23 | 24 | -------------------------------------------------------------------------------- /pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 枚举值演示:{{status}} 4 | 5 | 6 | 全局混入:{{title1}} 7 | 8 | 9 | 局部混入:{{title2}} 10 | 11 | 12 | 13 | iconfont使用: 14 | 15 | 16 | 17 | 18 | 演示自定义导航栏,返回上页功能: 19 | 跳转到详细页 20 | 21 | 22 | 演示授权登录: 23 | 点击授权登录 24 | 25 | 26 | 27 | 省市区联动地址: 28 | 点击查看 29 | 30 | 31 | 地图选点地址: 32 | 点击查看 33 | 34 | 35 | 智能识别地址: 36 | 点击查看 37 | 38 | 39 | Toast封装演示: 40 | 点击测试 41 | 42 | 43 | recycle-list(长列表性能优化时使用): 44 | 点击查看 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /pages/map/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | {{cityName}} 9 | 10 | 11 | 12 | 14 | 您可拖动地图, 标记准确位置 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | {{item.title}} 30 | {{item.addr}} 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /utils/request.js: -------------------------------------------------------------------------------- 1 | import { 2 | HEADER, 3 | TOKENNAME, 4 | CACHE_TOKEN, 5 | HTTP_REQUEST_URL, 6 | } from './../config.js'; 7 | 8 | /* 9 | api:Stirng 接口URL 10 | method :Stirng 请求方法 :get post 11 | data:Object 请求数据 12 | noAuth: Boolean true,不需要携带 token; false,反之 13 | customHeader:自定义请求头(覆盖默认的) 14 | baseURL: 覆盖默认的 baseURL 15 | */ 16 | export default function request(api, method, data, { 17 | noAuth = false, 18 | customHeader = null, 19 | baseURL = '' 20 | }) { 21 | let header = customHeader || HEADER; 22 | const token = wx.getStorageSync(CACHE_TOKEN) 23 | if (!noAuth && token) header[TOKENNAME] = token 24 | return new Promise((reslove, reject) => { 25 | wx.request({ 26 | url: (baseURL || HTTP_REQUEST_URL) + api, 27 | method: method || 'GET', 28 | header: header, 29 | data: data || {}, 30 | success: (res) => { 31 | if (res.statusCode === 401) { 32 | wx.removeStorageSync(CACHE_TOKEN) 33 | wx.showToast({ 34 | type: "loading", 35 | title: '超出登录有效期,重新获取中...', 36 | }) 37 | wx.switchTab({ 38 | url: '/pages/index/index', 39 | }) 40 | reject(null); 41 | } 42 | reslove(res.data || null) 43 | }, 44 | fail: (msg) => { 45 | wx.hideLoading() 46 | wx.showToast({ 47 | icon: "none", 48 | title: "请求失败,服务器异常" 49 | }) 50 | reject(null); 51 | } 52 | }) 53 | }); 54 | } 55 | 56 | ['options', 'get', 'post', 'put', 'head', 'delete', 'trace', 'connect'].forEach((method) => { 57 | request[method] = (api, data, opt) => request(api, method, data, opt || {}) 58 | }); -------------------------------------------------------------------------------- /css/variables.wxss: -------------------------------------------------------------------------------- 1 | page { 2 | /*全局通用的CSS变量 */ 3 | --text-color: #707070; 4 | --text-secondary-color: #c8c9cc; 5 | --border-color: #eee; 6 | --my-user-info-color: #fff; 7 | --main-color: #54565a; 8 | --nav-bg-color: #54565a; 9 | --custom-active-color: #d70039; 10 | --service-item-color: #d70039; 11 | 12 | 13 | /* vant 通用样式覆盖,根据需要进行调整 */ 14 | /* --action-sheet-item-font-size: 36rpx; 15 | --action-sheet-item-line-height: 60rpx; 16 | --font-size-md: 36rpx; 17 | --nav-bar-title-font-size: 38rpx; 18 | --nav-bar-height: 100rpx; 19 | --picker-toolbar-height: 100rpx; 20 | --picker-action-font-size: 38rpx; 21 | --picker-option-font-size: 38rpx; 22 | --picker-confirm-action-color: var(--custom-active-color); 23 | --cell-vertical-padding: 20rpx; 24 | --cell-horizontal-padding: 32rpx; 25 | --cell-font-size: 32rpx; 26 | --cell-line-height: 60rpx; 27 | --cell-icon-size: 40rpx; 28 | --field-icon-size: 40rpx; 29 | --field-clear-icon-size: 40rpx; 30 | --field-error-message-text-font-size: 28rpx; 31 | --padding-base: 10rpx; 32 | --padding-md: 30rpx; 33 | --padding-xs: 18rpx; 34 | --uploader-icon-size: 44rpx; 35 | --tabbar-height: 100rpx; 36 | --tabbar-item-font-size: 26rpx; 37 | --tabbar-item-icon-size: 44rpx; 38 | --tabbar-item-margin-bottom: 8rpx; 39 | --tabbar-height: 100rpx; 40 | --tabbar-item-font-size: 26rpx; 41 | --tabbar-item-icon-size: 44rpx; 42 | --tabbar-item-margin-bottom: 8rpx; 43 | --sidebar-width: 210rpx; 44 | --sidebar-font-size: 32rpx; 45 | --sidebar-padding: 30rpx 20rpx; 46 | --sidebar-selected-border-color: var(--custom-active-color); 47 | --tab-font-size: 30rpx; 48 | --tabs-line-height: 86rpx; 49 | --tabs-bottom-bar-height: 6rpx; 50 | --dialog-width: 80%; */ 51 | } -------------------------------------------------------------------------------- /utils/mixin.js: -------------------------------------------------------------------------------- 1 | //自定义小程序页面 page 的 mixin 功能(不同于 behaviors) 2 | const nativePage = Page 3 | const lifecycle = ['onLoad', 'onReady', 'onShow', 'onHide', 'onUnload', 'onPullDownRefresh', 'onReachBottom', 'onShareAppMessage', 'onPageScroll'] 4 | let globalMixin = null 5 | 6 | //全局mixin方法 7 | wx.mixin = function(config){ 8 | if(isType(config,'object')){ 9 | globalMixin = config 10 | } 11 | } 12 | 13 | //原生Page代理 14 | Page = function (config) { 15 | let mixins = config.mixins 16 | //加入全局mixin 17 | if(globalMixin){ 18 | (mixins || (mixins=[])).unshift(globalMixin) 19 | } 20 | if (isType(mixins, 'array') && mixins.length > 0) { 21 | Reflect.deleteProperty(config, 'mixins') 22 | merge(mixins, config) 23 | } 24 | nativePage(config) 25 | } 26 | 27 | function merge(mixins, config) { 28 | mixins.forEach(mixin => { 29 | if (isType(mixin, 'object')) { 30 | //合并data、生命周期以及其他数据 31 | Object.keys(mixin).forEach(key => { 32 | if (key === 'data') { 33 | config[key] = Object.assign({}, mixin[key], config[key]) 34 | } else if (lifecycle.includes(key)) { 35 | let nativeLifecycle = config[key] 36 | config[key] = function () { 37 | let arg = Array.prototype.slice.call(arguments) 38 | mixin[key].call(this, arg) 39 | return nativeLifecycle && nativeLifecycle.call(this, arg) 40 | } 41 | } else { 42 | config[key] = mixin[key] 43 | } 44 | }) 45 | } 46 | }) 47 | } 48 | 49 | //判断类型工具 50 | function isType(target, type) { 51 | let targetType = Object.prototype.toString.call(target).slice(8, -1).toLowerCase() 52 | type = type.toLowerCase() 53 | return targetType === type 54 | } -------------------------------------------------------------------------------- /fonts/iconfont.wxss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: "iconfont"; 3 | /* Project id 2619442 */ 4 | /* 小程序 url 只支持 base64 */ 5 | src: 6 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAOUAAsAAAAAB5gAAANGAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACCcAqDCIJKATYCJAMICwYABCAFhG0HMxuPBsieg43jCBZ58nS800P9qHy+vOpXKu0xMiCXzoKNpMGXyQLUDdKxCXJ3v35ZagGWxVEkAXFaAgQEV3b5H/eOf0N8vvMupzk3frDiQApsTxuLrEAC84BTkN2DschzQABa+WoxvWX77vA7etmFKCyzmUCSSY7VrIdaUGIbMgk1QokuTZBPAcxlbgGYG3yePBSmBhiKAjOn825hwrl3sdYIOs47zipPMG68Gmo4A1AAKCB7ZWoymo0d0SiYjdJWGVmJdCWLwLtYr9djrvuHBxAgFWa6DwCIoZjnxHLYp0rNAAAWOgNjAIt7PRKgM5WOPNH1QuOFqLDotdE7s87W7UwjS5eEuZYsCu3da9HixbqZ4X17LA715WRlQ8+taVvL0hbHhj4ueFwi0qHYLfwGPmxNmFq0NHz144b7FYv5P0JclLyoGIeSN6Xf68z3bLV0aRi/ZMmiRaE9uvdx3uq8LXtZNpaFLOP5LhXnqjY4/roHdCATf+3PPXb4x7hKdG36S28bpir6/z9pDg7vyqhC9nVJuqTQM4okvGo5arPfLVX18SqRhg8tj+nalqrCaFn+h+i+v4JFxbCqJ3431uoy/H7puABGmRKUFkWezBhN+oU/5cZlJ181nD4/qF2z7oOS1k+qYKN69trR49ZabxeqP0jJNtv6/+eD6l2oBwDe6UZa91++gy3xUeED/av/qnUsAODJr3AhsHtngZkB9YkbCfwFERCV1VwAk1UsIcoYg94YX2t5AC/TGuRZX2no3UYJtdB7NqvEJkCpJSMsPhsUdIpBSa0OtLK0Wq0TTEKR1ADINJkAEWAvMPycBSrAbYTFPwWFMF9BKSAKWnwEb6mTmlXbTBL0smDiDMM5q9HtEm2NrA0zuwrmwQ69lOYEBF6QmqweriA3Px9pJbgEaYwZTUPMhbIscqLkdnItyWmCw+HmPJLbJhjlXIsseyrz8sSqF+Ua3U5oNSMR6MkEJhyD4ThWRm4uotdapi28vyuB2WAOelJDQ5WWJ5A0sXbPKZArH0BaCV1Qw7Vc0mQIs0IymYgjkrg5cVoSvcBhBd04nupBNgIjWS5LD79HpTwaTUSFufOLnU+3DUDLlDFh5VNotuAyj7C4CgAAAA==') format('woff2'), 7 | url('iconfont.woff?t=1624008421612') format('woff'), 8 | url('iconfont.ttf?t=1624008421612') format('truetype'); 9 | } 10 | 11 | .iconfont { 12 | font-family: "iconfont" !important; 13 | font-size: 16px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | .icon-fengzheng:before { 20 | content: "\e61d"; 21 | } -------------------------------------------------------------------------------- /utils/smartWeChat/README.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # 建议使用api接口,此方法暂不推荐 9 | ## demo 10 | 11 | 详见```demo```文件夹 12 | 13 | ![](https://gitee.com/Wzhichao/img/raw/master/uPic/HiovfR25%20.png) 14 | 15 | ## 小程序引入 16 | 务必勾选不检验域名等等 17 | ![](https://gitee.com/Wzhichao/img/raw/master/uPic/q50LEr14%20.png) 18 | 19 | 将仓库中的```smartWeChat```文件夹拷贝到项目中```app.js```的同级目录 20 | 21 | ![image.png](https://gitee.com/Wzhichao/img/raw/master/uPic/P2DFuD45%20.png) 22 | 23 | > smartWeChat/js/address_parse.js(自建后台) 24 | 25 | 如需要自行构建后台,json文件在demo/后台json/database_export-sw0HKSJkxA1j.json 26 | 27 | 这里需要将demo里的接口替换为后台提供的接口,接口格式返回可以参考https://wangzc.wang/addressJson/1 28 | 29 | 后台json文件···demo/后台json/database_export-sw0HKSJkxA1j.json``` 30 | 31 | > app.js 32 | ``` 33 | var address_parse = require("./smartWeChat/js/address_parse"); 34 | 35 | ... 36 | ... 37 | ... 38 | 39 | App({ 40 | .... 41 | smart: function (val){ 42 | return address_parse.method(val || '') 43 | }, 44 | getAddressData:function(){//手动重新挂载数据 45 | address_parse.getData() 46 | } 47 | }) 48 | 49 | ``` 50 | 51 | > 调用.js 52 | ``` 53 | const app = getApp() 54 | //注意!!省市区文件加载时间可能略长 55 | //需要识别调用 56 | app.smart('新疆阿克苏温宿县博孜墩柯尔克孜族乡吾斯塘博村一组306号 150-3569-6956 马云') 57 | 58 | //ex 59 | //这里改为事件触发即可 60 | onLoad: function() { 61 | setTimeout(function(){ 62 | app.getAddressData()//保险起见,手动挂载数据 63 | var address = app.smart('广东省珠海市香洲区盘山路28号幸福茶庄,陈景勇,13593464918') 64 | console.log(address) 65 | },10000) 66 | } 67 | 68 | ``` 69 | ### 数据源跟换 70 | 71 | 由于小程序限制文件大小不能超过2MB,所以数据以接口返回,若需要更新请加群联系作者 72 | 73 | ### 注,初次加载会调用接口请求数据,后续会从缓存中读取 74 | ### 接口地址 https://wangzc.wang/addressJson/x 75 | -------------------------------------------------------------------------------- /project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": false, 8 | "es6": true, 9 | "enhance": true, 10 | "postcss": true, 11 | "preloadBackgroundData": false, 12 | "minified": true, 13 | "newFeature": false, 14 | "coverView": true, 15 | "nodeModules": true, 16 | "autoAudits": false, 17 | "showShadowRootInWxmlPanel": false, 18 | "scopeDataCheck": false, 19 | "uglifyFileName": false, 20 | "checkInvalidKey": true, 21 | "checkSiteMap": true, 22 | "uploadWithSourceMap": true, 23 | "compileHotReLoad": false, 24 | "useMultiFrameRuntime": true, 25 | "useApiHook": true, 26 | "useApiHostProcess": true, 27 | "babelSetting": { 28 | "ignore": [], 29 | "disablePlugins": [], 30 | "outputPath": "" 31 | }, 32 | "enableEngineNative": false, 33 | "useIsolateContext": true, 34 | "userConfirmedBundleSwitch": false, 35 | "packNpmManually": false, 36 | "packNpmRelationList": [], 37 | "minifyWXSS": true, 38 | "showES6CompileOption": false 39 | }, 40 | "compileType": "miniprogram", 41 | "libVersion": "2.15.0", 42 | "appid": "wxd1ae1593ac877f86", 43 | "projectname": "wx_template", 44 | "debugOptions": { 45 | "hidedInDevtools": [] 46 | }, 47 | "scripts": {}, 48 | "staticServerOptions": { 49 | "baseURL": "", 50 | "servePath": "" 51 | }, 52 | "isGameTourist": false, 53 | "condition": { 54 | "search": { 55 | "list": [] 56 | }, 57 | "conversation": { 58 | "list": [] 59 | }, 60 | "game": { 61 | "list": [] 62 | }, 63 | "plugin": { 64 | "list": [] 65 | }, 66 | "gamePlugin": { 67 | "list": [] 68 | }, 69 | "miniprogram": { 70 | "list": [] 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /pages/address-detail_1/index.wxss: -------------------------------------------------------------------------------- 1 | @import '/app.wxss'; 2 | 3 | .smart-address-container { 4 | background: #fff; 5 | } 6 | 7 | .form-container { 8 | --cell-horizontal-padding: 30rpx; 9 | --padding-xs: 18rpx 10 | } 11 | 12 | .smart-address-title { 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | height: 80rpx; 17 | font-size: 32rpx; 18 | color: #aaaaaa; 19 | } 20 | 21 | .smart-input-root .van-cell__value { 22 | font-size: 32rpx; 23 | background: #eee !important; 24 | border-radius: 10rpx !important; 25 | overflow: hidden !important; 26 | } 27 | 28 | .smart-input { 29 | height: 300rpx !important; 30 | background: #eee !important; 31 | padding: 20rpx !important; 32 | border-radius: 10rpx !important; 33 | 34 | } 35 | 36 | .smart-address { 37 | display: flex; 38 | flex-direction: column; 39 | 40 | /* justify-content: flex-end; */ 41 | 42 | } 43 | 44 | .smart-address-btn { 45 | display: flex; 46 | justify-content: flex-end; 47 | margin-bottom: 10rpx; 48 | } 49 | 50 | .smart-address-btn view { 51 | font-size: 32rpx; 52 | padding: 6rpx 10rpx; 53 | width: 100rpx; 54 | text-align: center; 55 | border-radius: 999em; 56 | } 57 | 58 | .smart-address-btn view:nth-child(1) { 59 | border: 1rpx solid #aaaaaa; 60 | color: #aaaaaa; 61 | } 62 | 63 | .smart-address-btn view:nth-child(2) { 64 | width: 150rpx; 65 | margin: 0 20rpx; 66 | background: var(--main-color); 67 | color: #fff; 68 | } 69 | 70 | .save-btn, 71 | .del-btn { 72 | width: 80%; 73 | margin: 40rpx auto 0rpx; 74 | border-radius: 999em; 75 | padding: 16rpx 0; 76 | font-size: 36rpx; 77 | text-align: center; 78 | background: var(--main-color); 79 | color: #fff; 80 | } 81 | 82 | .del-btn { 83 | background: #cfcfcf; 84 | color: #949494; 85 | } 86 | 87 | .btn-contianer { 88 | margin-top: 60rpx; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /pages/address-detail_2/index.wxss: -------------------------------------------------------------------------------- 1 | @import '/app.wxss'; 2 | 3 | .smart-address-container { 4 | background: #fff; 5 | } 6 | 7 | .form-container { 8 | --cell-horizontal-padding: 30rpx; 9 | --padding-xs: 18rpx 10 | } 11 | 12 | .smart-address-title { 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | height: 80rpx; 17 | font-size: 32rpx; 18 | color: #aaaaaa; 19 | } 20 | 21 | .smart-input-root .van-cell__value { 22 | font-size: 32rpx; 23 | background: #eee !important; 24 | border-radius: 10rpx !important; 25 | overflow: hidden !important; 26 | } 27 | 28 | .smart-input { 29 | height: 300rpx !important; 30 | background: #eee !important; 31 | padding: 20rpx !important; 32 | border-radius: 10rpx !important; 33 | 34 | } 35 | 36 | .smart-address { 37 | display: flex; 38 | flex-direction: column; 39 | 40 | /* justify-content: flex-end; */ 41 | 42 | } 43 | 44 | .smart-address-btn { 45 | display: flex; 46 | justify-content: flex-end; 47 | margin-bottom: 10rpx; 48 | } 49 | 50 | .smart-address-btn view { 51 | font-size: 32rpx; 52 | padding: 6rpx 10rpx; 53 | width: 100rpx; 54 | text-align: center; 55 | border-radius: 999em; 56 | } 57 | 58 | .smart-address-btn view:nth-child(1) { 59 | border: 1rpx solid #aaaaaa; 60 | color: #aaaaaa; 61 | } 62 | 63 | .smart-address-btn view:nth-child(2) { 64 | width: 150rpx; 65 | margin: 0 20rpx; 66 | background: var(--main-color); 67 | color: #fff; 68 | } 69 | 70 | .save-btn, 71 | .del-btn { 72 | width: 80%; 73 | margin: 40rpx auto 0rpx; 74 | border-radius: 999em; 75 | padding: 16rpx 0; 76 | font-size: 36rpx; 77 | text-align: center; 78 | background: var(--main-color); 79 | color: #fff; 80 | } 81 | 82 | .del-btn { 83 | background: #cfcfcf; 84 | color: #949494; 85 | } 86 | 87 | .btn-contianer { 88 | margin-top: 60rpx; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /pages/address-detail_3/index.wxss: -------------------------------------------------------------------------------- 1 | @import '/app.wxss'; 2 | 3 | .smart-address-container { 4 | background: #fff; 5 | } 6 | 7 | .form-container { 8 | --cell-horizontal-padding: 30rpx; 9 | --padding-xs: 18rpx 10 | } 11 | 12 | .smart-address-title { 13 | display: flex; 14 | justify-content: center; 15 | align-items: center; 16 | height: 80rpx; 17 | font-size: 32rpx; 18 | color: #aaaaaa; 19 | } 20 | 21 | .smart-input-root .van-cell__value { 22 | font-size: 32rpx; 23 | background: #eee !important; 24 | border-radius: 10rpx !important; 25 | overflow: hidden !important; 26 | } 27 | 28 | .smart-input { 29 | height: 300rpx !important; 30 | background: #eee !important; 31 | padding: 20rpx !important; 32 | border-radius: 10rpx !important; 33 | 34 | } 35 | 36 | .smart-address { 37 | display: flex; 38 | flex-direction: column; 39 | 40 | /* justify-content: flex-end; */ 41 | 42 | } 43 | 44 | .smart-address-btn { 45 | display: flex; 46 | justify-content: flex-end; 47 | margin-bottom: 10rpx; 48 | } 49 | 50 | .smart-address-btn view { 51 | font-size: 32rpx; 52 | padding: 6rpx 10rpx; 53 | width: 100rpx; 54 | text-align: center; 55 | border-radius: 999em; 56 | } 57 | 58 | .smart-address-btn view:nth-child(1) { 59 | border: 1rpx solid #aaaaaa; 60 | color: #aaaaaa; 61 | } 62 | 63 | .smart-address-btn view:nth-child(2) { 64 | width: 150rpx; 65 | margin: 0 20rpx; 66 | background: var(--main-color); 67 | color: #fff; 68 | } 69 | 70 | .save-btn, 71 | .del-btn { 72 | width: 80%; 73 | margin: 40rpx auto 0rpx; 74 | border-radius: 999em; 75 | padding: 16rpx 0; 76 | font-size: 36rpx; 77 | text-align: center; 78 | background: var(--main-color); 79 | color: #fff; 80 | } 81 | 82 | .del-btn { 83 | background: #cfcfcf; 84 | color: #949494; 85 | } 86 | 87 | .btn-contianer { 88 | margin-top: 60rpx; 89 | } 90 | 91 | -------------------------------------------------------------------------------- /pages/address-detail_2/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 10 | 13 | 16 | 17 | 18 | 19 | 保存地址 20 | 21 | 22 | 删除 23 | 24 | 25 | 26 | 点击「服务地址」跳转地图选点页面, 开发者工具无法显示地图!「点击预览」在手机上查看! 27 | 28 | -------------------------------------------------------------------------------- /components/area-select/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | MAP_KEY 3 | } from "../../config.js" //腾讯位置服务的 Key 4 | var QQMapWX = require('../../utils/qqmap-wx-jssdk.min'); // 引入 SDK 文件 5 | var qqmapsdk; // SDK实例对象 6 | Component({ 7 | properties: {}, 8 | data: { 9 | isShowAreaSelect: false, 10 | areaList: { 11 | // 变量名称是 van-area 规定写死的,不能换!不能换!不能换! 12 | province_list: {}, //省 13 | city_list: {}, //市 14 | county_list: {} //区 15 | }, 16 | // areaSource: [] 17 | }, 18 | lifetimes: { 19 | attached() { 20 | //创建SDK实例 21 | qqmapsdk = new QQMapWX({ 22 | key: MAP_KEY 23 | }); 24 | //调用 getCityList 25 | qqmapsdk.getCityList({ 26 | success: (res) => { //成功后的回调 27 | // this.data.areaSource = res.result 28 | this.setData({ 29 | "areaList.province_list": this.ArrayToObject(res.result[0]), 30 | "areaList.city_list": this.ArrayToObject(res.result[1]), 31 | "areaList.county_list": this.ArrayToObject(res.result[2]), 32 | }) 33 | }, 34 | fail: function (error) { 35 | console.error(error); 36 | }, 37 | complete: function (res) { 38 | console.log(res); 39 | } 40 | }); 41 | } 42 | }, 43 | methods: { 44 | //确认选择 45 | confirmArea: function (e) { 46 | const values = e.detail.values 47 | //直辖市,需要处理数据,保持省市一致,例如,省:北京市;市:北京市;区:朝阳区 48 | if (values.some(x => !Boolean(x))) { 49 | [values[1], values[2]] = [values[0], values[1]]; 50 | } 51 | const arr = (values.map(x => x.name)) 52 | this.triggerEvent("confirm", arr) 53 | this.setData({ 54 | isShowAreaSelect: false, 55 | }) 56 | }, 57 | 58 | //关闭省市区选择组件 59 | closeAreaSelect: function () { 60 | this.setData({ 61 | isShowAreaSelect: false 62 | }) 63 | }, 64 | 65 | //打开省市区选择组件 66 | showAreaSelect: function () { 67 | this.setData({ 68 | isShowAreaSelect: true 69 | }) 70 | }, 71 | 72 | // 格式化省市区数据 73 | ArrayToObject(arr) { 74 | const obj = {} 75 | for (let i = 0; i < arr.length; i++) { 76 | obj[arr[i].id] = arr[i].fullname 77 | } 78 | return obj 79 | } 80 | } 81 | }) -------------------------------------------------------------------------------- /pages/address-detail_3/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 13 | 14 | 15 | 19 | 28 | 29 | 30 | 31 | 保存地址 32 | 33 | 34 | 删除 35 | 36 | 37 | 38 | 开发者工具无法显示地图!「点击预览」在手机上查看! 39 | 40 | 41 | -------------------------------------------------------------------------------- /pages/map/index.wxss: -------------------------------------------------------------------------------- 1 | @import "../../app.wxss"; 2 | 3 | page { 4 | overflow: hidden; 5 | } 6 | 7 | .cityName { 8 | height: 100%; 9 | min-width: 120rpx; 10 | max-width: 160rpx; 11 | font-size: 30rpx; 12 | padding: 0 14rpx; 13 | box-sizing: border-box; 14 | border-right: 1rpx solid var(--border-color); 15 | margin-right: 14rpx; 16 | text-align: center; 17 | } 18 | 19 | .current-site-icon { 20 | width: 50rpx; 21 | height: 50rpx; 22 | position: absolute; 23 | top: 50%; 24 | left: 50%; 25 | transform: translate(-50%, -50%); 26 | } 27 | 28 | .near-item { 29 | display: flex; 30 | line-height: 40rpx; 31 | padding: 20rpx 30rpx; 32 | text-align: left; 33 | border-bottom: 1px solid #eee; 34 | } 35 | 36 | .current-site { 37 | font-size: 40rpx; 38 | margin-right: 30rpx; 39 | } 40 | 41 | .item-main { 42 | flex: 1; 43 | } 44 | 45 | .near-item .title { 46 | color: #282828; 47 | font-size: 32rpx; 48 | white-space: nowrap; 49 | overflow: hidden; 50 | text-overflow: ellipsis; 51 | } 52 | 53 | .near-item .address { 54 | color: #707070; 55 | font-size: 24rpx; 56 | white-space: nowrap; 57 | overflow: hidden; 58 | text-overflow: ellipsis; 59 | } 60 | 61 | .near-item .activeTitle { 62 | color: var(--custom-active-color); 63 | } 64 | 65 | .near-item .activeAddress { 66 | color: var(--custom-active-color); 67 | } 68 | 69 | .map-prompt { 70 | width: 420rpx; 71 | height: 60rpx; 72 | line-height: 60rpx; 73 | font-size: 24rpx; 74 | color: #707070; 75 | text-align: center; 76 | background: #fff; 77 | border-radius: 10rpx; 78 | box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1); 79 | position: absolute; 80 | bottom: 40rpx; 81 | left: 50%; 82 | transform: translate(-50%, 0); 83 | } 84 | 85 | .reload { 86 | width: 80rpx; 87 | height: 80rpx; 88 | background: #fff; 89 | border-radius: 50%; 90 | box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1); 91 | position: absolute; 92 | bottom: 30rpx; 93 | right: 30rpx; 94 | } 95 | 96 | .reload .center1 { 97 | width: 30rpx; 98 | height: 30rpx; 99 | border: 1rpx solid var(--custom-active-color); 100 | border-radius: 50%; 101 | margin: 24rpx auto; 102 | } 103 | 104 | .reload .center2 { 105 | width: 25rpx; 106 | height: 25rpx; 107 | background: var(--custom-active-color); 108 | border-radius: 50%; 109 | margin: 3rpx auto; 110 | } -------------------------------------------------------------------------------- /utils/authLogin.js: -------------------------------------------------------------------------------- 1 | import { 2 | CACHE_USERINFO, 3 | CACHE_TOKEN, 4 | APP_ID, 5 | CACHE_CODE, 6 | CACHE_CODE_TIME, 7 | CACHE_TOKEN_TIME, 8 | CODE_EFFECTIVE_TIME, 9 | TOKEN_EFFECTIVE_TIME, 10 | } from "../config" 11 | 12 | const { 13 | login 14 | } = require("../api/user"); 15 | 16 | export async function authLogin(instance) { 17 | const cacheUserInfo = wx.getStorageSync(CACHE_USERINFO) 18 | let userInfo = cacheUserInfo ? JSON.parse(cacheUserInfo) : {} 19 | getApp().globalData.userInfo = userInfo 20 | 21 | //有token时,不需要重新登录 22 | const token = wx.getStorageSync(CACHE_TOKEN) 23 | const tokenTime = wx.getStorageSync(CACHE_TOKEN_TIME) 24 | if (token && (tokenTime + TOKEN_EFFECTIVE_TIME > (new Date()).getTime())) { 25 | return authMain(instance, userInfo) 26 | } 27 | // 防止第一次经入小程序时,反复调用 wx.login,超出频率规范 28 | const code = wx.getStorageSync(CACHE_CODE) 29 | const codeTime = wx.getStorageSync(CACHE_CODE_TIME) 30 | if (code && (codeTime + CODE_EFFECTIVE_TIME > (new Date()).getTime())) { 31 | const loginRes = await loginMain(res.code) 32 | return authMain(instance, loginRes.result) 33 | } 34 | wx.login({ 35 | success: async (res) => { 36 | if (res.code) { 37 | const loginRes = await loginMain(res.code) 38 | authMain(instance, loginRes.result) 39 | } else { 40 | console.log('登录失败!' + res.errMsg) 41 | } 42 | } 43 | }) 44 | } 45 | //调用 token 和 储存用户token等信息 46 | async function loginMain(code) { 47 | const loginRes = await login(APP_ID, code) 48 | wx.setStorageSync(CACHE_TOKEN, loginRes.result.token) 49 | wx.setStorageSync(CACHE_TOKEN_TIME, (new Date()).getTime()) 50 | wx.setStorageSync(CACHE_CODE, code) 51 | wx.setStorageSync(CACHE_CODE_TIME, (new Date()).getTime()) 52 | wx.setStorageSync(CACHE_USERINFO, JSON.stringify(loginRes.result)) 53 | const globalData = getApp().globalData 54 | globalData.userInfo = loginRes.result || {} 55 | return loginRes 56 | } 57 | 58 | async function authMain(instance, loginRes) { 59 | //判断用户名是否为空,弹出授权窗口 60 | if (!loginRes.wxNickname) { 61 | instance.setData({ 62 | authType: "UserInfo", 63 | isShowAuth: true 64 | }) 65 | return 66 | } 67 | //判断用户名是否为空,弹出授权窗口 68 | if (!loginRes.wxMobile) { 69 | instance.setData({ 70 | authType: "PhoneNumber", 71 | isShowAuth: true, 72 | }) 73 | return 74 | } 75 | //用户名和手机号码都不为空证明已经授权过了 76 | //这里可以调用首页需要的 api 77 | } -------------------------------------------------------------------------------- /components/authorize/index.js: -------------------------------------------------------------------------------- 1 | // components/authorize/index.js 2 | import { 3 | CACHE_USERINFO, 4 | } from "../../config" 5 | import { 6 | updateUserInfo, 7 | updatePhone 8 | } from "../../api/user" 9 | Component({ 10 | properties: { 11 | type: { 12 | type: String, 13 | value: "UserInfo" 14 | }, 15 | showAuth: { 16 | type: Boolean, 17 | value: false 18 | }, 19 | }, 20 | 21 | data: { 22 | loading: false, 23 | }, 24 | /** 25 | * 组件的方法列表 26 | */ 27 | lifetimes: {}, 28 | methods: { 29 | getUserProfile() { 30 | wx.getUserProfile({ 31 | lang: 'zh_CN', 32 | desc: "微信授权登录", 33 | success: async (res) => { 34 | //传给后端的参数 35 | let param = { 36 | encryptedData: { 37 | encryptedData: res.encryptedData, 38 | iv: res.iv 39 | }, 40 | rawData: res.rawData, 41 | signature: res.signature, 42 | userInfo: res.userInfo 43 | } 44 | //调用更新接口 45 | let updateRes = await this.update(updateUserInfo, param) 46 | if (!updateRes) return 47 | // 弹出手机号码授权窗口 48 | this.setData({ 49 | showAuth: "PhoneNumber", 50 | type: true, 51 | }); 52 | }, 53 | fail(res) { 54 | console.log(res) 55 | } 56 | }) 57 | }, 58 | async getPhoneNumber(e) { 59 | if (e.detail.errMsg === "getPhoneNumber:ok") { 60 | //传给后端的参数 61 | const param = { 62 | encryptedData: { 63 | encryptedData: e.detail.encryptedData, 64 | iv: e.detail.iv 65 | }, 66 | } 67 | //调用接口 68 | let updateRes = await this.update(updatePhone, param) 69 | if (!updateRes) return 70 | //关闭隐藏接口 71 | this.hide() 72 | } 73 | }, 74 | onCancel() { 75 | this.hide() 76 | }, 77 | hide() { 78 | this.setData({ 79 | showAuth: false 80 | }) 81 | }, 82 | //更新用户信息 83 | async update(updateApi, param) { 84 | this.setData({ 85 | loading: true 86 | }) 87 | let updateRes = await updateApi(param) 88 | this.setData({ 89 | loading: false 90 | }) 91 | if (updateRes.code !== 200) { 92 | wx.showToast({ 93 | icon: "error", 94 | title: "更新失败", 95 | }) 96 | return false 97 | } 98 | //更新本地保存 userInfo 数据 99 | getApp().globalData.userInfo = updateRes.result || {} 100 | wx.setStorageSync(CACHE_USERINFO, JSON.stringify(updateRes.result)) 101 | return updateRes 102 | } 103 | } 104 | }) -------------------------------------------------------------------------------- /pages/address-detail_1/index.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | data: { 3 | //导航栏配置 4 | navConfig: { 5 | title: "地址详细", 6 | isLeftArrow: true 7 | }, 8 | 9 | //表单数据 10 | formData: { 11 | contactName: '', 12 | contactTel: "", 13 | county: "", 14 | province: "", 15 | city: "", 16 | addressDetail: "", 17 | }, 18 | isShowAreaSelect: false, 19 | phoneNumberError: "", 20 | phoneNumbrRegExp: /^(?:(?:\+|00)86)?1[3-9]\d{9}$/ 21 | }, 22 | 23 | onLoad: function (options) { 24 | if (options.address) { 25 | this.setData({ 26 | formData: JSON.parse(decodeURIComponent(options.address)), 27 | }) 28 | } 29 | }, 30 | 31 | changeContactName({ 32 | detail 33 | }) { 34 | this.setData({ 35 | "formData.contactName": detail, 36 | }) 37 | }, 38 | 39 | changeContactTel({ 40 | detail 41 | }) { 42 | const param = { 43 | "formData.contactTel": detail, 44 | } 45 | if (detail.length === 11 && /^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(detail)) { 46 | param.phoneNumberError = "" 47 | } 48 | this.setData(param) 49 | }, 50 | 51 | changeHouseMumber({ 52 | detail 53 | }) { 54 | this.setData({ 55 | "formData.houseNumber": detail, 56 | }) 57 | }, 58 | 59 | confirmArea: function ({ 60 | detail 61 | }) { 62 | this.setData({ 63 | "formData.province": detail[0], 64 | "formData.city": detail[1], 65 | "formData.county": detail[2], 66 | }) 67 | }, 68 | 69 | changeAddressDetail({ 70 | detail 71 | }) { 72 | this.setData({ 73 | "formData.addressDetail": detail 74 | }) 75 | }, 76 | 77 | getLocation() { 78 | const { 79 | longitudeLatitude 80 | } = this.data.formData 81 | wx.navigateTo({ 82 | url: '../../pages/map/index?longitudeLatitude=' + (longitudeLatitude || ''), 83 | }) 84 | }, 85 | 86 | changeSmartAddress(e) { 87 | this.setData({ 88 | smartAddress: e.detail 89 | }) 90 | }, 91 | 92 | async saveAddress() { 93 | const formData = this.data.formData 94 | for (const key in formData) { 95 | if (formData.hasOwnProperty(key)) { 96 | if (!formData[key]) { 97 | wx.showToast({ 98 | icon: "none", 99 | title: '请填写完整信息', 100 | }) 101 | return 102 | } 103 | } 104 | } 105 | if (!(/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(formData.contactTel))) { 106 | wx.showToast({ 107 | icon: "none", 108 | title: '手机号格式错误', 109 | }) 110 | this.setData({ 111 | "phoneNumberError": "手机号格式错误" 112 | }) 113 | } 114 | }, 115 | 116 | async delAddress() { 117 | if (res.code === 200) { 118 | wx.navigateBack({ 119 | delta: 1, 120 | }) 121 | } 122 | }, 123 | 124 | showAreaSelect: function () { 125 | this.selectComponent("#area-select").showAreaSelect(); 126 | }, 127 | }) -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- 1 | @import "/css/variables.wxss"; 2 | @import "/fonts/iconfont.wxss"; 3 | 4 | page { 5 | font-size: 32rpx; 6 | /* font-family: sans-serif; */ 7 | background: #f5f5f5; 8 | box-sizing: border-box; 9 | /* IOS底部安全距离 */ 10 | padding-bottom: constant(safe-area-inset-bottom); 11 | /*兼容 IOS<11.2*/ 12 | padding-bottom: env(safe-area-inset-bottom); 13 | /*兼容 IOS>11.2*/ 14 | } 15 | 16 | /*共公样式 */ 17 | .all_center { 18 | position: absolute; 19 | left: 50%; 20 | top: 50%; 21 | transform: translate(-50%, -50%); 22 | } 23 | 24 | /* 垂直居中 */ 25 | .y_center { 26 | position: absolute; 27 | top: 50%; 28 | transform: translateY(-50%); 29 | } 30 | 31 | /* 水平居中 */ 32 | .x_center { 33 | position: absolute; 34 | left: 50%; 35 | transform: translateX(-50%); 36 | } 37 | 38 | /* flex 布局 */ 39 | /* 横轴 */ 40 | .flex_r { 41 | display: flex; 42 | flex-direction: row; 43 | } 44 | 45 | /* 横轴水平居中 */ 46 | .flex_r_h { 47 | display: flex; 48 | flex-direction: row; 49 | justify-content: center; 50 | } 51 | 52 | /* 横轴垂直居中 */ 53 | .flex_r_v { 54 | display: flex; 55 | flex-direction: row; 56 | align-items: center; 57 | } 58 | 59 | /* 横轴水平垂直居中 */ 60 | .flex_r_hv { 61 | display: flex; 62 | flex-direction: row; 63 | justify-content: center; 64 | align-items: center; 65 | } 66 | 67 | /* 纵轴 */ 68 | .flex_c { 69 | display: flex; 70 | flex-direction: column; 71 | } 72 | 73 | /* 纵轴水平 */ 74 | .flex_c_h { 75 | display: flex; 76 | flex-direction: column; 77 | align-items: center; 78 | } 79 | 80 | /* 纵轴垂直 */ 81 | .flex_c_v { 82 | display: flex; 83 | flex-direction: column; 84 | justify-content: center; 85 | } 86 | 87 | /* 纵轴水平垂直 */ 88 | .flex_c_hv { 89 | display: flex; 90 | flex-direction: column; 91 | justify-content: center; 92 | align-items: center; 93 | } 94 | 95 | 96 | /* 1px */ 97 | .scale-1px { 98 | position: relative; 99 | border: none; 100 | } 101 | 102 | .scale-1px:after { 103 | content: ''; 104 | position: absolute; 105 | bottom: 0; 106 | left: 0; 107 | background: var(--border-color); 108 | width: 100%; 109 | height: 1px; 110 | -webkit-transform: scaleY(0.5); 111 | transform: scaleY(0.5); 112 | -webkit-transform-origin: 0 0; 113 | transform-origin: 0 0; 114 | } 115 | 116 | /* 单行超出省略; */ 117 | .ellipsis_1 { 118 | overflow: hidden; 119 | text-overflow: ellipsis; 120 | display: -webkit-box; 121 | -webkit-line-clamp: 1; 122 | -webkit-box-orient: vertical; 123 | word-break: break-all; 124 | } 125 | 126 | .ellipsis_2 { 127 | overflow: hidden; 128 | text-overflow: ellipsis; 129 | display: -webkit-box; 130 | -webkit-line-clamp: 2; 131 | -webkit-box-orient: vertical; 132 | word-break: break-all; 133 | } 134 | 135 | .huanhang { 136 | display: block; 137 | word-break: break-all; 138 | overflow-wrap: break-word; 139 | } 140 | 141 | /* 底部占位 */ 142 | .tabbar-fit-box { 143 | height: var(--tabbar-height); 144 | } 145 | 146 | /* 客服按钮 */ 147 | .contactButton { 148 | position: absolute; 149 | top: 0; 150 | left: 0; 151 | width: 100%; 152 | height: 100%; 153 | opacity: 0; 154 | } 155 | 156 | /* 按钮 */ 157 | .custom-btn-container { 158 | width: 80%; 159 | margin: 20rpx auto; 160 | } 161 | 162 | .submit { 163 | --button-normal-font-size: 32rpx; 164 | --button-default-height: 80rpx; 165 | width: 100% !important; 166 | } 167 | 168 | /* 布局通用样式 */ 169 | .container { 170 | height: 100vh; 171 | width: 100vw; 172 | display: flex; 173 | flex-direction: column; 174 | overflow: hidden; 175 | box-sizing: border-box; 176 | } 177 | 178 | .scroll { 179 | flex: 1; 180 | overflow: hidden; 181 | } 182 | 183 | /* 弹出选择 样式覆盖 */ 184 | .van-action-sheet__cancel, 185 | .van-action-sheet__item { 186 | padding: 20rpx 30rpx !important 187 | } -------------------------------------------------------------------------------- /pages/address-detail_3/index.js: -------------------------------------------------------------------------------- 1 | const app = getApp() 2 | Page({ 3 | data: { 4 | //导航栏配置 5 | navConfig: { 6 | title: "地址详细", 7 | isLeftArrow: true 8 | }, 9 | //表单数据 10 | formData: { 11 | contactName: '', 12 | contactTel: "", 13 | county: "", 14 | province: "", 15 | city: "", 16 | addressDetail: "", 17 | houseNumber: "", 18 | }, 19 | isShowSmartAddress: true, 20 | isShowAreaSelect: false, 21 | isSelect: false, 22 | phoneNumberError: "", 23 | }, 24 | 25 | onLoad: function (options) { 26 | this.setData({ 27 | formData: JSON.parse(decodeURIComponent(options.address)), 28 | }) 29 | }, 30 | 31 | onReady: function () { 32 | this.toast = this.selectComponent('.my-toast'); 33 | }, 34 | 35 | changeContactName({ 36 | detail 37 | }) { 38 | this.setData({ 39 | "formData.contactName": detail, 40 | }) 41 | }, 42 | 43 | changeContactTel({ 44 | detail 45 | }) { 46 | const param = { 47 | "formData.contactTel": detail, 48 | } 49 | if (detail.length === 11 && /^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(detail)) { 50 | param.phoneNumberError = "" 51 | } 52 | this.setData(param) 53 | }, 54 | 55 | changeHouseMumber({ 56 | detail 57 | }) { 58 | this.setData({ 59 | "formData.houseNumber": detail, 60 | }) 61 | }, 62 | 63 | showSmartAddress: function () { 64 | this.setData({ 65 | isShowSmartAddress: !this.data.isShowSmartAddress 66 | }) 67 | }, 68 | 69 | confirmArea: function ({ 70 | detail 71 | }) { 72 | this.setData({ 73 | "formData.province": detail[0], 74 | "formData.city": detail[1], 75 | "formData.county": detail[2], 76 | }) 77 | }, 78 | 79 | changeAddressDetail({ 80 | detail 81 | }) { 82 | this.setData({ 83 | "formData.addressDetail": detail 84 | }) 85 | }, 86 | 87 | getLocation() { 88 | const { 89 | longitudeLatitude 90 | } = this.data.formData 91 | wx.navigateTo({ 92 | url: '../../pages/map/index?longitudeLatitude=' + (longitudeLatitude || ''), 93 | }) 94 | }, 95 | 96 | insightAddress() { 97 | const addressObj = app.smart(this.data.smartAddress) 98 | let { 99 | name = "", 100 | phone = "", 101 | province = "", 102 | city = "", 103 | county = "", 104 | street = "", 105 | address = "" 106 | } = addressObj 107 | this.setData({ 108 | "formData.contactName": name, 109 | "formData.contactTel": phone, 110 | "formData.province": province, 111 | "formData.city": city, 112 | "formData.county": county, 113 | "formData.addressDetail": street + address, 114 | }) 115 | }, 116 | 117 | changeSmartAddress(e) { 118 | this.setData({ 119 | smartAddress: e.detail 120 | }) 121 | }, 122 | 123 | async saveAddress() { 124 | const formData = this.data.formData 125 | for (const key in formData) { 126 | if (formData.hasOwnProperty(key)) { 127 | if (!formData[key]) { 128 | wx.showToast({ 129 | icon: "none", 130 | title: '请填写完整信息', 131 | }) 132 | return 133 | } 134 | } 135 | } 136 | if (!(/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(formData.contactTel))) { 137 | wx.showToast({ 138 | icon: "none", 139 | title: '手机号格式错误', 140 | }) 141 | this.setData({ 142 | "phoneNumberError": "手机号格式错误" 143 | }) 144 | return 145 | } 146 | }, 147 | async delAddress() { 148 | this.setData({ 149 | "toastConfig.type": "success", 150 | "toastConfig.message": "删除成功", 151 | }) 152 | this.toast.show() 153 | if (res.code === 200) { 154 | wx.navigateBack({ 155 | delta: 1, 156 | }) 157 | } 158 | }, 159 | showAreaSelect: function () { 160 | this.selectComponent("#area-select").showAreaSelect(); 161 | }, 162 | }) -------------------------------------------------------------------------------- /pages/address-detail_2/index.js: -------------------------------------------------------------------------------- 1 | 2 | Page({ 3 | data: { 4 | //导航栏配置 5 | navConfig: { 6 | title: "地址详细", 7 | isLeftArrow: true 8 | }, 9 | //表单数据 10 | formData: { 11 | contactName: '', 12 | contactTel: "", 13 | county: "", 14 | province: "", 15 | city: "", 16 | addressDetail: "", 17 | houseNumber: "", 18 | }, 19 | isShowSmartAddress: false, 20 | isShowAreaSelect: false, 21 | isSelect: false, 22 | phoneNumberError: "", 23 | }, 24 | 25 | onLoad: function (options) { 26 | this.setData({ 27 | formData: JSON.parse(decodeURIComponent(options.address)), 28 | }) 29 | }, 30 | 31 | onReady: function () { 32 | this.toast = this.selectComponent('.my-toast'); 33 | }, 34 | 35 | changeContactName({ 36 | detail 37 | }) { 38 | this.setData({ 39 | "formData.contactName": detail, 40 | }) 41 | }, 42 | 43 | changeContactTel({ 44 | detail 45 | }) { 46 | const param = { 47 | "formData.contactTel": detail, 48 | } 49 | if (detail.length === 11 && /^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(detail)) { 50 | param.phoneNumberError = "" 51 | } 52 | this.setData(param) 53 | }, 54 | 55 | changeHouseMumber({ 56 | detail 57 | }) { 58 | this.setData({ 59 | "formData.houseNumber": detail, 60 | }) 61 | }, 62 | 63 | showSmartAddress: function () { 64 | this.setData({ 65 | isShowSmartAddress: !this.data.isShowSmartAddress 66 | }) 67 | }, 68 | 69 | confirmArea: function ({ 70 | detail 71 | }) { 72 | this.setData({ 73 | "formData.province": detail[0], 74 | "formData.city": detail[1], 75 | "formData.county": detail[2], 76 | }) 77 | }, 78 | 79 | changeAddressDetail({ 80 | detail 81 | }) { 82 | this.setData({ 83 | "formData.addressDetail": detail 84 | }) 85 | }, 86 | 87 | getLocation() { 88 | const { 89 | longitudeLatitude 90 | } = this.data.formData 91 | wx.navigateTo({ 92 | url: '../../pages/map/index?longitudeLatitude=' + (longitudeLatitude || ''), 93 | }) 94 | }, 95 | 96 | // insightAddress() { 97 | // const addressObj = app.smart(this.data.smartAddress) 98 | // let { 99 | // name = "", 100 | // phone = "", 101 | // province = "", 102 | // city = "", 103 | // county = "", 104 | // street = "", 105 | // address = "" 106 | // } = addressObj 107 | // this.setData({ 108 | // "formData.contactName": name, 109 | // "formData.contactTel": phone, 110 | // "formData.province": province, 111 | // "formData.city": city, 112 | // "formData.county": county, 113 | // "formData.addressDetail": street + address, 114 | // }) 115 | // }, 116 | 117 | changeSmartAddress(e) { 118 | this.setData({ 119 | smartAddress: e.detail 120 | }) 121 | }, 122 | 123 | async saveAddress() { 124 | const formData = this.data.formData 125 | for (const key in formData) { 126 | if (formData.hasOwnProperty(key)) { 127 | if (!formData[key]) { 128 | wx.showToast({ 129 | icon: "none", 130 | title: '请填写完整信息', 131 | }) 132 | return 133 | } 134 | } 135 | } 136 | if (!(/^(?:(?:\+|00)86)?1[3-9]\d{9}$/.test(formData.contactTel))) { 137 | wx.showToast({ 138 | icon: "none", 139 | title: '手机号格式错误', 140 | }) 141 | this.setData({ 142 | "phoneNumberError": "手机号格式错误" 143 | }) 144 | return 145 | } 146 | }, 147 | 148 | async delAddress() { 149 | this.setData({ 150 | "toastConfig.type": "success", 151 | "toastConfig.message": "删除成功", 152 | }) 153 | this.toast.show() 154 | if (res.code === 200) { 155 | wx.navigateBack({ 156 | delta: 1, 157 | }) 158 | } 159 | }, 160 | 161 | showAreaSelect: function () { 162 | this.selectComponent("#area-select").showAreaSelect(); 163 | }, 164 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 微信小程序通用项目模板——「提高效率,~~安心摸鱼~~专注于业务逻辑」 2 | 3 | 模板特点:vant-weapp、自定义导航栏、自定 tabbar、自定义mixin混入、全局样式、请求接口二次封装(request.js)、枚举(emun.js)、全局环境变量、添加地址模板三种方式(省市区联动、地图选点、智能识别)、vant 转换像素单位(px 转 rpx , 需自己转换) 等等 4 | 5 | ## 项目启动 6 | 7 | - 安装依赖 8 | ``` 9 | //项目根目录,执行命令 10 | npm i 11 | ``` 12 | - 构建 npm 包 13 | 14 | 微信开发者工具 --> 工具 --> 构建 npm 15 | 16 | 17 | ## 项目目录: 18 | 19 | ``` 20 | wx_template 21 | ├─ api 22 | │ └─ user.js // user 用户相关的 api 23 | ├─ app.js 24 | ├─ app.json // 小程序全局配置 25 | ├─ app.wxss // 全局样式 26 | ├─ common 27 | │ ├─ behavior.js // 组件公用的 behavior 28 | │ ├─ emun.js // 枚举 29 | │ └─ globalMixin.js // 页面公用的 m1312ixin(混入) 30 | ├─ components 31 | │ ├─ area-select // 地区选择组件 32 | │ │ ├─ index.js 33 | │ │ ├─ index.json 34 | │ │ ├─ index.wxml 35 | │ │ └─ index.wxss 36 | │ ├─ authorize // 用户信息和手机号码授权组件 37 | │ │ ├─ index.js 38 | │ │ ├─ index.json 39 | │ │ ├─ index.wxml 40 | │ │ ├─ index.wxss 41 | │ │ └─ mixin.js 42 | │ └─ navbar // 顶部导航栏 43 | │ ├─ index.js 44 | │ ├─ index.json 45 | │ ├─ index.wxml 46 | │ └─ index.wxss 47 | ├─ config.js // 全局配置 48 | ├─ css 49 | │ └─ variables.wxss // css 全局变量 50 | ├─ custom-tab-bar // 自定义 tabbar 51 | │ ├─ index.js 52 | │ ├─ index.json 53 | │ ├─ index.wxml 54 | │ └─ index.wxss 55 | ├─ fonts 56 | │ └─ iconfont.wxss // iconfont 字体样式 57 | ├─ pages // 页面(用于演示) 58 | │ ├─ index 59 | │ ├─ map 60 | │ ├─ index 61 | │ ├─ address-detail_1 62 | │ ├─ address-detail_2 63 | │ ├─ address-detail_3 64 | ├─ project.config.json // 项目配置,对应小程序开发者工具,右侧的勾选项 65 | ├─ sitemap.json 66 | ├─ gulpfile.json // gulpfile gulp的配置文件,用于搭配 postcss-pxtransform 转换 vant 像素单位 67 | └─ utils 68 | ├─ smartWeChat // 智能识别地址 69 | ├─ authLogin.js // 登录校验逻辑 70 | ├─ mixin.js // 使小程序页面也具备类似于 vue 的 mixin(混入) 功能 71 | ├─ request.js // 请求的统一处理,例如:携带token、token校验、异常处理等 72 | ├─ qqmap-wx-jssdk.min.js // 腾讯地图 sdk 73 | ├─ smartWeChat // 地址只能识别插件 74 | └─ utils.js // 一些公用的工具函数、如果时间格式化、图片压缩等 75 | ``` 76 | 77 | ## vant-weapp 转换像素单位 (px 转 rpx) 78 | 假设你已经安装了`vant-weapp`, 并且已经 `npm 构建`,如果构建失败可以参考 —— [「微信官网」](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html) 79 | ### 安装依赖 80 | ``` 81 | npm i gulp gulp-postcss postcss-pxtransform -D 82 | ``` 83 | ### 创建 gulp 配置信息 84 | 根目录里面已经创建好了 `gulpfile.js`,不用新建,但是有个地方需要注意,看清楚 `vant-weapp` 构建后的路径是否正确,如果不正确改成自己的就好 85 | ``` 86 | //根据自己的 vant 构建路径来 87 | return gulp.src(['miniprogram_npm/@vant/weapp/**/*.wxss']) 88 | .pipe(postcss(processors)) 89 | .pipe(gulp.dest('miniprogram_npm/@vant/weapp/')); 90 | ``` 91 | ### 修改 node_modules/postcss-pxtransform/index.js 92 | ``` 93 | // 默认设置 deviceRatio 94 | // const deviceRatio = { 95 | // 640: 2.34 / 2, 96 | // 750: 1, 97 | // 828: 1.81 / 2 98 | // } 99 | 100 | const deviceRatio = { 101 | 640: 2.34, 102 | 750: 2, 103 | 828: 1.81 104 | } 105 | ``` 106 | 107 | ### 添加 npm 脚本,并运行 108 | ``` 109 | // package.json(已配置) 110 | scripts": { 111 | "build": "gulp css", 112 | }, 113 | ``` 114 | ``` 115 | // 运行 npm 脚本 116 | npm run build 117 | ``` 118 | 119 | ## 项目相关文章: 120 | 121 | 掘金: 122 | - [微信小程序开发那些事 —— 项目前期准备篇](https://juejin.cn/post/6975434044024553503) 123 | - [微信小程序登录功能的实现以及坑点 —— 前端实现](https://juejin.cn/post/6976455315298451470) 124 | - [微信小程序添加地址的三种实现方式 —— 省市区联动选择器、地图选点、智能识别地址](https://juejin.cn/post/6979432031961022478) 125 | 126 | 知乎: 127 | - [微信小程序开发那些事 —— 项目前期准备篇](https://zhuanlan.zhihu.com/p/382180744) 128 | - [微信小程序登录功能的实现以及坑点 —— 前端实现](https://zhuanlan.zhihu.com/p/382588175) 129 | - [微信小程序添加地址的三种实现方式 —— 省市区联动选择器、地图选点、智能识别地址](https://zhuanlan.zhihu.com/p/385354223) 130 | 131 | -------------------------------------------------------------------------------- /utils/utils.js: -------------------------------------------------------------------------------- 1 | import { 2 | CACHE_TOKEN, 3 | HTTP_OSS_URL 4 | } from "../config" 5 | 6 | /** 7 | * 对Date的扩展,将 Date 转化为指定格式的String 8 | * 月(Y)、月(m)、日(d)、小时(H)、分(M)、秒(S) 可以用 1-2 个占位符, 9 | * 例子: 10 | * dateFormat('YYYY-mm-dd HH:MM:SS', new Date()) ==> 2020-01-01 08:00:00 11 | */ 12 | 13 | export const dateFormat = (date = new Date(), fmt = "YYYY-mm-dd") => { 14 | const opt = { 15 | "Y+": date.getFullYear().toString(), // 年 16 | "m+": (date.getMonth() + 1).toString(), // 月 17 | "d+": date.getDate().toString(), // 日 18 | "H+": date.getHours().toString(), // 时 19 | "M+": date.getMinutes().toString(), // 分 20 | "S+": date.getSeconds().toString() // 秒 21 | // 有其他格式化字符需求可以继续添加,必须转化成字符串 22 | }; 23 | let ret 24 | for (let k in opt) { 25 | ret = new RegExp("(" + k + ")").exec(fmt) 26 | if (ret) { 27 | fmt = fmt.replace(ret[1], (ret[1].length == 1) ? (opt[k]) : (opt[k].padStart(ret[1].length, "0"))) 28 | }; 29 | }; 30 | return fmt 31 | } 32 | 33 | // 获取年月日 YYYY-MM-DD 34 | const getDate = (time) => { 35 | return time.getFullYear() + "-" + 36 | (time.getMonth() + 1).toString().padStart(2, "0") + "-" + 37 | time.getDate().toString().padStart(2, "0") 38 | } 39 | 40 | // 数组形式返回,今天往后的day后的天 41 | //day 为返回今天往后的天数 42 | const getDateList = (day) => { 43 | let dateList = [] 44 | for (let i = 1; i <= day; i++) { 45 | let today = new Date(); 46 | let targetday_milliseconds = today.getTime() + 1000 * 60 * 60 * 24 * i; 47 | today.setTime(targetday_milliseconds); //注意,这行是关键代码 48 | dateList.push(today.getDate()) 49 | } 50 | return dateList; 51 | } 52 | 53 | // 今天往后的day后的天 ,并格式化 YYYY-MM-DD 54 | const getAfterDate = (day) => { 55 | let today = new Date(); 56 | let targetday_milliseconds = today.getTime() + 1000 * 60 * 60 * 24 * day; 57 | today.setTime(targetday_milliseconds); //注意,这行是关键代码 58 | return getDate(today) 59 | } 60 | 61 | // 返回星期几 62 | const getWeekList = (dataList) => { 63 | const weekEum = { 64 | 0: "日", 65 | 1: "一", 66 | 2: "二", 67 | 3: "三", 68 | 4: "四", 69 | 5: "五", 70 | 6: "六", 71 | } 72 | return dataList.map(x => { 73 | let today = new Date() 74 | let targetday_milliseconds = today.getTime() + 1000 * 60 * 60 * 24 * (x - today.getDate()); 75 | today.setTime(targetday_milliseconds); 76 | return weekEum[today.getDay()] 77 | }) 78 | } 79 | 80 | //压缩图片, 老方法,不建议使用。 81 | /* 82 | instance 为当前实例 83 | canvas canvas DOM元素实例 84 | canvsdId canvas-id="xxx" 85 | url 图片文件本地临时路径 86 | ratio 压缩比例 0 ~ 1,1表示不压缩 87 | suffix 图片后缀 88 | */ 89 | const compressImage = async function (instance, canvas, canvasId, url, ratio = 0.8, suffix = 'png') { 90 | return new Promise((resolve, reject) => { 91 | wx.getImageInfo({ 92 | src: url, 93 | success: function (res) { 94 | var canvasWidth = res.width //图片原始长宽 95 | var canvasHeight = res.height 96 | canvasWidth = Math.trunc(res.width * ratio) 97 | canvasHeight = Math.trunc(res.height * ratio) 98 | instance.setData({ 99 | cWidth: canvasWidth, 100 | cHeight: canvasHeight 101 | }) 102 | // const canvas = wx.createCanvasContext(canvasId, instance) 103 | canvas.drawImage(res.path, 0, 0, canvasWidth, canvasHeight) 104 | canvas.draw(false, setTimeout(() => { 105 | wx.canvasToTempFilePath({ 106 | canvasId: canvasId, 107 | destWidth: canvasWidth, 108 | destHeight: canvasHeight, 109 | fileType: suffix, 110 | quality: ratio, 111 | success: function (res) { 112 | // 返回压缩后的图片路径 113 | resolve(res.tempFilePath) 114 | }, 115 | fail: function (res) { 116 | reject(null) 117 | } 118 | }, instance) 119 | }, 500)) 120 | }, 121 | fail: function (res) { 122 | console.log(res.errMsg) 123 | }, 124 | }) 125 | }) 126 | } 127 | 128 | // 只接受单张图片上传 129 | const upload = async function (file, canvas, canvasId, fileType, success) { 130 | //只接受 jpg、gif、png 格式的文件, 131 | const acceptFileType = ['jpg', 'gif', 'png'] 132 | const index = file.url.lastIndexOf('.') + 1 133 | const suffix = file.url.slice(index) 134 | if (!acceptFileType.includes(suffix)) { 135 | this.setData({ 136 | 'toastConfig.type': 'text', 137 | 'toastConfig.message': `仅支持上传,${acceptFileType.join('、')}格式的文件`, 138 | }) 139 | return this.toast.show() 140 | } 141 | const fileUrl = await compressImage(this, canvas, canvasId, file.url, 0.8, suffix) 142 | file.url = fileUrl 143 | 144 | // 图片大于 2M 时压缩 145 | // if (file.size > 2 * 1024 * 1024) { 146 | // console.log("压缩") 147 | // console.log(file.url) 148 | // const fileUrl = await compressImage(this, canvas, canvasId, file.url, 0.8) 149 | // file.url = fileUrl 150 | // console.log(file.url) 151 | // } 152 | wx.uploadFile({ 153 | url: HTTP_OSS_URL + 'upload', 154 | filePath: file.url, 155 | name: 'file', 156 | formData: { 157 | fileType: fileType 158 | }, 159 | header: { 160 | "X-Access-Token": wx.getStorageSync(CACHE_TOKEN) 161 | }, 162 | success: (res) => { 163 | success(res) 164 | }, 165 | }); 166 | } 167 | 168 | 169 | // 防抖 170 | const debounce = (fn, wait, immediate = false) => { 171 | let timer, startTimeStamp = 0; 172 | let context, args; 173 | 174 | let run = (timerInterval) => { 175 | timer = setTimeout(() => { 176 | let now = (new Date()).getTime(); 177 | let interval = now - startTimeStamp 178 | if (interval < timerInterval) { 179 | console.log('debounce reset', timerInterval - interval); 180 | startTimeStamp = now; 181 | run(wait - interval); 182 | } else { 183 | if (!immediate) { 184 | fn.apply(context, args); 185 | } 186 | clearTimeout(timer); 187 | timer = null; 188 | } 189 | 190 | }, timerInterval); 191 | } 192 | 193 | return function () { 194 | context = this; 195 | args = arguments; 196 | let now = (new Date()).getTime(); 197 | startTimeStamp = now; 198 | 199 | if (!timer) { 200 | console.log('debounce set', wait); 201 | if (immediate) { 202 | fn.apply(context, args); 203 | } 204 | run(wait); 205 | } 206 | 207 | } 208 | 209 | } 210 | module.exports = { 211 | dateFormat, 212 | getDateList, 213 | getWeekList, 214 | getDate, 215 | getAfterDate, 216 | compressImage, 217 | upload, 218 | debounce 219 | } -------------------------------------------------------------------------------- /pages/map/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | MAP_KEY 3 | } from "../../config" 4 | const citySelector = requirePlugin('citySelector'); 5 | import QQMapWX from "../../utils/qqmap-wx-jssdk.min.js" 6 | let qqmapsdk; 7 | Page({ 8 | data: { 9 | navConfig: { 10 | title: "地图选点", 11 | isLeftArrow: true 12 | }, 13 | cityName: "佛山市", 14 | latitude: '', 15 | longitude: '', 16 | centerData: {}, 17 | nearList: [], 18 | selectedId: 0, 19 | defaultKeyword: '房产小区', 20 | keyword: '', 21 | pageIndex: 1, 22 | pageSize: 20, 23 | isDone: false, 24 | MAP_KEY, 25 | }, 26 | 27 | onLoad: async function (options) { 28 | //获取地图 map 实例 29 | this.mapCtx = wx.createMapContext('myMap') 30 | // 实例化API核心类 31 | qqmapsdk = new QQMapWX({ 32 | key: MAP_KEY 33 | }); 34 | //判断是否授权定位 35 | let scopeRes = await wx.getSetting() 36 | if (!scopeRes.authSetting['scope.userLocation']) { 37 | try { 38 | // 没有授权的时候弹出授权窗口 39 | await wx.authorize({ 40 | scope: 'scope.userLocation', 41 | }) 42 | } catch (error) { 43 | // 用户手动关闭的授权,无法自动调起授权窗口,我们只能提示引导用户授权 44 | wx.showModal({ 45 | showCancel: false, 46 | title: '位置授权', 47 | content: '该功能,需要进行「位置授权」才能使用。可点击「右上角」-->「设置」-->「位置消息」-->「仅在使用小程序使用」' 48 | }) 49 | return 50 | } 51 | } 52 | wx.showLoading({ 53 | title: '加载中' 54 | }); 55 | 56 | //判断表单是否传了经纬度,如果有则使用表单的,用于在地图里显示刚才选择的位置 57 | const longitudeLatitude = options ? options.longitudeLatitude : "" 58 | if (longitudeLatitude) { 59 | const longLatArr = longitudeLatitude.split(',') 60 | this.initLocation(longLatArr[1], longLatArr[0]) 61 | } else { 62 | //微信作死!新版本 wx.getLocation 存在调用频率限制, 使用 onLocationChange 来代替 63 | wx.startLocationUpdate({ 64 | success: (res) => { 65 | wx.onLocationChange((location) => { 66 | if (location) { 67 | const { 68 | latitude, 69 | longitude 70 | } = location 71 | wx.stopLocationUpdate() 72 | this.setData({ 73 | latitude, 74 | longitude 75 | }) 76 | this.initLocation(latitude, longitude) 77 | } 78 | }) 79 | }, 80 | }) 81 | } 82 | }, 83 | 84 | onShow: function () { 85 | //城市选择插件,获取城市信息 86 | const selectedCity = citySelector.getCity(); 87 | if (selectedCity) { 88 | const { 89 | fullname, 90 | location 91 | } = selectedCity 92 | this.setData({ 93 | longitude: location.longitude, 94 | latitude: location.latitude, 95 | cityName: fullname 96 | }) 97 | this.data.pageIndex = 1 98 | this.data.isDone = false 99 | this.nearBySearch() 100 | } 101 | 102 | }, 103 | onUnload() { 104 | // 页面卸载时清空插件数据,防止再次进入页面,getCity返回的是上次的结果 105 | citySelector.clearCity(); 106 | }, 107 | 108 | toSelectCity() { 109 | const key = MAP_KEY; // 使用在腾讯位置服务申请的key 110 | const referer = '微信模板'; // 调用插件的app的名称 111 | wx.navigateTo({ 112 | url: `plugin://citySelector/index?key=${key}&referer=${referer}`, 113 | }) 114 | }, 115 | 116 | //监听拖动地图,拖动结束根据中心点更新页面 117 | mapChange: function (e) { 118 | if (e.type == 'end' && (e.causedBy == 'scale' || e.causedBy == 'drag')) { 119 | this.mapCtx.getCenterLocation({ 120 | success: (res) => { 121 | this.setData({ 122 | nearList: [], 123 | latitude: res.latitude, 124 | longitude: res.longitude, 125 | }) 126 | this.data.pageIndex = 1 127 | this.data.isDone = false 128 | this.nearBySearch.call(this); 129 | } 130 | }) 131 | } 132 | 133 | }, 134 | 135 | // 重置 136 | reload: function () { 137 | this.setData({ 138 | nearList: [] 139 | }) 140 | this.onLoad(); 141 | }, 142 | 143 | chooseCenter: function (e) { 144 | let id = e.currentTarget.id; 145 | for (let i = 0; i < this.data.nearList.length; i++) { 146 | if (i == id) { 147 | this.setData({ 148 | selectedId: id, 149 | centerData: this.data.nearList[i], 150 | latitude: this.data.nearList[i].latitude, 151 | longitude: this.data.nearList[i].longitude, 152 | }); 153 | this.selectedOk() 154 | return; 155 | } 156 | } 157 | }, 158 | 159 | nearBySearch: function (e) { 160 | if (e) { 161 | this.setData({ 162 | keyword: e.detail, 163 | pageIndex: 1, 164 | nearList: [], 165 | addressInput: e.detail 166 | }) 167 | } 168 | wx.hideLoading(); 169 | wx.showLoading({ 170 | title: '加载中' 171 | }); 172 | qqmapsdk.search({ 173 | keyword: this.data.keyword, 174 | location: this.data.latitude + ',' + this.data.longitude, 175 | page_size: this.data.pageSize, 176 | page_index: this.data.pageIndex, 177 | success: (res) => { 178 | wx.hideLoading(); 179 | let sug = []; 180 | for (let i = 0; i < res.data.length; i++) { 181 | sug.push({ 182 | title: res.data[i].title, 183 | id: res.data[i].id, 184 | addr: res.data[i].address, 185 | province: res.data[i].ad_info.province, 186 | city: res.data[i].ad_info.city, 187 | district: res.data[i].ad_info.district, 188 | latitude: res.data[i].location.lat, 189 | longitude: res.data[i].location.lng 190 | }); 191 | } 192 | let pageIndex = this.data.pageIndex + 1 193 | if (sug.length < this.data.pageSize) { 194 | this.data.isDone = true 195 | pageIndex = this.data.pageIndex 196 | }; 197 | this.setData({ 198 | selectedId: 0, 199 | centerData: sug[0], 200 | nearList: this.data.nearList.concat(sug), 201 | pageIndex: pageIndex 202 | }) 203 | }, 204 | complete: function (res) { 205 | wx.hideLoading(); 206 | } 207 | }); 208 | }, 209 | //确认选择地址 210 | selectedOk: function () { 211 | let pages = getCurrentPages(); 212 | //获取上一个页面的实例 213 | let prevPage = pages[pages.length - 2]; 214 | const { 215 | title, 216 | city, 217 | district, 218 | province, 219 | latitude, 220 | longitude 221 | } = this.data.centerData 222 | prevPage.setData({ 223 | "formData.county": district, 224 | "formData.province": province, 225 | "formData.city": city, 226 | "formData.addressDetail": title, 227 | "formData.longitudeLatitude": longitude + ',' + latitude, 228 | }) 229 | wx.navigateBack({ 230 | delta: 1 231 | }) 232 | }, 233 | 234 | //初始化 235 | initLocation: function (latitude, longitude) { 236 | qqmapsdk.reverseGeocoder({ 237 | location: { 238 | latitude, 239 | longitude 240 | }, 241 | get_poi: 1, 242 | success: (res) => { 243 | this.setData({ 244 | latitude: latitude, 245 | longitude: longitude, 246 | keyword: this.data.defaultKeyword, 247 | cityName: res.result.address_component.city, 248 | pageIndex: 1, 249 | addressInput: "" 250 | }) 251 | this.nearBySearch(); 252 | }, 253 | }); 254 | }, 255 | loadLocation() { 256 | if (!this.data.isDone) this.nearBySearch(); 257 | } 258 | }) -------------------------------------------------------------------------------- /utils/smartWeChat/js/address_parse.js: -------------------------------------------------------------------------------- 1 | var addressList = []; //地址列表 2 | var zipCodeList = []; //邮编列表 3 | var zipCode = require("./zipCode.js"); 4 | 5 | console.log("正在加载省市区数据..."); 6 | 7 | const wx_getaddress = () => { 8 | return new Promise((resolve, reject) => { 9 | let array = []; 10 | let index = 0; 11 | let length = 7; 12 | // console.log('共计' + length + '条数据') 13 | for (let i = 0; i < length; i++) { 14 | if (wx.getStorageSync(i + "")) { 15 | index++; 16 | // console.log('第' + index + '条数据在缓存中读取完毕') 17 | array = [...array, ...JSON.parse(wx.getStorageSync(i + ""))]; 18 | if (index == length) { 19 | resolve(array); 20 | } 21 | } else { 22 | setTimeout(() => { 23 | wx.request({ 24 | url: "https://wangzc.wang/addressJson/" + i, 25 | method: "GET", 26 | success: function(res) { 27 | index++; 28 | wx.setStorage({ 29 | key: i + "", 30 | data: JSON.stringify(res.data) 31 | }); 32 | // console.log('第' + index + '条数据加载完毕') 33 | array = [...array, ...res.data]; 34 | if (index == length) { 35 | resolve(array); 36 | } 37 | } 38 | }); 39 | }, 2000 * i); 40 | } 41 | } 42 | }); 43 | }; 44 | 45 | function getData() { 46 | wx_getaddress().then(res => { 47 | addressList = res;//地址赋值;这里可以加入自己写的接口进行赋值 48 | addressList.forEach(item => { 49 | formatAddresList(item, 1, ""); 50 | }); 51 | console.log(addressList); 52 | zipCodeList = zipCodeFormat(zipCode.variable); 53 | console.log("省市区数据挂载完毕!!"); 54 | }); 55 | } 56 | getData(); 57 | /** 58 | * 地址数据处理 59 | * @param addressList-各级数据对象 60 | * @param index-对应的省/市/县区/街道 61 | * @param province-只有直辖市会处理为 北京市北京市 62 | * @returns 63 | */ 64 | function formatAddresList(addressList, index, province) { 65 | if (index === 1) { 66 | //省 67 | addressList.province = addressList.name; 68 | } 69 | if (index === 2) { 70 | //市 71 | if (addressList.name == "市辖区") { 72 | addressList.name = province.name; 73 | } 74 | addressList.city = addressList.name; 75 | } 76 | if (index === 3) { 77 | //区或者县 78 | addressList.county = addressList.name; 79 | } 80 | if (index === 4) { 81 | //街道 82 | addressList.street = addressList.name; 83 | } 84 | if (addressList.children) { 85 | index++; 86 | addressList.children.forEach(res => { 87 | formatAddresList(res, index, addressList); 88 | }); 89 | } 90 | } 91 | /** 92 | * 解析邮编 93 | * @param 94 | * @returns 95 | */ 96 | function zipCodeFormat(zipCode) { 97 | let list = []; 98 | zipCode.forEach(el => { 99 | if (el.child) { 100 | el.child.forEach(event => { 101 | if (event.child) { 102 | event.child.forEach(element => { 103 | list.push(element.zipcode); 104 | }); 105 | } 106 | }); 107 | } 108 | }); 109 | return list; 110 | } 111 | 112 | var smartObj = {}; 113 | /** 114 | * 解析邮编 115 | * @param event识别的地址 116 | * @returns 117 | */ 118 | function smart(event) { 119 | event = stripscript(event); //过滤特殊字符 120 | let obj = {}; 121 | let copyaddress = JSON.parse(JSON.stringify(event)); 122 | copyaddress = copyaddress.split(" "); 123 | 124 | copyaddress.forEach((res, index) => { 125 | if (res) { 126 | if (res.length == 1) { 127 | res += "XX"; // 过滤掉一位的名字或者地址 128 | } 129 | let addressObj = smatrAddress(res); 130 | obj = Object.assign(obj, addressObj); 131 | if (JSON.stringify(addressObj) === "{}") { 132 | obj.name = res.replace("XX", ""); 133 | } 134 | } 135 | }); 136 | return obj; 137 | } 138 | function smatrAddress(event) { 139 | smartObj = {}; 140 | let address = event; 141 | //address= event.replace(/\s/g, ''); //去除空格 142 | address = stripscript(address); //过滤特殊字符 143 | 144 | //身份证号匹配 145 | if (IdentityCodeValid(address)) { 146 | smartObj.idCard = address; 147 | address = address.replace(address, ""); 148 | } 149 | 150 | //电话匹配 151 | let phone = address.match( 152 | /(86-[1][0-9]{10}) | (86[1][0-9]{10})|([1][0-9]{10})/g 153 | ); 154 | if (phone) { 155 | smartObj.phone = phone[0]; 156 | address = address.replace(phone[0], ""); 157 | } 158 | 159 | //邮编匹配 160 | zipCodeList.forEach(res => { 161 | if (address.indexOf(res) != -1) { 162 | let num = address.indexOf(res); 163 | let code = address.slice(num, num + 6); 164 | smartObj.zipCode = code; 165 | address = address.replace(code, ""); 166 | } 167 | }); 168 | let matchAddress = ""; 169 | //省匹配 比如输入北京市朝阳区,会用北 北京 北京市 北京市朝 以此类推在addressList里的province中做匹配,会得到北京市 河北省 天津市等等; 170 | let matchProvince = []; //粗略匹配上的省份 171 | // for (let begIndex = 0; begIndex < address.length; begIndex++) { 172 | matchAddress = ""; 173 | for (let endIndex = 0; endIndex < address.length; endIndex++) { 174 | // if (endIndex > begIndex) { 175 | matchAddress = address.slice(0, endIndex + 2); 176 | addressList.forEach(res => { 177 | if (res["province"].indexOf(matchAddress) != -1) { 178 | matchProvince.push({ 179 | province: res.province, 180 | provinceCode: res.code, 181 | matchValue: matchAddress 182 | }); 183 | } 184 | }); 185 | // } 186 | } 187 | // } 188 | 189 | //统计筛选初略统计出的省份 190 | matchProvince.forEach(res => { 191 | res.index = 0; 192 | matchProvince.forEach(el => { 193 | if (res.province == el.province) { 194 | el.index++; 195 | if (res.matchValue.length > el.matchValue.length) { 196 | el.matchValue = res.matchValue; 197 | } 198 | } 199 | }); 200 | }); 201 | if (matchProvince.length != 0) { 202 | let province = matchProvince.reduce((p, v) => (p.index < v.index ? v : p)); 203 | smartObj.province = province.province; 204 | smartObj.provinceCode = province.provinceCode; 205 | address = address.replace(province.matchValue, ""); 206 | } 207 | //市查找 208 | let matchCity = []; //粗略匹配上的市 209 | matchAddress = ""; 210 | for (let endIndex = 0; endIndex < address.length; endIndex++) { 211 | matchAddress = address.slice(0, endIndex + 2); 212 | addressList.forEach(el => { 213 | // if (el.name == smartObj.province) { 214 | if (el.code == smartObj.provinceCode || !smartObj.provinceCode) { 215 | if ( 216 | smartObj.province == "北京市" || 217 | smartObj.province == "天津市" || 218 | smartObj.province == "上海市" || 219 | smartObj.province == "重庆市" 220 | ) { 221 | el.children.forEach(item => { 222 | item.children.forEach(res => { 223 | if (res["county"].indexOf(matchAddress) != -1) { 224 | matchCity.push({ 225 | county: res.county, 226 | countyCode: res.code, 227 | city: item.city, 228 | cityCode: item.code, 229 | matchValue: matchAddress, 230 | province: el.province, 231 | provinceCode: el.code 232 | }); 233 | } 234 | }); 235 | }); 236 | } else { 237 | el.children.forEach(res => { 238 | if (res["city"].indexOf(matchAddress) != -1) { 239 | matchCity.push({ 240 | city: res.city, 241 | cityCode: res.code, 242 | matchValue: matchAddress, 243 | province: el.province, 244 | provinceCode: el.code 245 | }); 246 | } 247 | }); 248 | } 249 | } 250 | // } 251 | }); 252 | } 253 | 254 | //统计筛选初略统计出的市 255 | matchCity.forEach(res => { 256 | res.index = 0; 257 | matchCity.forEach(el => { 258 | if (res.city == el.city) { 259 | el.index++; 260 | if (res.matchValue.length > el.matchValue.length) { 261 | el.matchValue = res.matchValue; 262 | } 263 | } 264 | }); 265 | }); 266 | if (matchCity.length != 0) { 267 | let city = matchCity.reduce((p, v) => (p.index < v.index ? v : p)); 268 | smartObj.city = city.city; 269 | smartObj.cityCode = city.cityCode; 270 | smartObj.county = city.county; 271 | smartObj.countyCode = city.countyCode; 272 | if (!smartObj.province) { 273 | smartObj.province = city.province; 274 | smartObj.provinceCode = city.provinceCode; 275 | } 276 | address = address.replace(city.matchValue, ""); 277 | } 278 | 279 | //区县查找 280 | let matchCounty = []; //粗略匹配上的区县 281 | matchAddress = ""; 282 | for (let endIndex = 0; endIndex < address.length; endIndex++) { 283 | matchAddress = address.slice(0, endIndex + 2); 284 | addressList.forEach(el => { 285 | // if (el.name == smartObj.province) { 286 | if ( 287 | smartObj.province == "北京市" || 288 | smartObj.province == "天津市" || 289 | smartObj.province == "上海市" || 290 | smartObj.province == "重庆市" 291 | ) { 292 | //nothing 293 | } else { 294 | el.children.forEach(item => { 295 | // if (item.name == smartObj.city) { 296 | item.children.forEach(res => { 297 | if (res["county"].indexOf(matchAddress) != -1) { 298 | //省/市 || 省 299 | if (smartObj.province) { 300 | if (res.code.slice(0, 2) == smartObj.provinceCode) { 301 | matchCounty.push({ 302 | county: res.county, 303 | countyCode: res.code, 304 | city: item.city, 305 | cityCode: item.code, 306 | matchValue: matchAddress, 307 | province: el.province, 308 | provinceCode: el.code 309 | }); 310 | } 311 | } else if (!smartObj.province && !smartObj.city) { 312 | matchCounty.push({ 313 | county: res.county, 314 | countyCode: res.code, 315 | city: item.city, 316 | cityCode: item.code, 317 | matchValue: matchAddress, 318 | province: el.province, 319 | provinceCode: el.code 320 | }); 321 | } 322 | } 323 | }); 324 | // } 325 | }); 326 | } 327 | // } 328 | }); 329 | } 330 | //统计筛选初略统计出的区县 331 | matchCounty.forEach(res => { 332 | res.index = 0; 333 | matchCounty.forEach(el => { 334 | if (res.city == el.city) { 335 | el.index++; 336 | if (res.matchValue.length > el.matchValue.length) { 337 | el.matchValue = res.matchValue; 338 | } 339 | } 340 | }); 341 | }); 342 | if (matchCounty.length != 0) { 343 | let city = matchCounty.reduce((p, v) => (p.index < v.index ? v : p)); 344 | smartObj.county = city.county; 345 | smartObj.countyCode = city.countyCode; 346 | if (!smartObj.province) { 347 | smartObj.province = city.province; 348 | smartObj.provinceCode = city.provinceCode; 349 | } 350 | if (!smartObj.city) { 351 | smartObj.city = city.city; 352 | smartObj.cityCode = city.cityCode; 353 | } 354 | address = address.replace(city.matchValue, ""); 355 | } 356 | 357 | //街道查找 358 | let matchStreet = []; //粗略匹配上的街道查 359 | matchAddress = ""; 360 | for (let endIndex = 0; endIndex < address.length; endIndex++) { 361 | matchAddress = address.slice(0, endIndex + 3); 362 | addressList.forEach(el => { 363 | if (el.name == smartObj.province) { 364 | if ( 365 | smartObj.province == "北京市" || 366 | smartObj.province == "天津市" || 367 | smartObj.province == "上海市" || 368 | smartObj.province == "重庆市" 369 | ) { 370 | //nothing 371 | } else { 372 | el.children.forEach(element => { 373 | if (element.name == smartObj.city) { 374 | element.children.forEach(item => { 375 | if (item.name == smartObj.county) { 376 | item.children.forEach(res => { 377 | if (res["street"].indexOf(matchAddress) != -1) { 378 | matchStreet.push({ 379 | street: res.street, 380 | streetCode: res.code, 381 | matchValue: matchAddress 382 | }); 383 | } 384 | }); 385 | } 386 | }); 387 | } 388 | }); 389 | } 390 | } 391 | }); 392 | } 393 | 394 | //统计筛选初略统计出的区县 395 | matchStreet.forEach(res => { 396 | res.index = 0; 397 | matchStreet.forEach(el => { 398 | if (res.city == el.city) { 399 | el.index++; 400 | if (res.matchValue.length > el.matchValue.length) { 401 | el.matchValue = res.matchValue; 402 | } 403 | } 404 | }); 405 | }); 406 | 407 | if (matchStreet.length != 0) { 408 | let city = matchStreet.reduce((p, v) => (p.index < v.index ? v : p)); 409 | smartObj.street = city.street; 410 | smartObj.streetCode = city.streetCode; 411 | address = address.replace(city.matchValue, ""); 412 | } 413 | //姓名查找 414 | if (smartObj.province) { 415 | smartObj.address = address; 416 | } 417 | 418 | return smartObj; 419 | } 420 | ////过滤特殊字符 421 | function stripscript(s) { 422 | s = s.replace(/(\d{3})-(\d{4})-(\d{4})/g, "$1$2$3"); 423 | s = s.replace(/(\d{3}) (\d{4}) (\d{4})/g, "$1$2$3"); 424 | var pattern = new RegExp( 425 | "[`~!@#$^&*()=|{}':;',\\[\\].<>/?~!@#¥……&*()——|{}【】‘;:”“’。,、?-]" 426 | ); 427 | var rs = ""; 428 | for (var i = 0; i < s.length; i++) { 429 | rs = rs + s.substr(i, 1).replace(pattern, " "); 430 | } 431 | rs = rs.replace(/[\r\n]/g, ""); 432 | return rs; 433 | } 434 | 435 | function IdentityCodeValid(code) { 436 | let pass; 437 | var city = { 438 | 11: "北京", 439 | 12: "天津", 440 | 13: "河北", 441 | 14: "山西", 442 | 15: "内蒙古", 443 | 21: "辽宁", 444 | 22: "吉林", 445 | 23: "黑龙江 ", 446 | 31: "上海", 447 | 32: "江苏", 448 | 33: "浙江", 449 | 34: "安徽", 450 | 35: "福建", 451 | 36: "江西", 452 | 37: "山东", 453 | 41: "河南", 454 | 42: "湖北 ", 455 | 43: "湖南", 456 | 44: "广东", 457 | 45: "广西", 458 | 46: "海南", 459 | 50: "重庆", 460 | 51: "四川", 461 | 52: "贵州", 462 | 53: "云南", 463 | 54: "西藏 ", 464 | 61: "陕西", 465 | 62: "甘肃", 466 | 63: "青海", 467 | 64: "宁夏", 468 | 65: "新疆", 469 | 71: "台湾", 470 | 81: "香港", 471 | 82: "澳门", 472 | 91: "国外 " 473 | }; 474 | var tip = ""; 475 | pass = true; 476 | 477 | if (!code || !/^\d{17}(\d|X)$/i.test(code)) { 478 | tip = "身份证号格式错误"; 479 | pass = false; 480 | } else if (!city[code.substr(0, 2)]) { 481 | tip = "地址编码错误"; 482 | pass = false; 483 | } else { 484 | //18位身份证需要验证最后一位校验位 485 | if (code.length == 18) { 486 | code = code.split(""); 487 | //∑(ai×Wi)(mod 11) 488 | //加权因子 489 | var factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; 490 | //校验位 491 | var parity = [1, 0, "X", 9, 8, 7, 6, 5, 4, 3, 2]; 492 | var sum = 0; 493 | var ai = 0; 494 | var wi = 0; 495 | for (var i = 0; i < 17; i++) { 496 | ai = code[i]; 497 | wi = factor[i]; 498 | sum += ai * wi; 499 | } 500 | var last = parity[sum % 11]; 501 | if (parity[sum % 11] != code[17]) { 502 | tip = "校验位错误"; 503 | pass = false; 504 | } 505 | } 506 | } 507 | return pass; 508 | } 509 | 510 | module.exports = { 511 | method: smart, 512 | getData: getData 513 | }; 514 | -------------------------------------------------------------------------------- /utils/qqmap-wx-jssdk.min.js: -------------------------------------------------------------------------------- 1 | var ERROR_CONF = { KEY_ERR: 311, KEY_ERR_MSG: 'key格式错误', PARAM_ERR: 310, PARAM_ERR_MSG: '请求参数信息有误', SYSTEM_ERR: 600, SYSTEM_ERR_MSG: '系统错误', WX_ERR_CODE: 1000, WX_OK_CODE: 200 }; var BASE_URL = 'https://apis.map.qq.com/ws/'; var URL_SEARCH = BASE_URL + 'place/v1/search'; var URL_SUGGESTION = BASE_URL + 'place/v1/suggestion'; var URL_GET_GEOCODER = BASE_URL + 'geocoder/v1/'; var URL_CITY_LIST = BASE_URL + 'district/v1/list'; var URL_AREA_LIST = BASE_URL + 'district/v1/getchildren'; var URL_DISTANCE = BASE_URL + 'distance/v1/'; var URL_DIRECTION = BASE_URL + 'direction/v1/'; var MODE = { driving: 'driving', transit: 'transit' }; var EARTH_RADIUS = 6378136.49; var Utils = { safeAdd(x, y) { var lsw = (x & 0xffff) + (y & 0xffff); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xffff) }, bitRotateLeft(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)) }, md5cmn(q, a, b, x, s, t) { return this.safeAdd(this.bitRotateLeft(this.safeAdd(this.safeAdd(a, q), this.safeAdd(x, t)), s), b) }, md5ff(a, b, c, d, x, s, t) { return this.md5cmn((b & c) | (~b & d), a, b, x, s, t) }, md5gg(a, b, c, d, x, s, t) { return this.md5cmn((b & d) | (c & ~d), a, b, x, s, t) }, md5hh(a, b, c, d, x, s, t) { return this.md5cmn(b ^ c ^ d, a, b, x, s, t) }, md5ii(a, b, c, d, x, s, t) { return this.md5cmn(c ^ (b | ~d), a, b, x, s, t) }, binlMD5(x, len) { x[len >> 5] |= 0x80 << (len % 32); x[((len + 64) >>> 9 << 4) + 14] = len; var i; var olda; var oldb; var oldc; var oldd; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for (i = 0; i < x.length; i += 16) { olda = a; oldb = b; oldc = c; oldd = d; a = this.md5ff(a, b, c, d, x[i], 7, -680876936); d = this.md5ff(d, a, b, c, x[i + 1], 12, -389564586); c = this.md5ff(c, d, a, b, x[i + 2], 17, 606105819); b = this.md5ff(b, c, d, a, x[i + 3], 22, -1044525330); a = this.md5ff(a, b, c, d, x[i + 4], 7, -176418897); d = this.md5ff(d, a, b, c, x[i + 5], 12, 1200080426); c = this.md5ff(c, d, a, b, x[i + 6], 17, -1473231341); b = this.md5ff(b, c, d, a, x[i + 7], 22, -45705983); a = this.md5ff(a, b, c, d, x[i + 8], 7, 1770035416); d = this.md5ff(d, a, b, c, x[i + 9], 12, -1958414417); c = this.md5ff(c, d, a, b, x[i + 10], 17, -42063); b = this.md5ff(b, c, d, a, x[i + 11], 22, -1990404162); a = this.md5ff(a, b, c, d, x[i + 12], 7, 1804603682); d = this.md5ff(d, a, b, c, x[i + 13], 12, -40341101); c = this.md5ff(c, d, a, b, x[i + 14], 17, -1502002290); b = this.md5ff(b, c, d, a, x[i + 15], 22, 1236535329); a = this.md5gg(a, b, c, d, x[i + 1], 5, -165796510); d = this.md5gg(d, a, b, c, x[i + 6], 9, -1069501632); c = this.md5gg(c, d, a, b, x[i + 11], 14, 643717713); b = this.md5gg(b, c, d, a, x[i], 20, -373897302); a = this.md5gg(a, b, c, d, x[i + 5], 5, -701558691); d = this.md5gg(d, a, b, c, x[i + 10], 9, 38016083); c = this.md5gg(c, d, a, b, x[i + 15], 14, -660478335); b = this.md5gg(b, c, d, a, x[i + 4], 20, -405537848); a = this.md5gg(a, b, c, d, x[i + 9], 5, 568446438); d = this.md5gg(d, a, b, c, x[i + 14], 9, -1019803690); c = this.md5gg(c, d, a, b, x[i + 3], 14, -187363961); b = this.md5gg(b, c, d, a, x[i + 8], 20, 1163531501); a = this.md5gg(a, b, c, d, x[i + 13], 5, -1444681467); d = this.md5gg(d, a, b, c, x[i + 2], 9, -51403784); c = this.md5gg(c, d, a, b, x[i + 7], 14, 1735328473); b = this.md5gg(b, c, d, a, x[i + 12], 20, -1926607734); a = this.md5hh(a, b, c, d, x[i + 5], 4, -378558); d = this.md5hh(d, a, b, c, x[i + 8], 11, -2022574463); c = this.md5hh(c, d, a, b, x[i + 11], 16, 1839030562); b = this.md5hh(b, c, d, a, x[i + 14], 23, -35309556); a = this.md5hh(a, b, c, d, x[i + 1], 4, -1530992060); d = this.md5hh(d, a, b, c, x[i + 4], 11, 1272893353); c = this.md5hh(c, d, a, b, x[i + 7], 16, -155497632); b = this.md5hh(b, c, d, a, x[i + 10], 23, -1094730640); a = this.md5hh(a, b, c, d, x[i + 13], 4, 681279174); d = this.md5hh(d, a, b, c, x[i], 11, -358537222); c = this.md5hh(c, d, a, b, x[i + 3], 16, -722521979); b = this.md5hh(b, c, d, a, x[i + 6], 23, 76029189); a = this.md5hh(a, b, c, d, x[i + 9], 4, -640364487); d = this.md5hh(d, a, b, c, x[i + 12], 11, -421815835); c = this.md5hh(c, d, a, b, x[i + 15], 16, 530742520); b = this.md5hh(b, c, d, a, x[i + 2], 23, -995338651); a = this.md5ii(a, b, c, d, x[i], 6, -198630844); d = this.md5ii(d, a, b, c, x[i + 7], 10, 1126891415); c = this.md5ii(c, d, a, b, x[i + 14], 15, -1416354905); b = this.md5ii(b, c, d, a, x[i + 5], 21, -57434055); a = this.md5ii(a, b, c, d, x[i + 12], 6, 1700485571); d = this.md5ii(d, a, b, c, x[i + 3], 10, -1894986606); c = this.md5ii(c, d, a, b, x[i + 10], 15, -1051523); b = this.md5ii(b, c, d, a, x[i + 1], 21, -2054922799); a = this.md5ii(a, b, c, d, x[i + 8], 6, 1873313359); d = this.md5ii(d, a, b, c, x[i + 15], 10, -30611744); c = this.md5ii(c, d, a, b, x[i + 6], 15, -1560198380); b = this.md5ii(b, c, d, a, x[i + 13], 21, 1309151649); a = this.md5ii(a, b, c, d, x[i + 4], 6, -145523070); d = this.md5ii(d, a, b, c, x[i + 11], 10, -1120210379); c = this.md5ii(c, d, a, b, x[i + 2], 15, 718787259); b = this.md5ii(b, c, d, a, x[i + 9], 21, -343485551); a = this.safeAdd(a, olda); b = this.safeAdd(b, oldb); c = this.safeAdd(c, oldc); d = this.safeAdd(d, oldd) } return [a, b, c, d] }, binl2rstr(input) { var i; var output = ''; var length32 = input.length * 32; for (i = 0; i < length32; i += 8) { output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff) } return output }, rstr2binl(input) { var i; var output = []; output[(input.length >> 2) - 1] = undefined; for (i = 0; i < output.length; i += 1) { output[i] = 0 } var length8 = input.length * 8; for (i = 0; i < length8; i += 8) { output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32) } return output }, rstrMD5(s) { return this.binl2rstr(this.binlMD5(this.rstr2binl(s), s.length * 8)) }, rstrHMACMD5(key, data) { var i; var bkey = this.rstr2binl(key); var ipad = []; var opad = []; var hash; ipad[15] = opad[15] = undefined; if (bkey.length > 16) { bkey = this.binlMD5(bkey, key.length * 8) } for (i = 0; i < 16; i += 1) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5c5c5c5c } hash = this.binlMD5(ipad.concat(this.rstr2binl(data)), 512 + data.length * 8); return this.binl2rstr(this.binlMD5(opad.concat(hash), 512 + 128)) }, rstr2hex(input) { var hexTab = '0123456789abcdef'; var output = ''; var x; var i; for (i = 0; i < input.length; i += 1) { x = input.charCodeAt(i); output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f) } return output }, str2rstrUTF8(input) { return unescape(encodeURIComponent(input)) }, rawMD5(s) { return this.rstrMD5(this.str2rstrUTF8(s)) }, hexMD5(s) { return this.rstr2hex(this.rawMD5(s)) }, rawHMACMD5(k, d) { return this.rstrHMACMD5(this.str2rstrUTF8(k), str2rstrUTF8(d)) }, hexHMACMD5(k, d) { return this.rstr2hex(this.rawHMACMD5(k, d)) }, md5(string, key, raw) { if (!key) { if (!raw) { return this.hexMD5(string) } return this.rawMD5(string) } if (!raw) { return this.hexHMACMD5(key, string) } return this.rawHMACMD5(key, string) }, getSig(requestParam, sk, feature, mode) { var sig = null; var requestArr = []; Object.keys(requestParam).sort().forEach(function (key) { requestArr.push(key + '=' + requestParam[key]) }); if (feature == 'search') { sig = '/ws/place/v1/search?' + requestArr.join('&') + sk } if (feature == 'suggest') { sig = '/ws/place/v1/suggestion?' + requestArr.join('&') + sk } if (feature == 'reverseGeocoder') { sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk } if (feature == 'geocoder') { sig = '/ws/geocoder/v1/?' + requestArr.join('&') + sk } if (feature == 'getCityList') { sig = '/ws/district/v1/list?' + requestArr.join('&') + sk } if (feature == 'getDistrictByCityId') { sig = '/ws/district/v1/getchildren?' + requestArr.join('&') + sk } if (feature == 'calculateDistance') { sig = '/ws/distance/v1/?' + requestArr.join('&') + sk } if (feature == 'direction') { sig = '/ws/direction/v1/' + mode + '?' + requestArr.join('&') + sk } sig = this.md5(sig); return sig }, location2query(data) { if (typeof data == 'string') { return data } var query = ''; for (var i = 0; i < data.length; i++) { var d = data[i]; if (!!query) { query += ';' } if (d.location) { query = query + d.location.lat + ',' + d.location.lng } if (d.latitude && d.longitude) { query = query + d.latitude + ',' + d.longitude } } return query }, rad(d) { return d * Math.PI / 180.0 }, getEndLocation(location) { var to = location.split(';'); var endLocation = []; for (var i = 0; i < to.length; i++) { endLocation.push({ lat: parseFloat(to[i].split(',')[0]), lng: parseFloat(to[i].split(',')[1]) }) } return endLocation }, getDistance(latFrom, lngFrom, latTo, lngTo) { var radLatFrom = this.rad(latFrom); var radLatTo = this.rad(latTo); var a = radLatFrom - radLatTo; var b = this.rad(lngFrom) - this.rad(lngTo); var distance = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLatFrom) * Math.cos(radLatTo) * Math.pow(Math.sin(b / 2), 2))); distance = distance * EARTH_RADIUS; distance = Math.round(distance * 10000) / 10000; return parseFloat(distance.toFixed(0)) }, getWXLocation(success, fail, complete) { wx.getLocation({ type: 'gcj02', success: success, fail: fail, complete: complete }) }, getLocationParam(location) { if (typeof location == 'string') { var locationArr = location.split(','); if (locationArr.length === 2) { location = { latitude: location.split(',')[0], longitude: location.split(',')[1] } } else { location = {} } } return location }, polyfillParam(param) { param.success = param.success || function () { }; param.fail = param.fail || function () { }; param.complete = param.complete || function () { } }, checkParamKeyEmpty(param, key) { if (!param[key]) { var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + key + '参数格式有误'); param.fail(errconf); param.complete(errconf); return true } return false }, checkKeyword(param) { return !this.checkParamKeyEmpty(param, 'keyword') }, checkLocation(param) { var location = this.getLocationParam(param.location); if (!location || !location.latitude || !location.longitude) { var errconf = this.buildErrorConfig(ERROR_CONF.PARAM_ERR, ERROR_CONF.PARAM_ERR_MSG + ' location参数格式有误'); param.fail(errconf); param.complete(errconf); return false } return true }, buildErrorConfig(errCode, errMsg) { return { status: errCode, message: errMsg } }, handleData(param, data, feature) { if (feature == 'search') { var searchResult = data.data; var searchSimplify = []; for (var i = 0; i < searchResult.length; i++) { searchSimplify.push({ id: searchResult[i].id || null, title: searchResult[i].title || null, latitude: searchResult[i].location && searchResult[i].location.lat || null, longitude: searchResult[i].location && searchResult[i].location.lng || null, address: searchResult[i].address || null, category: searchResult[i].category || null, tel: searchResult[i].tel || null, adcode: searchResult[i].ad_info && searchResult[i].ad_info.adcode || null, city: searchResult[i].ad_info && searchResult[i].ad_info.city || null, district: searchResult[i].ad_info && searchResult[i].ad_info.district || null, province: searchResult[i].ad_info && searchResult[i].ad_info.province || null }) } param.success(data, { searchResult: searchResult, searchSimplify: searchSimplify }) } else if (feature == 'suggest') { var suggestResult = data.data; var suggestSimplify = []; for (var i = 0; i < suggestResult.length; i++) { suggestSimplify.push({ adcode: suggestResult[i].adcode || null, address: suggestResult[i].address || null, category: suggestResult[i].category || null, city: suggestResult[i].city || null, district: suggestResult[i].district || null, id: suggestResult[i].id || null, latitude: suggestResult[i].location && suggestResult[i].location.lat || null, longitude: suggestResult[i].location && suggestResult[i].location.lng || null, province: suggestResult[i].province || null, title: suggestResult[i].title || null, type: suggestResult[i].type || null }) } param.success(data, { suggestResult: suggestResult, suggestSimplify: suggestSimplify }) } else if (feature == 'reverseGeocoder') { var reverseGeocoderResult = data.result; var reverseGeocoderSimplify = { address: reverseGeocoderResult.address || null, latitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lat || null, longitude: reverseGeocoderResult.location && reverseGeocoderResult.location.lng || null, adcode: reverseGeocoderResult.ad_info && reverseGeocoderResult.ad_info.adcode || null, city: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.city || null, district: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.district || null, nation: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.nation || null, province: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.province || null, street: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street || null, street_number: reverseGeocoderResult.address_component && reverseGeocoderResult.address_component.street_number || null, recommend: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.recommend || null, rough: reverseGeocoderResult.formatted_addresses && reverseGeocoderResult.formatted_addresses.rough || null }; if (reverseGeocoderResult.pois) { var pois = reverseGeocoderResult.pois; var poisSimplify = []; for (var i = 0; i < pois.length; i++) { poisSimplify.push({ id: pois[i].id || null, title: pois[i].title || null, latitude: pois[i].location && pois[i].location.lat || null, longitude: pois[i].location && pois[i].location.lng || null, address: pois[i].address || null, category: pois[i].category || null, adcode: pois[i].ad_info && pois[i].ad_info.adcode || null, city: pois[i].ad_info && pois[i].ad_info.city || null, district: pois[i].ad_info && pois[i].ad_info.district || null, province: pois[i].ad_info && pois[i].ad_info.province || null }) } param.success(data, { reverseGeocoderResult: reverseGeocoderResult, reverseGeocoderSimplify: reverseGeocoderSimplify, pois: pois, poisSimplify: poisSimplify }) } else { param.success(data, { reverseGeocoderResult: reverseGeocoderResult, reverseGeocoderSimplify: reverseGeocoderSimplify }) } } else if (feature == 'geocoder') { var geocoderResult = data.result; var geocoderSimplify = { title: geocoderResult.title || null, latitude: geocoderResult.location && geocoderResult.location.lat || null, longitude: geocoderResult.location && geocoderResult.location.lng || null, adcode: geocoderResult.ad_info && geocoderResult.ad_info.adcode || null, province: geocoderResult.address_components && geocoderResult.address_components.province || null, city: geocoderResult.address_components && geocoderResult.address_components.city || null, district: geocoderResult.address_components && geocoderResult.address_components.district || null, street: geocoderResult.address_components && geocoderResult.address_components.street || null, street_number: geocoderResult.address_components && geocoderResult.address_components.street_number || null, level: geocoderResult.level || null }; param.success(data, { geocoderResult: geocoderResult, geocoderSimplify: geocoderSimplify }) } else if (feature == 'getCityList') { var provinceResult = data.result[0]; var cityResult = data.result[1]; var districtResult = data.result[2]; param.success(data, { provinceResult: provinceResult, cityResult: cityResult, districtResult: districtResult }) } else if (feature == 'getDistrictByCityId') { var districtByCity = data.result[0]; param.success(data, districtByCity) } else if (feature == 'calculateDistance') { var calculateDistanceResult = data.result.elements; var distance = []; for (var i = 0; i < calculateDistanceResult.length; i++) { distance.push(calculateDistanceResult[i].distance) } param.success(data, { calculateDistanceResult: calculateDistanceResult, distance: distance }) } else if (feature == 'direction') { var direction = data.result.routes; param.success(data, direction) } else { param.success(data) } }, buildWxRequestConfig(param, options, feature) { var that = this; options.header = { "content-type": "application/json" }; options.method = 'GET'; options.success = function (res) { var data = res.data; if (data.status === 0) { that.handleData(param, data, feature) } else { param.fail(data) } }; options.fail = function (res) { res.statusCode = ERROR_CONF.WX_ERR_CODE; param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)) }; options.complete = function (res) { var statusCode = +res.statusCode; switch (statusCode) { case ERROR_CONF.WX_ERR_CODE: { param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)); break } case ERROR_CONF.WX_OK_CODE: { var data = res.data; if (data.status === 0) { param.complete(data) } else { param.complete(that.buildErrorConfig(data.status, data.message)) } break } default: { param.complete(that.buildErrorConfig(ERROR_CONF.SYSTEM_ERR, ERROR_CONF.SYSTEM_ERR_MSG)) } } }; return options }, locationProcess(param, locationsuccess, locationfail, locationcomplete) { var that = this; locationfail = locationfail || function (res) { res.statusCode = ERROR_CONF.WX_ERR_CODE; param.fail(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)) }; locationcomplete = locationcomplete || function (res) { if (res.statusCode == ERROR_CONF.WX_ERR_CODE) { param.complete(that.buildErrorConfig(ERROR_CONF.WX_ERR_CODE, res.errMsg)) } }; if (!param.location) { that.getWXLocation(locationsuccess, locationfail, locationcomplete) } else if (that.checkLocation(param)) { var location = Utils.getLocationParam(param.location); locationsuccess(location) } } }; class QQMapWX { constructor(options) { if (!options.key) { throw Error('key值不能为空') } this.key = options.key }; search(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (!Utils.checkKeyword(options)) { return } var requestParam = { keyword: options.keyword, orderby: options.orderby || '_distance', page_size: options.page_size || 10, page_index: options.page_index || 1, output: 'json', key: that.key }; if (options.address_format) { requestParam.address_format = options.address_format } if (options.filter) { requestParam.filter = options.filter } var distance = options.distance || "1000"; var auto_extend = options.auto_extend || 1; var region = null; var rectangle = null; if (options.region) { region = options.region } if (options.rectangle) { rectangle = options.rectangle } var locationsuccess = function (result) { if (region && !rectangle) { requestParam.boundary = "region(" + region + "," + auto_extend + "," + result.latitude + "," + result.longitude + ")"; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'search') } } else if (rectangle && !region) { requestParam.boundary = "rectangle(" + rectangle + ")"; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'search') } } else { requestParam.boundary = "nearby(" + result.latitude + "," + result.longitude + "," + distance + "," + auto_extend + ")"; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'search') } } wx.request(Utils.buildWxRequestConfig(options, { url: URL_SEARCH, data: requestParam }, 'search')) }; Utils.locationProcess(options, locationsuccess) }; getSuggestion(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (!Utils.checkKeyword(options)) { return } var requestParam = { keyword: options.keyword, region: options.region || '全国', region_fix: options.region_fix || 0, policy: options.policy || 0, page_size: options.page_size || 10, page_index: options.page_index || 1, get_subpois: options.get_subpois || 0, output: 'json', key: that.key }; if (options.address_format) { requestParam.address_format = options.address_format } if (options.filter) { requestParam.filter = options.filter } if (options.location) { var locationsuccess = function (result) { requestParam.location = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_SUGGESTION, data: requestParam }, "suggest")) }; Utils.locationProcess(options, locationsuccess) } else { if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'suggest') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_SUGGESTION, data: requestParam }, "suggest")) } }; reverseGeocoder(options) { var that = this; options = options || {}; Utils.polyfillParam(options); var requestParam = { coord_type: options.coord_type || 5, get_poi: options.get_poi || 0, output: 'json', key: that.key }; if (options.poi_options) { requestParam.poi_options = options.poi_options } var locationsuccess = function (result) { requestParam.location = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'reverseGeocoder') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_GET_GEOCODER, data: requestParam }, 'reverseGeocoder')) }; Utils.locationProcess(options, locationsuccess) }; geocoder(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'address')) { return } var requestParam = { address: options.address, output: 'json', key: that.key }; if (options.region) { requestParam.region = options.region } if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'geocoder') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_GET_GEOCODER, data: requestParam }, 'geocoder')) }; getCityList(options) { var that = this; options = options || {}; Utils.polyfillParam(options); var requestParam = { output: 'json', key: that.key }; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'getCityList') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_CITY_LIST, data: requestParam }, 'getCityList')) }; getDistrictByCityId(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'id')) { return } var requestParam = { id: options.id || '', output: 'json', key: that.key }; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'getDistrictByCityId') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_AREA_LIST, data: requestParam }, 'getDistrictByCityId')) }; calculateDistance(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'to')) { return } var requestParam = { mode: options.mode || 'walking', to: Utils.location2query(options.to), output: 'json', key: that.key }; if (options.from) { options.location = options.from } if (requestParam.mode == 'straight') { var locationsuccess = function (result) { var locationTo = Utils.getEndLocation(requestParam.to); var data = { message: "query ok", result: { elements: [] }, status: 0 }; for (var i = 0; i < locationTo.length; i++) { data.result.elements.push({ distance: Utils.getDistance(result.latitude, result.longitude, locationTo[i].lat, locationTo[i].lng), duration: 0, from: { lat: result.latitude, lng: result.longitude }, to: { lat: locationTo[i].lat, lng: locationTo[i].lng } }) } var calculateResult = data.result.elements; var distanceResult = []; for (var i = 0; i < calculateResult.length; i++) { distanceResult.push(calculateResult[i].distance) } return options.success(data, { calculateResult: calculateResult, distanceResult: distanceResult }) }; Utils.locationProcess(options, locationsuccess) } else { var locationsuccess = function (result) { requestParam.from = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'calculateDistance') } wx.request(Utils.buildWxRequestConfig(options, { url: URL_DISTANCE, data: requestParam }, 'calculateDistance')) }; Utils.locationProcess(options, locationsuccess) } }; direction(options) { var that = this; options = options || {}; Utils.polyfillParam(options); if (Utils.checkParamKeyEmpty(options, 'to')) { return } var requestParam = { output: 'json', key: that.key }; if (typeof options.to == 'string') { requestParam.to = options.to } else { requestParam.to = options.to.latitude + ',' + options.to.longitude } var SET_URL_DIRECTION = null; options.mode = options.mode || MODE.driving; SET_URL_DIRECTION = URL_DIRECTION + options.mode; if (options.from) { options.location = options.from } if (options.mode == MODE.driving) { if (options.from_poi) { requestParam.from_poi = options.from_poi } if (options.heading) { requestParam.heading = options.heading } if (options.speed) { requestParam.speed = options.speed } if (options.accuracy) { requestParam.accuracy = options.accuracy } if (options.road_type) { requestParam.road_type = options.road_type } if (options.to_poi) { requestParam.to_poi = options.to_poi } if (options.from_track) { requestParam.from_track = options.from_track } if (options.waypoints) { requestParam.waypoints = options.waypoints } if (options.policy) { requestParam.policy = options.policy } if (options.plate_number) { requestParam.plate_number = options.plate_number } } if (options.mode == MODE.transit) { if (options.departure_time) { requestParam.departure_time = options.departure_time } if (options.policy) { requestParam.policy = options.policy } } var locationsuccess = function (result) { requestParam.from = result.latitude + ',' + result.longitude; if (options.sig) { requestParam.sig = Utils.getSig(requestParam, options.sig, 'direction', options.mode) } wx.request(Utils.buildWxRequestConfig(options, { url: SET_URL_DIRECTION, data: requestParam }, 'direction')) }; Utils.locationProcess(options, locationsuccess) } }; module.exports = QQMapWX; --------------------------------------------------------------------------------