首页 正文

共享单车怎么用微信骑

大家注意了!现在用微信也能扫小黄车了!


你是否还在为ofo没有微信小程序而发愁?

你是否还在因为共享单车APP占用手机存储空间而无可奈何?

然而就在昨天,共享单车原创者和领骑者ofo小黄车宣布,ofo微信小程序正式上线。这也就是说,从现在开始,我们只需用微信“扫一扫”,就可进入ofo小程序,解锁小黄车!

让人难以想象的是,这个曾在6月份特地辟谣称“没有官方微信小程序”的ofo公众号还关联了官方微信小程序。不仅如此,在微信端使用ofo的新用户还可以享受每周前五次骑行免押金、免费骑的特惠福利,就算是老用户领张月卡也可享受畅骑30天。


如此可见,微信小程序的魅力不是一般的大啊。为此,我特意整理了有关微信小程序与传统原生应用对比的资料。看看为什么刚刚进入支付宝小程序的ofo也要做微信小程序。


大家从资料中就可以看出来,使用微信小程序不但成本低、开发时间少,而且用户使用门槛很低,这就足以给ofo留住10%-20%的低频客户。而且从用户体验上讲,小程序的体验已经非常接近原生,和HTML5的应用差距很大。这会进一步推动用户用小程序代替APP。不同于游戏,像共享单车类的App提供的都是简单的信息展示、提交功能,并没有用到很多系统的原生能力,用微信提供的界面实现毫无压力。

最重要的一点是微信小程序的存在,使大家使用手机的方式更加简单,不需要下载和安装,用完即走,这就进一步凸显了ofo“骑时可以更轻松”的服务理念。



至于用法很简单,大家在搜索中输入“ofo小黄车”即可看到“ofo小黄车官方版”,点进去会发现,用法和APP的用法一样。

据楚北网报道称,截至目前,除ofo App外,用户可通过滴滴出行、百度地图、支付宝、高德地图、Keep、ofo微信小程序等各个渠道开启ofo骑车服务,多个入口多种选择,使用便捷,轻松高效。

迷之兴奋,微信做共享单车?Webike是什么我给你讲清楚

文 | 于欣烈 | 速度荷尔蒙

上面这张图今天在我的朋友圈刷屏了。

不少微信群里也是一片哗然,微信要进入共享单车的传言扑面而来。中国共享单车市场真的会出现摩拜、ofo 和 Webike 的红绿灯组合吗?

腾讯的同学马上开始「勘误」:这些自行车只是公司内部自用,并非商业项目。

我觉得,引发各路人士如此兴奋的原因,主要是巨头无小事,一举一动都会被深度解读。而微信颇为「高调」地拿出 Webike 这样一个项目名称,很容易引发外界联想。

我先给大家讲讲 Webike 究竟都是什么车,微信的同学要怎么玩儿。毕竟大多数文章里都语焉不详。

WeBike 一共有两款车型:

银河系列

后街系列

这两款自行车都来自 700bike 给微信制作的公司定制款,700bike 之前也曾给穷游等互联网公司做过定制版自行车。

来,看下 700bike 银河、后街 mini 两款车型图,和 Webike 车型可以一一对应。

银河的价格也是不低呀。

那这些小绿车,想必也是用扫码方式开锁骑行喽?微信扫一扫成为共享单车市场的入口?随之而来的商业机会,想想也让人有点小激动呢。

然而,这些 Webike 身上并没有二维码,也不会像摩拜那样可以用手机定位寻车。

从功能上来说,他们就是最普通的自行车而已。跟微信 app、智能手机都没什么关系。

Webike 的用法相当传统,跟移动互联网没半毛钱关系。

微信员工要用工卡才能在行政部门免费借用 Webike,每人限借1辆。然后还要在一份纸质借用文档上签字,借车人自己保留下联。

嗯,流程就像你在酒店礼宾部寄存行李类似。

微信的同学们要在办公室时间才能办理借车手续,然后1天内归还。

这就是关于 Webike 的一切。共享单车创业公司的同学们,你们可以睡个好觉了。

不「赞赏」没关系,点个「赞」吧!

欢迎关注微信公众号:速度荷尔蒙

更多文章:叭叭呜 - 知乎专栏

【转】给ofo共享单车撸一个微信小程序


1.首页地图页


2.维修报障页


3.登录页


4.钱包余额页


5.充值页


6.获取了密码页


7.计费页
1.准备工作
微信小程序当然属于腾讯大佬的(给大佬递茶):微信小程序开发者工具,腾讯开放了小程序个人开发平台,只需要一个微信号就可以成为小程序开发者了。
2.小程序页面

必须真机调试测试代码
3.创建页面结构
上一节已经分析了默认的文件结构以及它们的功能,现在我们要创建ofo小程序所需要的页面。

1.删除pages下默认的index文件夹,logs/utils文件夹可选择性删除2.在与pages同级目录下创建images文件夹,存放页面需要用到的图标,下载图标3.本小程序不需要在app.js里面编写内容,可以注释这里面的代码4.在app.json里,删掉默认代码,编写如下代码(app.json文件里不能有任何注释,这里是为了描述页面功能更直观):

{
"pages":[
"pages/index/index", // 地图页
"pages/warn/index", // 车辆报障页
"pages/scanresult/index", // 扫码成功页
"pages/billing/index", // 开始计费页
"pages/my/index", // 账户页
"pages/wallet/index", // 钱包页
"pages/charge/index", // 充值页
"pages/logs/logs" // 日志页
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#b9dd08", // 标题栏背景色
"navigationBarTitleText": "ofo 共享单车", // 标题栏文字
"navigationBarTextStyle":"black" // 标题栏文字样式
}
}

5.app.wxss是通用样式,先添加几个通用样式,以后用得到:

/**app.wxss**/
.container{
background-color: #f2f2f2;
height: 100vh;
}
.title{
background-color: #f2f2f2;
padding: 30rpx 0 30rpx 50rpx;
font-size: 28rpx;
color: #000;
}
.tapbar{
display: flex;
align-items: center;
justify-content: space-between;
background-color: #fff;
padding: 40rpx;
}
.btn-charge{
width: 90%;
background-color: #b9dd08;
margin: 40rpx auto 30rpx;
text-align: center;
}

保存后,你的pages文件夹下就是这样的界面了(在app.json下创建路径会自动创建文件夹,贼方便)



页面结构
4.编写地图首页 (index文件夹)
先来回看一下效果图



1.首页地图页

页面分析:
1.整页显示地图,宽高占手机窗口的100%;
2.地图之上有五个按钮图标和多个黄色ofo标记:定位按钮,立即用车按钮,举报按钮,黄色头像按钮和位于地图中心的标记。
4.1 要在整页显示地图,我们可以在index.wxml引入地图组件:
<!--index.wxml-->
<view class="container">
<map id="ofoMap"
latitude="{{latitude}}" // 纬度
longitude="{{longitude}}" // 经度
scale="{{scale}}" // 缩放级别
show-location/> // 显示带有方向的小圆点
</view>

{{...}} 里面是数据变量,由js里的data对象定义。
4.2 初始化数据,在index.js的data对象里添加如下代码:
//index.js
Page({
data: {
scale: 18, // 缩放级别,默认18,数值在0~18之间
latitude: 0, // 给个默认值
longitude: 0 // 给个默认值
},
onLoad:function(options){
// 页面初始化 options为页面跳转所带来的参数
},
onReady:function(){
// 页面渲染完成
},
onShow:function(){
// 页面显示
},
onHide:function(){
// 页面隐藏
},
onUnload:function(){
// 页面关闭
}

这样我们的地图就默认中心位置为经度 0,纬度0。当然可能在开发者工具里显示不出来,莫慌!这不是我们想要的,我们想要的是我们自己的位置,所以得先获取我们当前所在位置的经纬度,在index.js里的onLoad方法里添加如下代码:
onLoad: function(options){
// 页面初始化 options为页面跳转所带来的参数

// 调用wx.getLocation系统API,获取并设置当前位置经纬度
wx.getLocation({
type: "gcj02", // 坐标系类型
// 获取经纬度成功回调
success: (res) => { // es6 箭头函数,可以解绑当前作用域的this指向,使得下面的this可以绑定到Page对象
this.setData({ // 为data对象里定义的经纬度默认值设置成获取到的真实经纬度,这样就可以在地图上显示我们的真实位置
longitude: res.longitude,
latitude: res.latitude
})
}
});
}

res是一个数据对象,它是由调用的对应API传过来的,如果你想知道res的具体值,可以在成功回调函数里打印,然后在控制台输出:console.log(res)。在调用一个陌生API的时候可以用这种方法查看返回的对象数据,对处理逻辑很有帮助。
我们在地图上显示了我们的真实位置,但没有移动到中心位置,wx.moveToLocation()函数可以把当前位置移动到地图中心。修改index.js:
//index.js
var app = getApp()
Page({
data: {
scale: 18,
latitude: 0,
longitude: 0
},
// 页面加载
onLoad: function(options){
// 1.页面初始化 options为页面跳转所带来的参数

// 2.调用wx.getLocation系统API,获取并设置当前位置经纬度
wx.getLocation({
type: "gcj02", // 坐标系类型
// 获取经纬度成功回调
success: (res) => { // es6 箭头函数,可以解绑当前作用域的this指向,使得下面的this可以绑定到Page对象
this.setData({ // 为data对象里定义的经纬度默认值设置成获取到的真实经纬度,这样就可以在地图上显示我们的真实位置
longitude: res.longitude,
latitude: res.latitude
})
}
});
}
// 页面显示
onShow: function(){
// 1.创建地图上下文,移动当前位置到地图中心
this.mapCtx = wx.createMapContext("ofoMap"); // 地图组件的id
this.movetoPosition()
},
// 定位函数,移动位置到地图中心
movetoPosition: function(){
this.mapCtx.moveToLocation();
}
})

这样,页面一显示就在地图中心显示当前位置。
4.3 满屏显示地图,在index.wxss里编写样式:
/**index.wxss**/
.container{
position: relative;
width: 100%; // 宽度占满设备
height: 100vh; // 高度占满设备
}
#ofoMap{
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
z-index: 1;
}

保存刷新,整个屏幕就都显示地图了>_<
4.4 添加地图上的按钮
其实这里的按钮不是真正的按钮,它们属于地图上的控件属性,并且不随着地图移动。这里有一个坑:
地图组件属于微信原生组件,层级最高,任何元素都不能在地图之上显示,即无论设置多大z-index都无法显示。所以,要想在地图上添加按钮来满足需求,就要用到地图控件属性。更多地图控件属性说明看这里
要添加地图控件,先在地图组件里声明controls="...":
<!--index.wxml-->
<view class="container">
<map id="ofoMap"
latitude="{{latitude}}" // 纬度
longitude="{{longitude}}" // 经度
scale="{{scale}}" // 缩放级别
controls="{{controls}}" // 地图控件数组,多个控件存放在数组里
show-location/> // 显示带有方向的小圆点
</view>

然后在index.js设置controls(代码注释还是挺多的)
//index.js
var app = getApp()
Page({
data: {
scale: 18,
latitude: 0,
longitude: 0
},
// 页面加载
onLoad: function(options){
// 1.页面初始化 options为页面跳转所带来的参数

// 2.调用wx.getLocation系统API,获取并设置当前位置经纬度
...已省略

// 3.设置地图控件的位置及大小,通过设备宽高定位
wx.getSystemInfo({ // 系统API,获取系统信息,比如设备宽高
success: (res) => {
this.setData({
// 定义控件数组,可以在data对象初始化为[],也可以不初始化,取决于是否需要更好的阅读
controls: [{
id: 1, // 给控件定义唯一id
iconPath: '/images/location.png', // 控件图标
position: { // 控件位置
left: 20, // 单位px
top: res.windowHeight - 80, // 根据设备高度设置top值,可以做到在不同设备上效果一致
width: 50, // 控件宽度/px
height: 50 // 控件高度/px
},
clickable: true // 是否可点击,默认为true,可点击
},
{
id: 2,
iconPath: '/images/use.png',
position: {
left: res.windowWidth/2 - 45,
top: res.windowHeight - 100,
width: 90,
height: 90
},
clickable: true
},
{
id: 3,
iconPath: '/images/warn.png',
position: {
left: res.windowWidth - 70,
top: res.windowHeight - 80,
width: 50,
height: 50
},
clickable: true
},
{
id: 4,
iconPath: '/images/marker.png',
position: {
left: res.windowWidth/2 - 11,
top: res.windowHeight/2 - 45,
width: 22,
height: 45
},
clickable: false
},
{
id: 5,
iconPath: '/images/avatar.png',
position: {
left: res.windowWidth - 68,
top: res.windowHeight - 155,
width: 45,
height: 45
},
clickable: true
}]
})
}
});
}
// 页面显示
onShow: function(){
...
},
// 定位函数,移动位置到地图中心
movetoPosition: function(){
this.mapCtx.moveToLocation();
}
})

4.5 为地图控件绑定事件
现在地图上总共有四个图标可点击(地图中心的标记控件不需要点击),我们需要为每个控件绑定不同的事件以实现不同的功能:
1.点击定位控件,触发定位当前位置到地图中心,因为用户在拖动地图,有时需要查看当前所在位置。
2.点击立即用车控件,调用微信内置扫码功能。然后获取开锁密码。
3.点击举报按钮,前往维修报障页面。
4.点击用户头像按钮,前往登录页面进行登录,查看余额,充值等操作
为控件绑定事件,需要在地图控件进行声明:bindcontroltap
<!--index.wxml-->
<view class="container">
<map id="ofoMap"
latitude="{{latitude}}" // 纬度
longitude="{{longitude}}" // 经度
scale="{{scale}}" // 缩放级别
controls="{{controls}}" // 地图控件数组,多个控件存放在数组里
bindcontroltap="bindcontroltap" // 控件点击事件
show-location/> // 显示带有方向的小圆点
</view>

注意: bindcontroltap事件会响应所有控件的点击,所以,我们需要根据控件id来区分控件,然后响应不同的事件。
在index.js添加bindcontroltap事件:
//index.js
var app = getApp()
Page({
data: {
scale: 18,
latitude: 0,
longitude: 0
},
// 页面加载
onLoad: function(options){
// 1.获取定时器,用于判断是否已经在计费
this.timer = options.timer;

// 2.调用wx.getLocation系统API,获取并设置当前位置经纬度
...已省略

// 3.设置地图控件的位置及大小,通过设备宽高定位
...已省略
}
// 地图控件点击事件
bindcontroltap: function(e){
// 判断点击的是哪个控件 e.controlId代表控件的id,在页面加载时的第3步设置的id
switch(e.controlId){
// 点击定位控件
case 1: this.movetoPosition();
break;
// 点击立即用车,判断当前是否正在计费,此处只需要知道是调用扫码,后面会讲到this.timer是怎么来的
case 2: if(this.timer === "" || this.timer === undefined){
// 没有在计费就扫码
wx.scanCode({
success: (res) => {
// 正在获取密码通知
wx.showLoading({
title: '正在获取密码',
mask: true
})
// 请求服务器获取密码和车号
wx.request({
url: 'https://www.easy-mock.com/mock/59098d007a878d73716e966f/ofodata/password',
data: {},
method: 'GET',
success: function(res){
// 请求密码成功隐藏等待框
wx.hideLoading();
// 携带密码和车号跳转到密码页
wx.redirectTo({
url: '../scanresult/index?password=' + res.data.data.password + '&number=' + res.data.data.number,
success: function(res){
wx.showToast({
title: '获取密码成功',
duration: 1000
})
}
})
}
})
}
})
// 当前已经在计费就回退到计费页
}else{
wx.navigateBack({
delta: 1
})
}
break;
// 点击保障控件,跳转到报障页
case 3: wx.navigateTo({
url: '../warn/index'
});
break;
// 点击头像控件,跳转到个人中心
case 5: wx.navigateTo({
url: '../my/index'
});
break;
default: break;
}
},
// 页面显示
onShow: function(){
...已省略
},
// 定位函数,移动位置到地图中心
movetoPosition: function(){
this.mapCtx.moveToLocation();
}
})

这里用到的API:
扫码API: wx.scanCode({})
显示加载框: wx.showLoading()
隐藏加载框: wx.hideLoading()
显示提示框: wx.showToast()
隐藏提示框: wx.hideToast()
向服务器发送请求:wx.request({})
关闭当前页面,跳转到指定页面: wx.redirectTo({})
保留当前页面,跳转到指定页面: wx.navigateTo({})
回退到指定页面: wx.naivgateBack({})
查看详细用法,查看官方API文档
tips: 跳转页面传参示例

let num = 1;
wx.navigateTo({
url: '../other/index?num=' + num
});
// other页面
onLoad: function(options){
console.log(options.num); // 1
}

多个参数用&分隔,如 'index?num=' + num + '&text=' + 'text'
4.6 在地图上添加单车标记makers和位置连线,还是在地图组件里先声明:
<!--index.wxml-->
<view class="container">
<map id="ofoMap"
latitude="{{latitude}}" // 纬度
longitude="{{longitude}}" // 经度
scale="{{scale}}" // 缩放级别
controls="{{controls}}" // 地图控件数组,多个控件存放在数组里
bindcontroltap="bindcontroltap" // 控件点击事件
polyline="{{polyline}}" // 位置连线
markers="{{markers}}" // 标记数组
bindmarkertap="bindmarkertap" // 标记点击事件
show-location/> // 显示带有方向的小圆点
</view>

然后在index.js里定义:
//index.js
var app = getApp()
Page({
data: {
scale: 18,
latitude: 0,
longitude: 0
},
// 页面加载
onLoad: function(options){
// 1.获取定时器,用于判断是否已经在计费
this.timer = options.timer;

// 2.调用wx.getLocation系统API,获取并设置当前位置经纬度
...已省略

// 3.设置地图控件的位置及大小,通过设备宽高定位
...已省略

// 4.请求服务器,显示附近的单车,用marker标记
wx.request({
url: 'https://www.easy-mock.com/mock/59098d007a878d73716e966f/ofodata/biyclePosition',
data: {},
method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
// header: {}, // 设置请求的 header
success: (res) => {
this.setData({
markers: res.data.data
})
}
})
}
// 地图控件点击事件
bindcontroltap: function(e){
...已省略
},
// 地图标记点击事件,连接用户位置和点击的单车位置
bindmarkertap: function(e){
let _markers = this.data.markers; // 拿到标记数组
let markerId = e.markerId; // 获取点击的标记id
let currMaker = _markers[markerId]; // 通过id,获取当前点击的标记
this.setData({
polyline: [{
points: [{ // 连线起点
longitude: this.data.longitude,
latitude: this.data.latitude
}, { // 连线终点(当前点击的标记)
longitude: currMaker.longitude,
latitude: currMaker.latitude
}],
color:"#FF0000DD",
width: 1,
dottedLine: true
}],
scale: 18
})
},
// 页面显示
onShow: function(){
...已省略
},
// 定位函数,移动位置到地图中心
movetoPosition: function(){
this.mapCtx.moveToLocation();
}
})

4.7 用户拖动地图事件
我们已经为地图控件和标记响应了不同的事件,现在如果用户拖动地图,我们需要在拖动附件显示单车,在地图组件声明地图拖动事件:
<!--index.wxml-->
<view class="container">
<map id="ofoMap"
latitude="{{latitude}}" // 纬度
longitude="{{longitude}}" // 经度
scale="{{scale}}" // 缩放级别
controls="{{controls}}" // 地图控件数组,多个控件存放在数组里
bindcontroltap="bindcontroltap" // 控件点击事件
polyline="{{polyline}}" // 位置连线
markers="{{markers}}" // 标记数组
bindmarkertap="bindmarkertap" // 标记点击事件
bindregionchange="bindregionchange" // 拖动地图事件
show-location/> // 显示带有方向的小圆点
</view>

在index.js里实现这个事件方法:
//index.js
var app = getApp()
Page({
data: {
scale: 18,
latitude: 0,
longitude: 0
},
// 页面加载
onLoad: function(options){
// 1.获取定时器,用于判断是否已经在计费
this.timer = options.timer;

// 2.调用wx.getLocation系统API,获取并设置当前位置经纬度
...已省略

// 3.设置地图控件的位置及大小,通过设备宽高定位
...已省略

// 4.请求服务器,显示附近的单车,用marker标记
...已省略
}
// 地图控件点击事件
bindcontroltap: function(e){
...已省略
},
// 地图视野改变事件
bindregionchange: function(e){
// 拖动地图,获取附件单车位置
if(e.type == "begin"){
wx.request({
url: 'https://www.easy-mock.com/mock/59098d007a878d73716e966f/ofodata/biyclePosition',
data: {},
method: 'GET',
success: (res) => {
this.setData({
_markers: res.data.data
})
}
})
// 停止拖动,显示单车位置
}else if(e.type == "end"){
this.setData({
markers: this.data._markers
})
}
},
// 地图标记点击事件,连接用户位置和点击的单车位置
bindmarkertap: function(e){
...已省略
},
// 页面显示
onShow: function(){
...已省略
},
// 定位函数,移动位置到地图中心
movetoPosition: function(){
this.mapCtx.moveToLocation();
}
})

至此,首页地图已经完成了,接下来要编写响应的跳转页面
5.编写扫码之后的获取密码页(scanresult文件夹)
上一节我们为立即用车响应了扫码事件,扫码成功后的页面是酱的:



获取了密码的页面
页面分析
1.后台需要拿到开锁密码,然后显示在页面上
2.我们需要一个定时器,规定多长时间用来检查车辆,这期间可以点击回首页去车辆报障链接,当然也就取消了本次扫码。
3.检查时长完成后,自动跳转到计费页面
1.页面布局
<!--pages/scanresult/index.wxml-->
<view class="container">
<view class="password-title">
<text>开锁密码</text>
</view>
<view class="password-content">
<text>{{password}}</text>
</view>
<view class="tips">
<text>请使用密码解锁,{{time}}s后开始计费</text>
<view class="tips-action" bindtap="moveToWarn">
车辆有问题?
<text class="tips-href">回首页去车辆报障</text>
</view>
</view>
</view>

2.页面样式
.container{
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
background-color: #fff;
}
.password-title,.tips{
width: 100%;
flex: 1;
text-align: center;
padding: 60rpx 0;
}
.password-content{
width: 100%;
flex: 8;
text-align: center;
font-size: 240rpx;
font-weight: 900;
}
.tips{
font-size: 32rpx;
}
.tips .tips-action{
margin-top: 20rpx;
}
.tips .tips-href{
color: #b9dd08
}

3.页面数据逻辑
// pages/scanresult/index.js
Page({
data:{
time: 9 // 默认计时时长,这里设短一点,用于调试,ofo app是90s
},
// 页面加载
onLoad:function(options){
// 获取解锁密码
this.setData({
password: options.password
})
// 设置初始计时秒数
let time = 9;
// 开始定时器
this.timer = setInterval(() => {
this.setData({
time: -- time
});
// 读完秒后携带单车号码跳转到计费页
if(time = 0){
clearInterval(this.timer)
wx.redirectTo({
url: '../billing/index?number=' + options.number
})
}
},1000)
},
// 点击去首页报障
moveToWarn: function(){
// 清除定时器
clearInterval(this.timer)
wx.redirectTo({
url: '../index/index'
})
}
})

注意:这里的this.timer不会被传参到pages/index/index.js里的onload函数里,被传参到首页的定时器是计费页的定时器,后面会讲到
tips: onload函数参数说明: options的值是扫码成功后请求服务器获取的单车编号和开锁密码
// pages/index/index.js
// 点击立即用车,判断当前是否正在计费
case 2: if(this.timer === "" || this.timer === undefined){
// 没有在计费就扫码
wx.scanCode({
success: (res) => {
// 正在获取密码通知
wx.showLoading({
title: '正在获取密码',
mask: true
})
// 请求服务器获取密码和车号
wx.request({
url: 'https://www.easy-mock.com/mock/59098d007a878d73716e966f/ofodata/password',
data: {},
method: 'GET',
success: function(res){
// 请求密码成功隐藏等待框
wx.hideLoading();
// 携带密码和车号跳转到密码页
wx.redirectTo({
url: '../scanresult/index?password=' + res.data.data.password + '&number=' + res.data.data.number,
success: function(res){
wx.showToast({
title: '获取密码成功',
duration: 1000
})
}
})
}
})
}
})
// 当前已经在计费就回退到计费页
}else{
wx.navigateBack({
delta: 1
})
}
break;
// pages/scanresult/index.js
onload: function(options){
console.log(options); // { password: "", number: "" }
}

6.编写计费页(billing文件夹)
上节中我们设置了计时器完成后,跳转到计费页,它是酱的:



计费页

页面分析:
1.后台需要拿到单车编号,并显示在页面上
2.我们需要一个计时器累加骑行事件用来计费,而且可以显示最大单位是小时
3.两个按钮:结束骑行,回到地图 。其中,点击结束骑行,关闭计时器,根据累计时长计费;点击回到地图,如果计时器已经关闭了,就关闭计费页,跳转到地图。如果计时器仍然在计时,保留当前页面,跳转到地图。
4.点击回到地图会把计时器状态带给首页,首页做出判断,判定再次点击立即用车响应合理逻辑(已经在计费,不能重复扫码。已经停止计费了,需要重新扫码)
1.页面结构
<!--pages/billing/index.wxml-->
<view class="container">
<view class="number">
<text>当前单车编号: {{number}}</text>
</view>
<view class="time">
<view class="time-title">
<text>{{billing}}</text>
</view>
<view class="time-content">
<text>{{hours}}:{{minuters}}:{{seconds}}</text>
</view>
</view>

<view class="endride">
<button type="warn" disabled="{{disabled}}" bindtap="endRide">结束骑行</button>
<button type="primary" bindtap="moveToIndex">回到地图</button>
</view>
</view>

2.页面样式
.container{
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
background-color: #fff;
}
.number,.endride{
padding: 60rpx 0;
flex: 2;
width: 100%;
text-align: center;
}
.time{
text-align: center;
width: 100%;
flex: 6;
}
.time .time-content{
font-size: 100rpx;
}
.endride button{
width: 90%;
margin-top: 40rpx;
}

3.页面数据逻辑
// pages/billing/index.js
Page({
data:{
hours: 0,
minuters: 0,
seconds: 0,
billing: "正在计费"
},
// 页面加载
onLoad:function(options){
// 获取车牌号,设置定时器
this.setData({
number: options.number,
timer: this.timer
})
// 初始化计时器
let s = 0;
let m = 0;
let h = 0;
// 计时开始
this.timer = setInterval(() => {
this.setData({
seconds: s++
})
if(s == 60){
s = 0;
m++;
setTimeout(() => {
this.setData({
minuters: m
});
},1000)
if(m == 60){
m = 0;
h++
setTimeout(() => {
this.setData({
hours: h
});
},1000)
}
};
},1000)
},
// 结束骑行,清除定时器
endRide: function(){
clearInterval(this.timer);
this.timer = "";
this.setData({
billing: "本次骑行耗时",
disabled: true
})
},
// 携带定时器状态回到地图
moveToIndex: function(){
// 如果定时器为空
if(this.timer == ""){
// 关闭计费页跳到地图
wx.redirectTo({
url: '../index/index'
})
// 保留计费页跳到地图
}else{
wx.navigateTo({
url: '../index/index?timer=' + this.timer
})
}
}
})

页面分析的第4步,主要实现在moveToIndex函数里。结束骑行之后,设置定时器值为空,在点击回到地图时判断计时器的状态(值是否为空)。如果为空,关闭计费页,结束本次骑行。如果不为空,携带定时器状态跳转到首页,首页立即用车点击事件就会对传过来的参数(计时器状态)响应合理逻辑。
7.编写维修报障页(warn文件夹)
点击举报控件,页面是酱的:



维修报障页1


维修报障页2

页面分析:
1.页面可以勾选故障类型,所以需要用到复选框组件;可以选择上传或拍摄图片,所以要使用wx.chooseImage({})选取图片API;可以输入车牌号好备注,所以需要使用input输入组件。
2.勾选类型,选择图片,输入备注信息完成后,后台需要获取这些输入的数据提交到服务器以获得反馈。
3.必须勾选类型和选择周围环境图片才能提交,否则弹窗提示。可以选择多张图片,也可以取消选择的图片。
1.页面结构
<!--pages/warn/index.wxml-->
<view class="container">
<view class="choose">
<view class="title">请选择故障类型</view>
<checkbox-group bindchange="checkboxChange" class="choose-grids">
<!-- itemsValue是data对象里定义的数组,item代表数组的每一项,此处语法为循环输出数组的每一项并渲染到每一个复选框。下面还有类似语法 -->
<block wx:for="{{itemsValue}}" wx:key="{{item}}">
<view class="grid">
<checkbox value="{{item.value}}" checked="{{item.checked}}" color="{{item.color}}" />{{item.value}}
</view>
</block>
</checkbox-group>
</view>
<view class="action">
<view class="title">拍摄单车周围环境,便于维修师傅找车</view>
<view class="action-photo">
<block wx:for="{{picUrls}}" wx:key="{{item}}" wx:index="{{index}}">
<image src="{{item}}"><icon type="cancel" data-index="{{index}}" color="red" size="18" class ="del" bindtap="delPic" /></image>
</block>
<text class="add" bindtap="bindCamera">{{actionText}}</text>
</view>
<view class="action-input">
<input bindinput="numberChange" name="number" placeholder="车牌号(车牌损坏不用填)" />
<input bindinput="descChange" name="desc" placeholder="备注" />
</view>
<view class="action-submit">
<button class="submit-btn" type="default" loading="{{loading}}" bindtap="formSubmit" style="background-color: {{btnBgc}}">提交</button>
</view>
</view>
</view>

2.页面样式
/* pages/wallet/index.wxss */
.choose{
background-color: #fff;
}
.choose-grids{
display: flex;
flex-wrap: wrap;
justify-content: space-around;
padding: 50rpx;
}
.choose-grids .grid{
width: 45%;
height: 100rpx;
margin-top: 36rpx;
border-radius: 6rpx;
line-height: 100rpx;
text-align: center;
border: 2rpx solid #b9dd08;
}
.choose-grids .grid:first-child,
.choose-grids .grid:nth-of-type(2){
margin-top: 0;
}
.action .action-photo{
background-color: #fff;
padding: 40rpx 0px 40rpx 50rpx;
}
.action .action-photo image{
position: relative;
display: inline-block;
width: 120rpx;
height: 120rpx;
overflow: visible;
margin-left: 25rpx;
}
.action .action-photo image icon.del{
display: block;
position: absolute;
top: -20rpx;
right: -20rpx;
}
.action .action-photo text.add{
display: inline-block;
width: 120rpx;
height: 120rpx;
line-height: 120rpx;
text-align: center;
font-size: 24rpx;
color: #ccc;
border: 2rpx dotted #ccc;
margin-left: 25rpx;
vertical-align: top;
}
.action .action-input{
padding-left: 50rpx;
margin-top: 30rpx;
background-color: #fff;
}
.action .action-input input{
width: 90%;
padding-top: 40rpx;
padding-bottom: 40rpx;
}
.action .action-input input:first-child{
border-bottom: 2rpx solid #ccc;
padding-bottom: 20rpx;
}
.action .action-input input:last-child{
padding-top: 20rpx;
}
.action .action-submit{
padding: 40rpx 40rpx;
background-color: #f2f2f2;
}

3.页面数据逻辑
// pages/wallet/index.js
Page({
data:{
// 故障车周围环境图路径数组
picUrls: [],
// 故障车编号和备注
inputValue: {
num: 0,
desc: ""
},
// 故障类型数组
checkboxValue: [],
// 选取图片提示
actionText: "拍照/相册",
// 提交按钮的背景色,未勾选类型时无颜色
btnBgc: "",
// 复选框的value,此处预定义,然后循环渲染到页面
itemsValue: [
{
checked: false,
value: "私锁私用",
color: "#b9dd08"
},
{
checked: false,
value: "车牌缺损",
color: "#b9dd08"
},
{
checked: false,
value: "轮胎坏了",
color: "#b9dd08"
},
{
checked: false,
value: "车锁坏了",
color: "#b9dd08"
},
{
checked: false,
value: "违规乱停",
color: "#b9dd08"
},
{
checked: false,
value: "密码不对",
color: "#b9dd08"
},
{
checked: false,
value: "刹车坏了",
color: "#b9dd08"
},
{
checked: false,
value: "其他故障",
color: "#b9dd08"
}
]
},
// 页面加载
onLoad:function(options){
wx.setNavigationBarTitle({
title: '报障维修'
})
},
// 勾选故障类型,获取类型值存入checkboxValue
checkboxChange: function(e){
let _values = e.detail.value;
if(_values.length == 0){
this.setData({
btnBgc: ""
})
}else{
this.setData({
checkboxValue: _values,
btnBgc: "#b9dd08"
})
}
},
// 输入单车编号,存入inputValue
numberChange: function(e){
this.setData({
inputValue: {
num: e.detail.value,
desc: this.data.inputValue.desc
}
})
},
// 输入备注,存入inputValue
descChange: function(e){
this.setData({
inputValue: {
num: this.data.inputValue.num,
desc: e.detail.value
}
})
},
// 提交到服务器
formSubmit: function(e){
if(this.data.picUrls.length > 0 && this.data.checkboxValue.length> 0){
wx.request({
url: 'https://www.easy-mock.com/mock/59098d007a878d73716e966f/ofodata/msg',
data: {
// picUrls: this.data.picUrls,
// inputValue: this.data.inputValue,
// checkboxValue: this.data.checkboxValue
},
method: 'get', // POST
// header: {}, // 设置请求的 header
success: function(res){
wx.showToast({
title: res.data.data.msg,
icon: 'success',
duration: 2000
})
}
})
}else{
wx.showModal({
title: "请填写反馈信息",
content: '看什么看,赶快填反馈信息,削你啊',
confirmText: "我我我填",
cancelText: "劳资不填",
success: (res) => {
if(res.confirm){
// 继续填
}else{
console.log("back")
wx.navigateBack({
delta: 1 // 回退前 delta(默认为1) 页面
})
}
}
})
}

},
// 选择故障车周围环境图 拍照或选择相册
bindCamera: function(){
wx.chooseImage({
count: 4,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
let tfps = res.tempFilePaths;
let _picUrls = this.data.picUrls;
for(let item of tfps){
_picUrls.push(item);
this.setData({
picUrls: _picUrls,
actionText: "+"
});
}
}
})
},
// 删除选择的故障车周围环境图
delPic: function(e){
let index = e.target.dataset.index;
let _picUrls = this.data.picUrls;
_picUrls.splice(index,1);
this.setData({
picUrls: _picUrls
})
}
})

注意: 这里选择的图片,路径为本地路径,如果要上传到服务器,需要调用API上传图片而不是上传本地路径。即不能把picUrls数组上传到服务器。
8.编写登录/未登录页(my文件夹)
点击头像控件,未登录,页面是酱的


未登录页
点击头像控件,已登录,页面是酱的



登录页
页面分析
1.个人中心页有两种状态,即未登录和已登录,所以要求数据驱动页面表现形式
2.点击登录/退出登录按钮需要响应合理逻辑,并改变按钮样式
3.只有登录状态下才会显示我的钱包按钮
1.页面结构(wx:if 是条件语句)
<!--pages/my/index.wxml-->
<view class="container">
<view class="user-info">
<!-- 用户未登录就没有头像-->
<block wx:if="{{userInfo.avatarUrl != ''}}">
<image src="{{userInfo.avatarUrl}}"></image>
</block>
<text>{{userInfo.nickName}}</text>
</view>
<!-- 用户未登录就没有钱包按钮-->
<block wx:if="{{userInfo.avatarUrl != ''}}">
<view class="my-wallet tapbar" bindtap="movetoWallet">
<text>我的钱包</text>
<text>></text>
</view>
</block>
<button bindtap="bindAction" class="btn-login" hover-class="gray" type="{{bType}}" >{{actionText}}</button>
</view>


2.页面样式
/* pages/my/index.wxss */
.user-info{
background-color: #fff;
padding-top: 60rpx;
}
.user-info image{
display: block;
width: 180rpx;
height: 180rpx;
border-radius: 50%;
margin: 0 auto 40rpx;
box-shadow: 0 0 20rpx rgba(0,0,0,.2)
}
.user-info text{
display: block;
text-align: center;
padding: 30rpx 0;
margin-bottom: 30rpx;
}
.btn-login{
position: absolute;
bottom: 60rpx;
width: 90%;
left: 50%;
margin-left: -45%;
}
.gray{
background-color: #ccc;
}

3.页面数据逻辑
// pages/my/index.js
Page({
data:{
// 用户信息
userInfo: {
avatarUrl: "",
nickName: "未登录"
},
bType: "primary", // 按钮类型
actionText: "登录", // 按钮文字提示
lock: false //登录按钮状态,false表示未登录
},
// 页面加载
onLoad:function(){
// 设置本页导航标题
wx.setNavigationBarTitle({
title: '个人中心'
})
// 获取本地数据-用户信息
wx.getStorage({
key: 'userInfo',
// 能获取到则显示用户信息,并保持登录状态,不能就什么也不做
success: (res) => {
wx.hideLoading();
this.setData({
userInfo: {
avatarUrl: res.data.userInfo.avatarUrl,
nickName: res.data.userInfo.nickName
},
bType: res.data.bType,
actionText: res.data.actionText,
lock: true
})
}
});
},
// 登录或退出登录按钮点击事件
bindAction: function(){
this.data.lock = !this.data.lock
// 如果没有登录,登录按钮操作
if(this.data.lock){
wx.showLoading({
title: "正在登录"
});
wx.login({
success: (res) => {
wx.hideLoading();
wx.getUserInfo({
withCredentials: false,
success: (res) => {
this.setData({
userInfo: {
avatarUrl: res.userInfo.avatarUrl,
nickName: res.userInfo.nickName
},
bType: "warn",
actionText: "退出登录"
});
// 存储用户信息到本地
wx.setStorage({
key: 'userInfo',
data: {
userInfo: {
avatarUrl: res.userInfo.avatarUrl,
nickName: res.userInfo.nickName
},
bType: "warn",
actionText: "退出登录"
},
success: function(res){
console.log("存储成功")
}
})
}
})
}
})
// 如果已经登录,退出登录按钮操作
}else{
wx.showModal({
title: "确认退出?",
content: "退出后将不能使用ofo",
success: (res) => {
if(res.confirm){
console.log("确定")
// 退出登录则移除本地用户信息
wx.removeStorageSync('userInfo')
this.setData({
userInfo: {
avatarUrl: "",
nickName: "未登录"
},
bType: "primary",
actionText: "登录"
})
}else {
console.log("cancel")
this.setData({
lock: true
})
}
}
})
}
},
// 跳转至钱包
movetoWallet: function(){
wx.navigateTo({
url: '../wallet/index'
})
}
})

我们将用户信息使用wx.setStorage({})和wx.getStorage({})这两个API来设置和获取本地存储,用于模拟维护用户登录状态。真实情况下需要使用session
9.编写我的钱包页
假设用户已登录,点击钱包,页面是酱的:



钱包余额页

页面分析
1.需要获取钱包余额数据并显示在页面上,充值后数据会自动更新
2.其他可点击按钮分别显示对应的模态框,因为微信只允许五个页面层级,避免过多页面层级造成用户迷失。
1.页面结构
<!--pages/wallet/index.wxml-->
<view class="container">
<view class="overage">
<view>
<text class="overage-header">我的余额(元)</text>
</view>
<view>
<text class="overage-amount">{{overage}}</text>
</view>
<view>
<text bindtap="overageDesc" class="overage-desc">余额说明</text>
</view>
</view>
<button bindtap="movetoCharge" class="btn-charge">充值</button>
<view bindtap="showTicket" class="my-ticket tapbar">
<text>我的用车券</text>
<text><text class="c-g">{{ticket}}张</text>></text>
</view>
<view bindtap="showDeposit" class="my-deposit tapbar">
<text>我的押金</text>
<text><text class="c-y">99元,押金退款</text>></text>
</view>
<view bindtap="showInvcode" class="my-invcode tapbar">
<text>关于ofo</text>
<text>></text>
</view>
</view>

2.页面样式
/* pages/wallet/index.wxss */
.overage{
background-color: #fff;
padding: 40rpx 0;
text-align: center;
}
.overage-header{
font-size: 24rpx;
}
.overage-amount{
display: inline-block;
padding: 20rpx 0;
font-size: 100rpx;
font-weight: 700;
}
.overage-desc{
padding: 10rpx 30rpx;
font-size: 24rpx;
border-radius: 40rpx;
border: 1px solid #666;
}
.my-deposit{
margin-top: 2rpx;
}
.my-invcode{
margin-top: 40rpx;
}
.c-y{
color: #b9dd08;
padding-top: -5rpx;
padding-right: 10rpx;
}
.c-g{
padding-top: -5rpx;
padding-right: 10rpx;
}


3.页面数据逻辑
// pages/wallet/index.js
Page({
data:{
overage: 0,
ticket: 0
},
// 页面加载
onLoad:function(options){
wx.setNavigationBarTitle({
title: '我的钱包'
})
},
// 页面加载完成,更新本地存储的overage
onReady:function(){
wx.getStorage({
key: 'overage',
success: (res) => {
this.setData({
overage: res.data.overage
})
}
})
},
// 页面显示完成,获取本地存储的overage
onShow:function(){
wx.getStorage({
key: 'overage',
success: (res) => {
this.setData({
overage: res.data.overage
})
}
})
},
// 余额说明
overageDesc: function(){
wx.showModal({
title: "",
content: "充值余额0.00元+活动赠送余额0.00元",
showCancel: false,
confirmText: "我知道了",
})
},
// 跳转到充值页面
movetoCharge: function(){
// 关闭当前页面,跳转到指定页面,返回时将不会回到当前页面
wx.redirectTo({
url: '../charge/index'
})
},
// 用车券
showTicket: function(){
wx.showModal({
title: "",
content: "你没有用车券了",
showCancel: false,
confirmText: "好吧",
})
},
// 押金退还
showDeposit: function(){
wx.showModal({
title: "",
content: "押金会立即退回,退款后,您将不能使用ofo共享单车确认要进行此退款吗?",
cancelText: "继续使用",
cancelColor: "#b9dd08",
confirmText: "押金退款",
confirmColor: "#ccc",
success: (res) => {
if(res.confirm){
wx.showToast({
title: "退款成功",
icon: "success",
duration: 2000
})
}
}
})
},
// 关于ofo
showInvcode: function(){
wx.showModal({
title: "ofo共享单车",
content: "微信服务号:ofobike,网址:m.ofo.so",
showCancel: false,
confirmText: "玩的6"
})
}
})

我们将金额信息使用wx.setStorage({})和wx.getStorage({})这两个API来设置和获取本地存储,用于模拟充值逻辑。
设置本地存储API官方文档
10.编写充值页面(charge文件夹)
点击充值按钮,页面是酱的



充值页

页面分析
1.输入金额,存储在data对象里,点击充值后,设置本地金额数据
2.点击充值按钮后自动跳转到钱包页。
1.页面结构
<!--pages/charge/index.wxml-->
<view class="container">
<view class="title">请输入充值金额</view>
<view class="input-box">
<input bindinput="bindInput" />
</view>
<button bindtap="charge" class="btn-charge">充值</button>
</view>


2.页面样式
/* pages/charge/index.wxss */
.input-box{
background-color: #fff;
margin: 0 auto;
padding: 20rpx 0;
border-radius: 10rpx;
width: 90%;
}
.input-box input{
width: 100%;
height: 100%;
text-align: center;
}
3.页面数据逻辑
// pages/charge/index.js
Page({
data:{
inputValue: 0
},
// 页面加载
onLoad:function(options){
wx.setNavigationBarTitle({
title: '充值'
})
},
// 存储输入的充值金额
bindInput: function(res){
this.setData({
inputValue: res.detail.value
})
},
// 充值
charge: function(){
// 必须输入大于0的数字
if(parseInt(this.data.inputValue) <= 0 || isNaN(this.data.inputValue)){
wx.showModal({
title: "警告",
content: "咱是不是还得给你钱?!!",
showCancel: false,
confirmText: "不不不不"
})
}else{
wx.redirectTo({
url: '../wallet/index',
success: function(res){
wx.showToast({
title: "充值成功",
icon: "success",
duration: 2000
})
}
})
}
},
// 页面销毁,更新本地金额,(累加)
onUnload:function(){
wx.getStorage({
key: 'overage',
success: (res) => {
wx.setStorage({
key: 'overage',
data: {
overage: parseInt(this.data.inputValue) + parseInt(res.data.overage)
}
})
},
// 如果没有本地金额,则设置本地金额
fail: (res) => {
wx.setStorage({
key: 'overage',
data: {
overage: parseInt(this.data.inputValue)
},
})
}
})
}
})

充值页面关闭时更新本地金额数据,所以需要在unLoad事件里执行
扩展:使用easy-mock伪造数据
easy-mock可以作为前端开发的伪后端,自己构造数据来测试前端代码。官网戳这里。
比如我们这个小程序用到了后端api接口
1.提交报障信息的反馈
2.单车编号和解锁密码
3.单车经纬度
开篇我们创建了多个页面,然后一个一个页面从页面分析,到完成数据逻辑,分别响应着不同的业务逻辑,有的页面与页面之间有数据往来,我们就通过跳转页面传参或设置本地存储来将它们建立起联系,环环相扣,构建起了整个小程序的基本功能。通过这个小程序,我们发现文档提供的API在不知不觉中已经失去了它的神秘感,它们就是不同的工具,为小程序实现业务请求搭建肢体骨架。

如何看待摩拜单车小程序接入微信扫一扫?

首先感谢 @爱范儿 的分析~

随着各色共享单车的普及,骑单车成为了一种方便、时尚有又健康的选择。而率先用上小程序新能力的「摩拜单车」小程序,成为「一扫即用,用完即走」的绝佳案例。

正巧,第 5 期的知晓程序 · MINA 奖就颁给了「摩拜单车」小程序。在我们对摩拜单车团队的独家采访中,我们也向他们提出了有关接入微信扫一扫的问题。他们的回答,也许可以回答这个问题。

点击这里,立即使用「摩拜单车」小程序。

微信扫码,就能骑走的「摩拜单车」

使用一部摩拜单车,需要几个步骤?

在 app 时代,你得下载、安装摩拜单车的客户端。

对于 95% 的人来说,要他们在路边,用自己流量下载一个 app,简直比登天还难。于是,大部分人在一时兴起之后,就灰飞烟灭了……

「摩拜单车」小程序的出现,巧妙地破解了这个困境:不用安装 app,用「摩拜单车」小程序扫码,就可解锁骑走。

然而,新的问题又来了。有多少大妈大叔,甚至城市小白领,知道小程序是什么东西呢?

于是,微信和摩拜单车联合发了个大招:在摩拜单车二维码不更改的情况下,用微信扫一扫,就能直接跳转到「摩拜单车」小程序。

老用户只需登录账号,便能解锁骑走新用户只需注册账号,交付押金,就能解锁骑走

而在此之前,你若是用微信扫一扫摩拜单车的二维码,去到的可是摩拜单车客户端的下载页面哦。

如今,从在路边发现单车,到扫码骑走,速度从未如此之快。

还记得「摩拜单车」小程序诞生之初,用户系统尚未接入,体验着实糟心。而春节后的数次更新,不仅完善了整个使用流程,更是带来了微信扫码即用的超便捷使用体验。

在以线下场景为主的小程序生态中,「摩拜单车」是其中当之无愧的佼佼者。

使用贴士:如果是 iPhone 6S 及以上的 iOS 用户,只需用力按下微信图标,便可立即调出「扫一扫」选项,快人一步扫码用车!

「摩拜单车」独家专访

1. 请简单介绍一下「摩拜单车」小程序的团队成员。

很难明确地说,小程序的团队有多少人。 因为,公司里几乎每位产品、设计、开发、测试、运维同学都为小程序出了一份力。现在保持了每一到两周,一个版本的迭代速度,不断优化我们的小程序。

2. 作为第一家,也是唯一一家上线小程序的共享单车,「摩拜单车」投入小程序开发的原因和目的是?

小程序可以帮助更多新用户接触体验摩拜,而这正是摩拜投入小程序开发的原因。 同样,这也符合摩拜单车一直以来的目标:为用户提供最好、最简洁的体验和最可靠的短途出行方式。

3.「摩拜单车」的小程序和 app,在功能设计上的主要差别是?为何会这样取舍?

微信扫码用车的特点是「扫码即用,用完即走」,非常适合用户身边有一辆摩拜单车,想要立即开锁骑走的场景。 使用微信「扫一扫」直接扫摩拜单车车身上的二维码,即可直接进入摩拜单车小程序,目前支持开锁骑行、注册、充值、找车的功能。 更多用户场景和功能,则可以在摩拜单车的 app 中实现,比如预约、筛选车型、搜索地点、领取和管理优惠券、邀请好友、分享行程、举报违停等功能。 未来,我们会在功能上做一些区分:小程序更轻、更简单;app 功能更全面,丰富。

4. 会否担心「摩拜单车」小程序抢夺 app 的自有用户?

不担心的,摩拜单车增长迅速,app 下载量已连续多周,排在 iOS 应用商店免费应用的首位、Android 版排在出行类应用的前几位。 而且,小程序和 app 在定位上有很大区别:一个使用起来轻便简单,一个功能丰富全面。 本次与微信合作开通微信扫码用车功能,不仅将给新用户带来更简单、快捷的开锁骑行体验,同时也意味着微信平台上的 8 亿多月活跃用户,都可以方便地体验摩拜单车。

5. 不更换单车二维码,微信扫一扫即可进入「摩拜单车」小程序解锁单车,这一点是如何实现的?

「摩拜单车」小程序此次上线的新能力,由微信团队和摩拜单车共同开发。双方通过技术打通,实现不换「码」即可进入小程序并使用这一功能。 此次合作,最初由摩拜单车向微信团队提出需求,双方经过深入探讨,确定了使用微信扫一扫摩拜单车的二维码,就会进入小程序这项能力,这是小程序上线的新能力。 微信团队表示,目前这一能力正在打磨优化中,后续会正式对外开放。

6. 你们如何看待微信小程序的未来?

小程序、摩拜单车、以及每一位互联网人今天做的每一个努力,可能都会让明天我们的世界变得更好。 就像摩拜单车所坚持的「科技改变骑行」理念一样,希望小程序也可以通过科技手段,给大家的生活带来更多更好的改变。MINA 奖评审如何看「摩拜单车」?

王崇旭,AppSo & 知晓程序负责人:

在摩拜单车接入微信扫一扫之后,这是首个我认为使用体验比原生 app 还要优秀的小程序。共享单车非常契合小程序的特性,在特定场景下用微信即扫即用、用完即走,而不再需要一个原生 app。可以想见,微信将来进一步开放「扫一扫」这个入口,小程序又会有更大的发挥空间。

刘剑锋,知晓程序产品负责人:

该产品切入的使用场景正是小程序所擅长的线下「即用即走」型场景,在最新一次的迭代中所更新的「扫一扫」功能使得整体服务体验更加流畅。现在新用户无须下载 app 即可马上使用共享单车的功能。

陈一斌,MindStore 创业者社区负责人:

有了摩拜单车的小程序,或许你就不必费时费力地去下载 app 了。因为在和微信合作之后,直接用微信扫一扫,就可以直接进入摩拜单车的小程序。方便、快捷,无需下载,不必等待安装。插播广告:什么是 MINA 奖?

「MINA 奖」是首个为小程序开发者设立的权威奖项,由最好的小程序生态运营商「知晓程序」主导发起。每周,MINA 奖都会评选出一款优质的小程序,并将它推荐给用户。

获奖的小程序和团队将会获得:

知晓程序独家报道机会;小程序商店首页推广;爱范儿全渠道,以及合作媒体曝光。

合作媒体:爱范儿、AppSo、MindStore、CSDN

本文由知晓程序原创出品,关注微信号 zxcx0101,在知晓程序后台回复「MINA」,进一步了解首个小程序权威奖项。

【晓头条】微信扫码可骑走多种单车 / 大学生花 10 万买公众号被收回 / 微信发布国庆大数据

这是晓头条的第 4 期

一眨眼,史上最长的国庆长假结束了!如果还无心工作,不如用这个小程序吸吸猫狗、提提神吧。

虽然你们都在放假,但是微信领域的新闻可是一个接一个:首先,用微信扫码,可以骑走市面上大部分共享单车;其次,有大学生花 10 万元购买公众号却意外被收回;最后,微信还发布了国庆 8 天的大数据。

现在,知晓程序就来为你细数上周(和上上周)微信领域的新闻。

小程序动态

1. 「滴滴出行」小程序推礼品卡,还能当红包抢

9 月 29 日,滴滴推出「滴滴出行卡」小程序。

使用这款小程序,用户可以选择不同卡面、面额的出行礼品卡,支付后通过分享,可送给微信好友。好友点击分享卡片可以领取卡片,并在滴滴行程中抵扣。

此外,用户还可以一次购买多张礼品卡,分享到群聊当红包抢。稍显可惜的是,每张卡的面额都是固定的,也就是说,用它发红包,就没有「手气最佳」一说了。


https://minapp.com/miniapp/4380/?utm_source=minappwechat&utm_medium=article (二维码自动识别)


「滴滴出行卡」小程序使用链接

2. 用微信可使用这些共享单车,还有优惠活动

「微信派」公众号 9 月 28 日发文称,通过微信扫码进入小程序,可以使用市面上包括摩拜、ofo、小蓝等多种共享单车。

此外,各家共享单车都为小程序用户,提供了不同程度的优惠活动。如果你已经习惯使用共享单车出行,不妨看看有没有适合你的优惠吧。

3. 宁夏首个旅游景区小程序上线

央广银川 10 月 2 日消息,宁夏首个旅游景区微信小程序「水洞沟手绘地图电子商城」在国庆节前正式上线。

这款小程序以水洞沟景区为基础,向游客展示景区手绘地图,并提供了不同游览线路图推荐,以及景区内部的游览线路。

此外,游客还可以直接在小程序中,购买水洞沟景区的纪念品。


「水洞沟手绘地图电子商城」小程序使用链接

公众号动态

1. 购买微信公众号出现纠纷,钱交了却没拿到号

「紫牛新闻」公众号 9 月 29 日发文称,南京两名大三学生花费 10 万元,购买了一个 8000 粉丝的微信公众号。两人在交易完成后不久,发现被公众号的真实所有人撤销运营者权限。两名大三学生因此报警,警方将此案定性为经济矛盾,不作诈骗立案,最终在调解下,两名学生的 10 万元已被退还。2. 贾跃亭提出禁令诉求,因公众号发文骚扰

——图片来自钛媒体

「钛媒体」10 月 3 日消息,贾跃亭向美国法院提出禁令诉求,因顾颖琼发表在公众号上发布的各种文章,导致自己精神抑郁,并担心自己以及家庭的安全。

「乐视生态」公众号随后发出声明称,美国洛杉矶高等法院已向顾颖琼(Yingqiong Gu)发出临时禁令,听证会将在 10 月 19 日举行。顾颖琼很快对此做出回应,称自己并不会被禁言。

3. 公众号内文广告开始测试,部分公众号已使用

上周,部分公众号内出现了文内广告,即在正文中间插入一张推广卡片。目前,所有的推广卡片都会指向小程序,同时带有「广告」字样,以及带紫色小程序图标的按钮。

受到邀请的公众号运营者,可以选择在编辑器界面右侧的「多媒体」中插入推广。

微信动态

1. 国庆都去哪了?微信说有人还去了格陵兰岛

10 月 8 日,微信发布《国庆假期微信大数据报告》,该报告以中国微信用户出国的朋友圈发布、微信支付等使用微信的行为为基础,描绘中国人 8 天国庆假期的旅游行为。

报告中,微信称东南亚是接待中国游客最多的地区之一,上海用户成为出境游的「主力军」。而假期中,最远的朋友圈签到到了格陵兰岛。

报告还称,中秋期间微信红包收发量达 63 亿;60 后打给 90 后语音通话平均时长超过 750 秒,超过其他用户的语音通话平均时长。

2. 企业微信发布 2.2 版本,加入文件协同功能

9 月 29 日,企业微信发布 2.2 版本,新增类似 Dropbox 的「文件盘」功能。你可以将文件上传至「文件盘」中,便于其他同事一起协作处理。

此外,新版本还适配了 iPad,并允许管理员在移动端批量邀请同事加入。

3. 微信启动页变脸 3 天,换成了风云四号的卫星图

9 月 25 日至 28 日,微信启动页更换 3 天,从经典的「蓝色弹珠」,更换为由我国新一代静止轨道气象卫星风云四号的卫星云图。风云四号搭载了全球首个大气垂直探测仪,并在国际上首次在单星上同时搭载了多通道扫描成像辐射计和干涉式大气垂直探测仪,以一颗卫星实现了两颗卫星的功能。微信此次更换启动图,是为了庆祝风云四号卫星在技术上取得的突破,也是多年来的「首换」。

知晓君说

国庆期间,除了「朋友圈签到」,你还用了微信哪些功能?

在留言区,说说国庆你是怎么用微信的吧!

用微信扫码使用共享单车,用完后怎么锁车?

扫码会转到共享单车小程序,还车在小程序里面就可以还

哈罗单车可以微信支付吗?

你好,是可以的。

哈罗单车的app是支持微信支付和支付宝两种支付宝方式的。

但如果在支付宝选择哈罗单车的应用,是不支持微信支付的。

如果不怎么使用支付宝,但是需要骑哈罗单车,建议下哈罗单车app。

第一次用共享单车怎么弄?

先判断一下要使用的共享单车开锁方式,市面上共享单车基本上要么和微信合作,要么和支付宝合作。第一次使用时要么用微信/或支付宝直接扫码按照提示操作,要么下载共享单车对应的app,注册登录,付押金,扫码解锁车子,就可以用了。

下班路上有位大叔问共享单车怎么用,我耐心的给他讲了,他要加我微信,还说要我做他老姑娘是什么意思?

我记得东北地区的老姑娘代表着闺女 如果你不在东北地区那我就不知道这个男人有啥企图了

这个App不是支付宝,却能一份押金打开摩拜、ofo等6种单车?

作者:逍遥小妖

嗯,共享单车大战基本是个色卡战争了。赤橙黄绿青蓝紫,七种颜色根本就不够共享单车厂商用的。

同样的战场还在用户的手机里,如果坚持每个单车下一个App,你手机里有一定有一屏很美——不过一般没人这么干,毕竟把各个平台的押金交一遍成本就不小了。

支付宝和微信都想做所有共享单车的唯一入口,但碍于利益关系,微信打不开ofo、支付宝打不开摩拜——两家分别发报告称自己市场占有率是对方两倍的公司互不兼容,可以说市场120%的单车入口都没统一了。

不过我最近发现了一个名为“全能车”的App主打的却是一份押金、一个余额、一个App,骑遍摩拜、ofo、小蓝单车、小鸣单车、骑呗单车和哈罗单车6种单车。

……这么神奇?不会是骗人的吧。

体验

下载之后的界面和你认识的共享单车App差不多,但是看到地图上这花花绿绿的圆点你就知道它确实略有本事。

我本着试试看的心态,交了299元的押金完成了注册,然后到办公室楼下去试了试。

第一辆扫了一个ofo……打开失败,说是被举报多次可能有故障。拿ofo官方客户端扫了一下,确实也是如此……嗯,这也是ofo的常见状况了。

找了第二辆之后顺利打开:

扫描之后,几乎和ofo客户端扫码的响应速度没有差别(甚至比支付宝还快一点)。屏幕上迅速出现了ofo单车的解锁码,输入后顺利解锁。

锁上小黄后,我又找到一辆小蓝单车。小蓝单车的解锁过程没有那么顺利,我试了两辆车全都卡在“解锁中”就没有反应了。

之后,我又试了试和ofo市场份额不分伯仲的摩拜:

扫描之后也是顺利开锁,和使用摩拜原生App没有区别。也就是说,至少ofo和摩拜这两家共享单车领域的巨头都被全能车“攻下来”了。

值得注意的是,所有单车的骑行价格都和原生App一样,全能车没有任何加价行为,不过好像扫红包车无法拿到红包。

原理

我的同事看到这一幕啧啧称奇,兴奋的就跑去问ofo和摩拜是怎么回事了。

但本能告诉我,要让ofo和摩拜两位冤家在这么一个没什么声势的App里合作是不可能的。所以这个全能车应该是在完全没有和共享单车平台接触的情况下,实现了这个功能。

细想一下其实原理还是很简单的:全能车可能自己在后台分别在6家共享单车平台上注册了几百个账号。

当你使用全能车App扫描任意一辆共享单车的时候,App会把二维码信息传递给全能车的后台,然后后台随机挑选一个目前正在空闲的单车账号去模拟扫描动作激活解锁操作。

最后,你面前的单车就奇迹般的解锁了。画个图是这样的:

懂了吧?要促成一个“各家单车和谐合作”的局面,其实根本就不用和各家共享单车公司合作呀!

如果说摩拜、ofo是单车共享平台的话,那么全能车可以说是“共享单车帐号共享平台”了,一个共享再共享的关系。

全能车App赚钱的方法可能也主要是押金生意:比如有1000个用户给全能车交了押金,通过一段时间的运营,全能车发现每天峰值也就是500个人在用车。那么全能车就可以只给真正的共享单车的平台交600份押金,然后按市场占比平均分一下小黄车150个、摩拜150个、小蓝100个,剩下几家再分300个。

剩下的400份押金,就是全能车自己的沉淀资金了,可以拿去理财。

问题

但是,这个模式其实一点都不美好。

首先我们大家都经历过早晚高峰地铁站口一辆车都没有的情况。在上面这个简单的模型里,全能车最后很有可能会发现1000个用户注册,有1000个都在高峰时段用车,根本没有剩余出来的押金。

再者说,免押金骑行已经逐渐成为了共享单车领域的一个趋势,如果仅以押金的金融魔术作为盈利点的话,全能车的未来也未免有些太狭窄了。

而事实上,“统一入口”的价值也已经开始被收割。如果说ofo和摩拜最大的竞争对手是彼此的话,那全能车则是在和微信、滴滴、支付宝抢生意。

除了以上三点之外,这个模式最大的问题还是来自于共享单车平台自己。那么共享单车平台自己是怎么想的呢?摩拜单车的市场负责人告诉PingWest品玩(微信号:wepingwest):

他同时表示,近期将对这一App所利用的漏洞进行封堵。而另一方面,小蓝单车的回复更直接一些:“我们自己也试过这个App了,他们打不开我们的车。”

哦,对了。全能车的退押金速度还是值得赞扬的,点击退款1秒到账。

-----------------------------------------

更多精彩请关注我们的微信公众号:PingWest品玩(wepingwest)

相关推荐

发布评论