Commit ac739d1f by 穆启卓

第一版

parents
**.DS_Store
**.sw[po]
typings
/typings
/.vscode
腾讯云微信小程序一站式解决方案客户端示例
=====================================
本示例包含:
1. 登录接口使用示例
2. 进行带会话的网络请求示例
3. WebSocket 信道服务使用示例
> 注意:所有示例均需要配合解决方案的云资源运行,具体请到[腾讯云控制台](https://console.qcloud.com/la)进行购买和配置。
## 运行示例
云资源准备好之后,修改 `config.js` 里面的服务域名:
```js
// 此处主机域名修改成腾讯云解决方案分配的域名
var host = 'yourid.qcloud.la';
```
修改之后,就可以使用微信开发者工具运行本示例。
## 源码简介
```tree
Demo
├── LICENSE
├── README.md
├── app.js
├── app.json
├── bower.json
├── config.js
├── package.json
├── pages
│   ├── chat
│   │   ├── chat.js
│   │   ├── chat.wxml
│   │   └── chat.wxss
│   └── index
│   ├── index.js
│   ├── index.wxml
│   └── index.wxss
└── vendor
└── qcloud-weapp-client-sdk/
```
`app.js` 是小程序入口文件。
`app.json` 是小程序的微信配置,其中指定了本示例的两个页面,页面分别在 `pages/index/``pages/chat/` 目录下。
`config.js` 是我们小程序自己的业务配置。
`vendor/qcloud-weapp-client-sdk`[客户端 SDK](https://github.com/tencentyun/weapp-client-sdk) 的一份拷贝。
\ No newline at end of file
This diff is collapsed. Click to expand it.
{
"pages": [
"pages/index/index",
"pages/authorization/authorization",
"pages/caption/caption"
],
"window": {
"navigationStyle": "custom",
"backgroundTextStyle": "dark",
"navigationBarBackgroundColor": "#E65845",
"navigationBarTitleText": "",
"navigationBarTextStyle": "white",
"backgroundColor": "#E65845"
}
}
\ No newline at end of file
page {
background-color: #f2f2f2;
height: 100%;
font-family: Microsoft YaHei;
}
.container {
display: flex;
display: -webkit-flex;
flex-direction: column;
justify-content: space-between;
/* height: 100%; */
height: 89.5%;
}
.page-body {
width: 100%;
flex-grow: 1;
overflow-x: hidden;
}
.page-foot {
padding: 0 0 34rpx 0;
text-align: center;
color: #1aad19;
font-size: 0;
}
.foot2 {
color: #586c94;
font-size: 26rpx;
display: inline-block;
}
.foot1 {
margin: 0 auto;
color: #888;
font-size: 24rpx;
}
.content-container {
height: 100%;
display: flex;
display: -webkit-flex;
flex-direction: column;
-webkit-flex-direction: column;
}
.content-body {
flex-grow: 1;
-webkit-flex-grow: 1;
}
.torecord {
color: #586c94;
font-size: 28rpx;
display: inline-block;
}
.descript {
margin: 0 auto;
color: #888;
font-size: 28rpx;
}
/* authorization modal */
.auth-container {
position: absolute;
height: 100%;
width: 100%;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.75);
z-index: 900;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.auth-modal-container {
margin: -64rpx 90rpx 0;
background-color: white;
border-radius: 8rpx;
position: relative;
}
.auth-modal-title {
font-size: 36rpx;
color: black;
font-weight: bold;
height: 100rpx;
line-height: 100rpx;
text-align: center;
border-bottom: 1rpx solid #eaeaea;
}
.auth-modal-content {
margin-top: 42rpx;
padding: 0 48rpx;
font-size: 34rpx;
color: #333;
text-align: left;
line-height: 1.5;
}
.auth-modal-content-tips {
margin-top: 20rpx;
padding: 0 48rpx;
font-size: 30rpx;
color: #ccc;
text-align: left;
line-height: 1.4;
}
.auth-btn-container {
width: 100%;
text-align: center;
padding-bottom: 16rpx;
margin-top: 42rpx;
}
.modal-auth-button {
font-size: 34rpx !important;
margin: 0 48rpx !important;
font-weight: bold;
}
/* .song-list{
style="padding-bottom:64rpx;"
} */
.song-info {
display: flex;
justify-content: space-between;
border-bottom: 1rpx solid rgba(224, 224, 224, 0.6);
padding: 30rpx 0 30rpx 0;
align-items: center;
overflow: visible;
}
.song-colunm {
display: flex;
flex-direction: column;
}
.song-row {
display: flex;
align-items: baseline;
line-height:1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.song-lyrics-row {
display: block !important;
margin-top: 26rpx;
line-height: 36rpx;
}
.song-lyrics {
font-size: 26rpx;
color: #a5a5a5;
}
.song-title {
display: flex;
align-items: baseline;
font-size: 30rpx;
color: #333;
/* line-height: 34rpx; */
height: 34rpx;
}
.song-listen {
display: flex;
font-size: 26rpx;
padding: 0 20rpx;
align-items: center;
}
.ic-play-btn {
width: 16rpx;
height: 20rpx;
display: flex;
/* margin-top: 10rpx; */
margin-left: 8rpx;
}
.ic-pause-btn {
width: 23rpx;
height: 23rpx;
/* display: flex; */
margin-top: 9rpx;
margin-left: 8rpx;
}
.song-lyrics {
font-size: 26rpx;
color: #a5a5a5;
margin-right: 20rpx;
}
.song-artist {
color: #ccc;
font-size: 26rpx;
margin-left: 10rpx;
}
.listen {
display: flex;
font-size: 26rpx;
color: #efa150;
line-height: 1;
}
.song-right {
display: flex;
align-items: center;
height: 100%;
/* width: 80rpx; */
position: relative;
}
.songlist-add, .songlist-del {
display: flex;
}
.song-left {
width: 600rpx;
}
.song-name{
max-width: 290rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap
}
.ic-song-chose {
height: 52rpx;
width: 52rpx;
}
.bottom-bar {
position: fixed;
left: 0;
bottom: -110rpx;
z-index: 1003;
height: 110rpx;
width: 100%;
background: #E65845;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.5s ease;
}
.bar-ani {
bottom: 0;
}
.bar-left {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 162rpx;
height: 100%;
background: #ffd666;
}
.bar-right {
/* display: flex; */
justify-content: center;
align-items: center;
flex: 1;
height: 100%;
color: #fff;
font-size: 36rpx;
}
.ic-song-chose {
height: 52rpx;
width: 52rpx;
}
.chose-song-num {
position: absolute;
left: 106rpx;
top: 22rpx;
background-color: #E65845;
color: #fff;
height: 28rpx;
width: 28rpx;
border-radius: 50%;
text-align: center;
line-height: 28rpx;
font-size: 20rpx;
}
.select-list-mask {
position: fixed;
top: 0;
left: 0;
z-index: 1001;
height: 100%;
width: 100%;
background: rgba(0, 0, 0, 0.6);
}
.select-list-box {
position: fixed;
bottom: 110rpx;
left: 0;
z-index: 1002;
width: 100%;
max-height: 100%;
background: #fff;
border-radius: 20rpx 20rpx 0 0;
box-sizing: border-box;
transition: all 0.3s linear;
transform: translate3d(0, 140%, 0);
}
.select-list-bot {
padding-bottom: 0;
}
.select-box-tip {
font-size: 26rpx;
color: #ccc;
height: 80rpx;
text-align: center;
line-height: 80rpx;
/* text-align: center; */
}
.navbar-div {
flex: 1;
position: absolute;
width: 100%;
top: 96rpx;
left: 0;
bottom: 0;
right: 0;
}
.word-area {
/* padding-left: 32rpx; */
padding-top: 0;
padding-bottom: 0;
box-sizing: border-box;
}
.word-item {
border: 0 !important;
display: flex;
padding: 40rpx 32rpx;
}
{
"name": "qcloud-weapp-client-demo",
"description": "QCloud Wechat App Demo",
"main": "app.js",
"authors": [
"Tencent Clound"
],
"license": "MIT",
"keywords": [
"qcloud",
"wechat",
"weapp",
"demo",
"sdk"
],
"homepage": "",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"qcloud-weapp-client-sdk": "*"
}
}
// components/authorization/authorization.js
Component({
/**
* 组件的属性列表
*/
properties: {
needAuth: {
type: Boolean,
value: false,
}
},
/**
* 组件的初始数据
*/
data: {
success: ''
},
/**
* 组件的方法列表
*/
methods: {
bindgetuserinfo(e) {
if (e.detail.errMsg === 'getUserInfo:ok') {
this.setData({
nickName: e.detail.userInfo.nickName,
avatarUrl: e.detail.userInfo.avatarUrl
})
wx.setStorageSync("nickName", e.detail.userInfo.nickName)
wx.setStorageSync("avatarUrl", e.detail.userInfo.avatarUrl)
// this.getUid()
this.triggerEvent('authevent', e.detail)
}
else {
// 失败的情况
this.triggerEvent('authfailevent', e.detail)
}
},
}
})
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
<!--components/authorization/authorization.wxml-->
<view hidden="{{!needAuth}}" class="auth-container">
<view class="auth-modal-container">
<view class="auth-modal-title">授权提示</view>
<view class="auth-modal-content">由于需要获取你的头像和昵称,请点击下方按钮进行授权</view>
<view class="auth-btn-container">
<button type="primary" open-type="getUserInfo" bindgetuserinfo="bindgetuserinfo" class="modal-auth-button">去授权</button>
<button open-type="contact" bindcontact="bindContact" session-from="record-userinfo-auth" size="mini" style="margin-top:8rpx;font-size:28rpx !important; padding:0 32rpx; height:74rpx; line-height:74rpx; vertical-align:middle;color:#586c94; border:0; " plain="true">联系客服</button>
</view>
</view>
</view>
/* components/authorization/authorization.wxss */
.auth-container {
position: fixed;
height: 100%;
width: 100%;
left: 0;
top: 0;
background: rgba(0, 0, 0, 0.75);
z-index: 900;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.auth-modal-container {
margin: -64rpx 90rpx 0;
background-color: white;
border-radius: 8rpx;
position: relative;
}
.auth-modal-title {
font-size: 36rpx;
color: black;
font-weight: bold;
height: 100rpx;
line-height: 100rpx;
text-align: center;
border-bottom: 1rpx solid #eaeaea;
}
.auth-modal-content {
margin-top: 42rpx;
padding: 0 48rpx;
font-size: 34rpx;
color: #333;
text-align: left;
line-height: 1.5;
}
.auth-modal-content-tips {
margin-top: 20rpx;
padding: 0 48rpx;
font-size: 30rpx;
color: #ccc;
text-align: left;
line-height: 1.4;
}
.auth-btn-container{
width: 100%;
text-align: center;
padding-bottom: 16rpx;
margin-top: 42rpx;
}
.modal-auth-button {
font-size: 34rpx !important;
margin: 0 48rpx !important;
font-weight: bold;
}
const config = require('../../config')
const app = getApp()
var page = [];
Component({
properties: {
navColor: {
type: String,
value: "#E65845",
},
titleColor:{
type: String,
value: "#ffffff",
},
showNavBar: {
type: Boolean,
value: true,
},
navTitle: {
type: String,
value: "",
},
showHome: {
type: Boolean,
value: false,
},
showBack: {
type: Boolean,
value: false,
},
showMargin: {
type: Boolean,
value: true
},
showExit: {
type: Boolean,
value: false
},
showBtnBg: {
type: Boolean,
value: true
},
paddingColor: {
type: String,
value: 'transparent'
}
},
data: {
homeTop: 18,
titleFontSize: 36,
titleFontWeight: 500,
navBarTop: 0,
titleTop: 20,
capsuleHeight: config.capsuleHeight,
statusBarHeight: config.statusBarHeight,
pixelRate: config.pixelRate,
},
ready: function () {
page = getCurrentPages();
// console.log("page", page);
let homePages = ['pages/index/index']
app.globalData.navHeight = (this.data.statusBarHeight + this.data.capsuleHeight) / this.data.pixelRate
// console.log('navHeight', app.globalData.navHeight);
this.setData({
capsuleHeight: config.capsuleHeight,
statusBarHeight: config.statusBarHeight,
pixelRate: config.pixelRate
})
if (page.length > 2) {
this.setData({
showBack: true,
showHome: true
})
} else if (page.length === 2) {
this.setData({
showBack: true,
showHome: false
})
} else {
if (homePages.includes(page[0].route)) {
this.setData({
showBack: false,
showHome: false
})
} else {
this.setData({
showBack: false,
showHome: true
})
}
}
},
methods: {
tapNavHome() {
wx.reLaunch({
url: "/pages/index/index",
})
},
tapNavBack() {
wx.navigateBack({
delta: 1
})
}
},
})
{
"component": true,
"usingComponents": {}
}
\ No newline at end of file
<!-- 手机信息块 -->
<view class="status-bar" style="background: {{navColor}}; height: {{statusBarHeight/pixelRate}}rpx;"></view>
<view wx:if="{{showNavBar}}" class="nav-bar" style="height: {{capsuleHeight/pixelRate}}rpx; top: {{statusBarHeight/pixelRate}}rpx; background: {{navColor}}; color: {{titleColor}}; font-size: {{titleFontSize}}rpx; font-weight: {{titleFontWeight}};">
<!-- 左边按钮 -->
<view wx:if="{{showExit || showHome || showBack}}" class="home-container {{showBack && showHome ? '' : 'show-back-btn'}}" style="{{showBtnBg?'':'background: transparent;'}}">
<navigator class="exit item" hover-class="none" open-type="exit" target="miniProgram" wx:if="{{showExit}}">
<image src="/images/all_people/nyhb_ic_exit.png" class="icon" mode="aspectFit"></image>
</navigator>
<view class="back item" wx:if="{{showBack}}" catch:tap="tapNavBack">
<image src="/images/all_people/nyhb_ic_back.png" class="icon" mode="aspectFit"></image>
</view>
<view class="home item" wx:if="{{showHome}}" bind:tap="tapNavHome">
<image src="/images/all_people/nyhb_ic_home.png" class="icon" mode="aspectFit"></image>
</view>
</view>
<!-- 中间文字 -->
<view class="title" style="color: {{titleColor}}">{{navTitle}}</view>
</view>
<!-- 占位块,控制navBar是否占页面空间 -->
<view style="padding-bottom: {{(statusBarHeight+capsuleHeight)/pixelRate}}rpx;background: {{paddingColor}}" hidden="{{!showMargin}}"></view>
/* navBar.wxss */
.status-bar {
position: fixed;
left: 0;
top: 0;
width: 100%;
z-index: 100000;
}
.nav-bar {
top: 0;
position: fixed;
left: 0;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 100000;
box-sizing: border-box;
/* text-shadow: 0 -1px 1px #000000; */
font-family: 'Microsoft Yahei', '微软雅黑', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 32rpx;
text-align: center;
}
.home-container {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
/* width: 64rpx; */
height: 60rpx;
margin: 0 20rpx;
/* border-radius: 14px; */
display: flex;
justify-content: center;
align-items: center;
z-index: 2000;
box-shadow: 0 0 2rpx rgba(255, 255, 255, 1);
background-color: rgba(0, 0, 0, 0.1);
border-radius: 30rpx;
box-sizing: border-box;
padding: 0;
}
.item{
/* padding: 0 20rpx; */
width: 75rpx;
/* margin-top: 8rpx; */
position: relative;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.back:after{
position: absolute;
right: 0rpx;
top: 50%;
transform: translateY(-50%);
display: block;
width: 2rpx;
height: 36rpx;
background-color: rgba(255, 255, 255, 0.2);
content: " ";
z-index: 99;
}
.icon {
width: 32rpx;
height: 32rpx;
}
.status-bar {
transition: background 0.5s;
}
.nav-bar {
transition: background 0.5s;
}
.show-back-btn {
width: 65rpx;
height: 65rpx;
border-radius: 50%;
}
.show-back-btn .back:after{
display: none;
}
.show-back-btn .item {
width: 65rpx;
}
// const host = 'miniapp-testapi.wxatech.com';
const host = 'miniapp-api.wxatech.com';
const content = 'game-bsdk';
const config = {
// 下面的地址配合云端 Demo 工作
service: {
host,
hostUrl: `https://${host}/${content}/api`,
// 登录地址,用于建立会话
loginUrl: `https://${host}/${content}/api/login`,
// 测试的请求地址,用于测试会话
requestUrl: `https://${host}/${content}/api/user`,
// 测试的信道服务地址
tunnelUrl: `https://${host}/${content}/api/tunnel`,
// 测试的上传地址
uploadUrl: `https://${host}/${content}/api/upload`,
avatarUrl: `http://${host}/${content}/static/images/avatar/0.png`,
originalId: 'gh_d1883989040a',
reportSourceUrl: 'https://kf.bleege.com/api/mina_source/report',
version: '1.0.0',
},
pixelRate:0.5, //px与rpx换算关系
platform:'ios', //操作平台 用于适配胶囊高度
capsuleHeight:44, //胶囊高度
statusBarHeight:20, //手机顶部状态栏高度
titleHeight:136, //整个导航头高度
systemHeight: 0, //手机屏幕高度
isAllScreen:false, //是否是全面屏手机
isHighHead:false, //是否是刘海屏手机
sampleRate: 44100,
encodeBitRate: 64000
};
module.exports = config;
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs"
}
}
\ No newline at end of file
{
"name": "qcloud-weapp-client-demo",
"version": "1.0.0",
"description": "腾讯云微信小程序客户端 DEMO",
"main": "app.js",
"repository": {
"type": "git",
"url": "https://github.com/tencentyun/weapp-client-demo.git"
},
"keywords": [],
"author": "CFETeam",
"license": "MIT"
}
let getAuthLoading = false;
Page({
data: {
userAuth: false,
stepAuth: false
},
onLoad(options) {
let data = {};
if (options.userAuth === 'true') {
data.userAuth = true;
} else if (options.userAuth === 'false') {
data.userAuth = false;
}
if (options.stepAuth === 'true') {
data.stepAuth = true;
} else if (options.stepAuth === 'false') {
data.stepAuth = false;
}
console.log(data)
this.setData(data);
},
getUserAuth(event) {
if (getAuthLoading) return;
getAuthLoading = true;
if (event.detail.errMsg === 'getUserInfo:ok') {
console.log('getUserAuth:ok', event.detail)
this.setData({
userAuth: true
});
setTimeout(() => {
getAuthLoading = false;
}, 100);
} else {
console.log('getUserAuth:fail', event)
}
},
getStepAuth() {
if (getAuthLoading) return;
getAuthLoading = true;
wx.authorize({
scope: 'scope.werun',
success:() => {
this.userAuthorizedStep();
},
fail:() => {
// 调起用户授权窗口失败,因为用户曾经拒绝过,而且在这里不能直接去到openSetting页,需要通过一个弹窗中转过去
wx.showModal({
content: '授权失败,请在接下来的页面中同意授权',
showCancel: false,
success:() => {
wx.openSetting({
success:(res) => {
if (res.authSetting['scope.werun']) {
this.userAuthorizedStep();
}
}
});
}
});
},
complete: () => {
setTimeout(() => {
getAuthLoading = false;
}, 100);
}
});
},
userAuthorizedStep() {
this.setData({
stepAuth: true
});
wx.getWeRunData({
success(res) {
console.log('getWeRunData', res)
}
});
}
})
\ No newline at end of file
{
"usingComponents": {
"navbar": "/components/navBar/navBar"
}
}
\ No newline at end of file
.page-container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
.app-logo {
width: 250rpx;
height: 250rpx;
border-radius: 50%;
margin-top: 60rpx;
}
.app-name {
font-size: 28rpx;
line-height: 1;
color: #000;
font-weight: bold;
margin-top: 30rpx;
}
.title {
width: 710rpx;
font-size: 28rpx;
line-height: 1;
color: #000;
font-weight: bold;
margin-top: 80rpx;
}
.explain {
width: 710rpx;
font-size: 26rpx;
line-height: 1.2;
color: #000;
margin-top: 20rpx;
}
.authorization-container {
width: 710rpx;
margin-top: 40rpx;
display: flex;
align-items: flex-start;
.authorized-icon {
width: 30rpx;
height: 30rpx;
flex-shrink: 0;
background: red;
}
.authorization-title-explain {
margin-left: 10rpx;
.authorization-title {
font-size: 28rpx;
line-height: 1.2;
color: #000;
font-weight: bold;
}
.authorization-explain {
font-size: 26rpx;
line-height: 1.2;
color: #000;
margin-top: 20rpx;
}
}
}
.authorization-btn {
width: 650rpx;
height: 80rpx;
border-radius: 80rpx;
background: blue;
font-size: 30rpx;
line-height: 80rpx;
color: #fff;
text-align: center;
margin-top: 120rpx;
}
.clear-btn {
border: none;
}
}
\ No newline at end of file
<navbar showHome="{{false}}" navTitle="{{navTitle}}" navColor="{{navColor}}"></navbar>
<view class="page-container">
<view class="app-logo"></view>
<view class="app-name">运动打卡</view>
<view class="title">请允许运动打卡获取以下权限</view>
<view class="explain">以下信息仅用于您登录运动打卡,我们将严格保密绝不外泄,更不会对您发送垃圾信息造成骚扰</view>
<view class="authorization-container">
<image class="authorized-icon" src="/images/authorization/auth_{{userAuth?'true':'false'}}.png"></image>
<view class="authorization-title-explain">
<view class="authorization-title">获取您的公开信息(昵称、头像等)</view>
<view class="authorization-explain">需获取您的公开信息,方便您使用运动打卡全部功能</view>
</view>
</view>
<view class="authorization-container">
<image class="authorized-icon" src="/images/authorization/auth_{{stepAuth?'true':'false'}}.png"></image>
<view class="authorization-title-explain">
<view class="authorization-title">获取您的微信运动步数</view>
<view class="authorization-explain">需获取您的微信运动步数,记录您每日运动步数</view>
</view>
</view>
<button class="authorization-btn clear-btn" plain open-type="getUserInfo" bindgetuserinfo="getUserAuth" wx:if="{{!userAuth}}">第一步,授权公开信息</button>
<view class="authorization-btn" bindtap="getStepAuth" wx:elif="{{!stepAuth}}">第二步,授权微信运动步数</view>
</view>
\ No newline at end of file
.page-container {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.page-container .app-logo {
width: 250rpx;
height: 250rpx;
border-radius: 50%;
margin-top: 60rpx;
}
.page-container .app-name {
font-size: 28rpx;
line-height: 1;
color: #000;
font-weight: bold;
margin-top: 30rpx;
}
.page-container .title {
width: 710rpx;
font-size: 28rpx;
line-height: 1;
color: #000;
font-weight: bold;
margin-top: 80rpx;
}
.page-container .explain {
width: 710rpx;
font-size: 26rpx;
line-height: 1.2;
color: #000;
margin-top: 20rpx;
}
.page-container .authorization-container {
width: 710rpx;
margin-top: 40rpx;
display: flex;
align-items: flex-start;
}
.page-container .authorization-container .authorized-icon {
width: 30rpx;
height: 30rpx;
flex-shrink: 0;
background: red;
}
.page-container .authorization-container .authorization-title-explain {
margin-left: 10rpx;
}
.page-container .authorization-container .authorization-title-explain .authorization-title {
font-size: 28rpx;
line-height: 1.2;
color: #000;
font-weight: bold;
}
.page-container .authorization-container .authorization-title-explain .authorization-explain {
font-size: 26rpx;
line-height: 1.2;
color: #000;
margin-top: 20rpx;
}
.page-container .authorization-btn {
width: 650rpx;
height: 80rpx;
border-radius: 80rpx;
background: blue;
font-size: 30rpx;
line-height: 80rpx;
color: #fff;
text-align: center;
margin-top: 120rpx;
}
.page-container .clear-btn {
border: none;
}
// pages/caption/caption.js
Page({
/**
* 页面的初始数据
*/
data: {
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {
},
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {
}
})
\ No newline at end of file
{
"navigationBarTextStyle":"black",
"usingComponents": {
"navbar": "/components/navBar/navBar"
}
}
\ No newline at end of file
<navbar showBack="{{true}}" navTitle="{{'说明'}}" titleColor="{{'#000'}}" navColor="{{'#fff'}}"></navbar>
<view class="page-container">
<view>说明</view>
<view>本小程序仅用于记录您的步数明细数据,获取步数数据成功后,返回报名页面,即打卡成功</view>
<view>1、运动步数为零怎么办?</view>
<view>请检查是否关注微信运动公众号,如已关注请继续行走;如未关注,则进入微信【添加好友】-搜索【微信运动】找到公众号并关注,进入公众号后你会收到一条提示是否要让微信访问我们的活动记录点击“好”即可。</view>
<view>2、如何参与步数打卡?</view>
<view>关注公众号【XXXX】,点击菜单栏【打卡入口】,成功报名活动后,活动结束24点前登录小程序同步最新步数</view>
</view>
\ No newline at end of file
/* pages/caption/caption.wxss */
\ No newline at end of file
var config = require('../../config');
let app = getApp();
Page({
data: {
stepAuth: false,
getStepLoading: false,
// commitStepPopupShow: false, // 同步步数成功弹窗
activityExplainShow: false, // 活动说明弹窗
currentSteps: 0,
targetSteps: 0,
stepRecordList: [],
},
onLoad() {
wx.getSetting({
success:(res) => {
// 微信步数是否授权
if (res.authSetting['scope.werun']) {
this.setData({
stepAuth: true
});
this.getWeRunData();
}
}
});
},
// 找微信获取步数加密数据
getStep() {
if (wx.getStorageSync('getStepLoading') && Date.now() - wx.getStorageSync('getStepLoading') < 60000) {
wx.showToast({
title: '已是最新步数,建议前往微信运动获取最新步数再返回同步',
icon: 'none',
duration: 2000
});
return;
}
if (this.data.getStepLoading) return;
this.data.getStepLoading = true;
// 查看微信运动步数授权信息
if (this.data.stepAuth) {
this.getWeRunData();
} else {
// 调起授权窗口
wx.authorize({
scope: 'scope.werun',
// 授权成功
success:() => {
this.setData({
stepAuth: true
});
this.getWeRunData();
},
// 授权失败,因为用户拒绝或者用户曾经拒绝过,而且在这里不能直接去到openSetting页,需要通过一个弹窗中转过去
fail:(err) => {
console.log('getStep err',err)
this.authorizationFail();
}
});
}
},
// 拿到微信运动步数加密数据
getWeRunData() {
wx.getWeRunData({
success:(res) => {
console.log('getWeRunData', res)
// this.changeShowPopup();
this.getStepNum(res.encryptedData, res.iv);
},
complete:() => {
setTimeout(() => {
this.data.getStepLoading = false;
}, 100);
}
});
},
// 找后端获取解密步数信息
getStepNum(encryptedData, iv) {
wx.request({
url: config.service.hostUrl + "/v1/get_step_num",
data: {
encrypted_data: encryptedData,
iv: iv
},
header: {
'Authorization': app.globalData.token
},
method: 'POST',
success:(res) => {
const resData = res.data.data;
// 数据处理,结合两个数组判断是否显示打卡角标和状态
const joinDate = [];
resData.records_info.forEach(item => {
joinDate.push(item.period);
});
resData.step_data.forEach(item => {
if (joinDate.indexOf(item.period) > -1) {
if (resData.records_info[joinDate.indexOf(item.period)].daka_done) {
item.status = 1; // 打卡成功
} else {
item.status = 2; // 打卡失败
}
} else {
item.status = 0; // 未参与打卡
}
});
console.log(resData.step_data)
this.setData({
currentSteps: (resData.step_data && resData.step_data[0]) ? resData.step_data[0].step : 0,
targetSteps: resData.total_step,
stepRecordList: resData.step_data
});
wx.setStorageSync('getStepLoading', Date.now());
wx.showModal({
content: '获取微信运动步数成功',
showCancel: false
});
},
fail:(err) => {
console.log('fail', err);
}
});
},
// 授权失败,去到openSetting页
authorizationFail() {
wx.showModal({
content: '如需完成步数打卡,请点击确定允许微信授权',
success:(res) => {
if (res.confirm) {
wx.openSetting({
success:(res) => {
if (res.authSetting['scope.werun']) {
this.setData({
stepAuth: true
});
this.getWeRunData();
} else {
// this.authorizationFail();
}
}
});
}
},
complete:() => {
setTimeout(() => {
this.data.getStepLoading = false;
}, 100);
}
});
},
// 活动说明弹窗
changeActivityExplainShow() {
this.setData({
activityExplainShow: !this.data.activityExplainShow
});
},
// // 同步步数成功弹窗
// changeShowPopup() {
// this.setData({
// commitStepPopupShow: !this.data.commitStepPopupShow
// });
// },
// goAuthorizationPage() {
// console.log(`/pages/authorization/authorization?userAuth=${this.data.userAuth}&stepAuth=${this.data.stepAuth}`)
// wx.navigateTo({
// url: `/pages/authorization/authorization?userAuth=${this.data.userAuth}&stepAuth=${this.data.stepAuth}`
// });
// },
// goCaptionPage() {
// wx.navigateTo({
// url: '/pages/caption/caption'
// });
// },
})
\ No newline at end of file
{
"navigationBarTextStyle":"black",
"backgroundColor": "#37d4a9",
"backgroundColorTop": "#37d4a9",
"backgroundColorBottom": "#24d6dc",
"usingComponents": {
"navbar": "/components/navBar/navBar"
}
}
\ No newline at end of file
page {
background: #24d6dc;
}
.page-container {
width: 100%;
.today-step-container {
width: 100%;
height: 684rpx;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.today-step-container-bg {
width: 100%;
height: 684rpx;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
.continuity-days {
height: 48rpx;
border-radius: 48rpx;
background: #fdb62d;
padding: 0 28rpx;
font-size: 26rpx;
line-height: 48rpx;
color: #fff;
position: absolute;
left: 20rpx;
top: 24rpx;
}
.explain {
width: 93rpx;
height: 32rpx;
position: absolute;
top: 30rpx;
right: 24rpx;
}
.today-step-info {
width: 430rpx;
height: 430rpx;
margin-top: 100rpx;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
.today-step-info-bg {
width: 430rpx;
height: 430rpx;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
.info-title {
font-size: 30rpx;
line-height: 1;
color: #fff;
margin-top: 150rpx;
}
.today-step-num {
font-size: 72rpx;
line-height: 1;
color: #fff;
margin-top: 2rpx;
}
}
.get-info-btn {
width: 296rpx;
height: 50rpx;
border-radius: 50rpx;
background: #19afe6;
font-size: 30rpx;
line-height: 50rpx;
color: #fff;
text-align: center;
margin-top: -40rpx;
z-index: 2;
}
.step-progress-container {
height: 34rpx;
background: #cef0f0;
border-radius: 6rpx;
overflow: hidden;
margin-top: 12rpx;
display: flex;
align-items: center;
.today-status {
height: 34rpx;
font-size: 22rpx;
line-height: 34rpx;
color: #fff;
background: #948cc9;
border-radius: 6rpx;
padding: 0 8rpx;
}
.today-progress {
font-size: 26rpx;
line-height: 1;
color: #698da7;
padding: 0 8rpx;
&>text {
font-size: 22rpx;
}
}
}
.other-step-title {
font-size: 34rpx;
line-height: 1;
color: #fff;
position: absolute;
left: 30rpx;
bottom: 28rpx;
}
}
.other-step-container {
width: 100%;
padding: 0 30rpx 36rpx;
background: #24d6dc;
box-sizing: border-box;
.other-step-section {
width: 100%;
height: 120rpx;
background: #fff;
border-radius: 10rpx;
box-shadow: 0 4rpx 8rpx 4rpx #05b8c8;
margin-top: 36rpx;
padding: 0 48rpx;
box-sizing: border-box;
display: flex;
align-items: center;
position: relative;
&:first-child {
margin-top: 0;
}
.status-mark {
width: 74rpx;
height: 73rpx;
display: block;
position: absolute;
left: -3rpx;
top: -2rpx;
}
.date-container {
flex-shrink: 0;
.date-item {
font-size: 30rpx;
line-height: 1.36;
color: #357aa6;
}
}
.other-step-num {
flex: 1;
font-size: 36rpx;
line-height: 1;
color: #477595;
font-weight: bold;
text-align: right;
}
}
}
}
.activity-explain-popup-bg {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, .5);
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
.activity-explain-popup-container {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
padding: 50rpx 0;
box-sizing: border-box;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
.popup-title {
font-size: 32rpx;
line-height: 1;
color: #333;
font-weight: bold;
}
.popup-explain {
width: 520rpx;
font-size: 26rpx;
line-height: 44rpx;
color: #888;
margin-top: 22rpx;
}
.hr {
width: 400rpx;
height: 1px;
background: #E5E5E5;
margin-top: 40rpx;
}
.content-title {
width: 480rpx;
font-size: 28rpx;
line-height: 1;
color: #333;
margin: 50rpx 0 12rpx;
position: relative;
&.title1:after {
content: '';
width: 12rpx;
height: 12rpx;
background: #25D6DA;
border-radius: 50%;
position: absolute;
left: -32rpx;
top: 50%;
transform: translateY(-50%);
}
&.title2:after {
content: '';
width: 12rpx;
height: 12rpx;
background: #FFA0D1;
border-radius: 50%;
position: absolute;
left: -32rpx;
top: 50%;
transform: translateY(-50%);
}
}
.content-text {
width: 480rpx;
font-size: 26rpx;
line-height: 44rpx;
color: #888;
}
.activity-explain-close-btn {
width: 58rpx;
height: 58rpx;
display: block;
position: absolute;
bottom: -108rpx;
left: 50%;
transform: translateX(-50%);
overflow: visible;
}
}
}
// .commit-step-popup-bg {
// position: fixed;
// left: 0;
// top: 0;
// right: 0;
// bottom: 0;
// background: rgba(0, 0, 0, .8);
// z-index: 999;
// display: flex;
// align-items: center;
// justify-content: center;
// .commit-step-popup {
// width: 560rpx;
// background: #fff;
// border-radius: 10rpx;
// overflow: hidden;
// .popup-content {
// width: 100%;
// height: 230rpx;
// display: flex;
// align-items: center;
// justify-content: center;
// padding: 0 70rpx;
// box-sizing: border-box;
// font-size: 32rpx;
// line-height: 50rpx;
// color: #585858;
// text-align: center;
// }
// .popup-btn-container {
// width: 100%;
// height: 108rpx;
// box-sizing: border-box;
// display: flex;
// border-top: 2rpx solid #e5e5e5;
// .cancel-btn {
// flex: 1;
// height: 108rpx;
// font-size: 32rpx;
// line-height: 108rpx;
// color: #999;
// text-align: center;
// }
// .confirm-btn {
// flex: 1;
// height: 108rpx;
// font-size: 32rpx;
// line-height: 108rpx;
// color: #25d6da;
// text-align: center;
// border-left: 2rpx solid #e5e5e5;
// }
// }
// }
// }
\ No newline at end of file
<navbar showExit="{{true}}" navTitle="{{'步数打卡'}}" titleColor="{{'#000'}}" navColor="{{'#fff'}}" paddingColor="{{'#37d4a9'}}" showBtnBg="{{false}}"></navbar>
<view class="page-container">
<view class="today-step-container">
<image class="today-step-container-bg" src="/images/index/sy_beijing1.png"></image>
<!-- <view class="continuity-days">连续打卡5天</view> -->
<image class="explain" src="/images/index/sy_shuoming.png" bindtap="changeActivityExplainShow"></image>
<view class="today-step-info" bindtap="getStep">
<image class="today-step-info-bg" src="/images/index/sy_ybp.png"></image>
<block wx:if="{{stepAuth}}">
<view class="info-title">今日步数</view>
<view class="today-step-num">{{currentSteps}}</view>
</block>
<view class="info-title" style="margin-top: 200rpx" wx:else>点此获取步数数据</view>
</view>
<view class="get-info-btn" bindtap="getStep">提交步数打卡</view>
<!-- <view class="get-info-btn" bindtap="goAuthorizationPage">点击完成步数打卡</view> -->
<view class="step-progress-container" wx:if="{{stepAuth && targetSteps}}">
<view class="today-status">进行中</view>
<view class="today-progress">{{currentSteps}}/{{targetSteps}}<text>步</text></view>
</view>
<view class="other-step-title" wx:if="{{stepRecordList.length}}">步数明细</view>
</view>
<view class="other-step-container">
<view class="other-step-section" wx:for="{{stepRecordList}}" wx:key="{{index}}" wx:if="{{index!==0}}">
<image class="status-mark" src="/images/index/sy_{{item.status===1?'wancheng':'shibai'}}.png" wx:if="{{item.status}}"></image>
<view class="date-container">
<view class="date-item">{{item.date}}总步数</view>
<view class="date-item">{{item.date_time}}</view>
</view>
<view class="other-step-num">+{{item.step}}</view>
</view>
</view>
</view>
<!-- 活动说明弹窗 -->
<view class="activity-explain-popup-bg" wx:if="{{activityExplainShow}}">
<view class="activity-explain-popup-container">
<view class="popup-title">活动说明</view>
<view class="popup-explain">本小程序仅用于记录您的步数明细数据,获取步数数据成功后,返回报名页面,即打卡成功</view>
<view class="hr"></view>
<view class="content-title title1">运动步数为0怎么办?</view>
<view class="content-text">请检查是否关注微信运动公众号,如已关注请继续行走;</view>
<view class="content-text">如未关注,则进入微信【添加好友】-搜索【微信运动】找到公众号并关注,进入公众号后你会收到一条提示是否要让微信访问我们的活动记录点击“好”即可。</view>
<view class="content-title title2">如何参与步数打卡?</view>
<view class="content-text">关注公众号【XXXX】,点击菜单栏【打卡入口】,成功报名活动后,活动结束24点前登录小程序同步最新步数。</view>
<image class="activity-explain-close-btn" src="/images/index/pop_ic_close.png" bindtap="changeActivityExplainShow"></image>
</view>
</view>
<!-- 同步步数成功弹窗 -->
<!-- <view class="commit-step-popup-bg" wx:if="{{commitStepPopupShow}}">
<view class="commit-step-popup">
<view class="popup-content">步数数据同步成功,是否需要退出小程序?</view>
<view class="popup-btn-container">
<view class="cancel-btn" bindtap="changeShowPopup">取消</view>
<navigator class="confirm-btn" hover-class="none" open-type="exit" target="miniProgram">确定</navigator>
</view>
</view>
</view> -->
\ No newline at end of file
page {
background: #24d6dc;
}
.page-container {
width: 100%;
}
.page-container .today-step-container {
width: 100%;
height: 684rpx;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.page-container .today-step-container .today-step-container-bg {
width: 100%;
height: 684rpx;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
.page-container .today-step-container .continuity-days {
height: 48rpx;
border-radius: 48rpx;
background: #fdb62d;
padding: 0 28rpx;
font-size: 26rpx;
line-height: 48rpx;
color: #fff;
position: absolute;
left: 20rpx;
top: 24rpx;
}
.page-container .today-step-container .explain {
width: 93rpx;
height: 32rpx;
position: absolute;
top: 30rpx;
right: 24rpx;
}
.page-container .today-step-container .today-step-info {
width: 430rpx;
height: 430rpx;
margin-top: 100rpx;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.page-container .today-step-container .today-step-info .today-step-info-bg {
width: 430rpx;
height: 430rpx;
position: absolute;
left: 0;
top: 0;
z-index: -1;
}
.page-container .today-step-container .today-step-info .info-title {
font-size: 30rpx;
line-height: 1;
color: #fff;
margin-top: 150rpx;
}
.page-container .today-step-container .today-step-info .today-step-num {
font-size: 72rpx;
line-height: 1;
color: #fff;
margin-top: 2rpx;
}
.page-container .today-step-container .get-info-btn {
width: 296rpx;
height: 50rpx;
border-radius: 50rpx;
background: #19afe6;
font-size: 30rpx;
line-height: 50rpx;
color: #fff;
text-align: center;
margin-top: -40rpx;
z-index: 2;
}
.page-container .today-step-container .step-progress-container {
height: 34rpx;
background: #cef0f0;
border-radius: 6rpx;
overflow: hidden;
margin-top: 12rpx;
display: flex;
align-items: center;
}
.page-container .today-step-container .step-progress-container .today-status {
height: 34rpx;
font-size: 22rpx;
line-height: 34rpx;
color: #fff;
background: #948cc9;
border-radius: 6rpx;
padding: 0 8rpx;
}
.page-container .today-step-container .step-progress-container .today-progress {
font-size: 26rpx;
line-height: 1;
color: #698da7;
padding: 0 8rpx;
}
.page-container .today-step-container .step-progress-container .today-progress > text {
font-size: 22rpx;
}
.page-container .today-step-container .other-step-title {
font-size: 34rpx;
line-height: 1;
color: #fff;
position: absolute;
left: 30rpx;
bottom: 28rpx;
}
.page-container .other-step-container {
width: 100%;
padding: 0 30rpx 36rpx;
background: #24d6dc;
box-sizing: border-box;
}
.page-container .other-step-container .other-step-section {
width: 100%;
height: 120rpx;
background: #fff;
border-radius: 10rpx;
box-shadow: 0 4rpx 8rpx 4rpx #05b8c8;
margin-top: 36rpx;
padding: 0 48rpx;
box-sizing: border-box;
display: flex;
align-items: center;
position: relative;
}
.page-container .other-step-container .other-step-section:first-child {
margin-top: 0;
}
.page-container .other-step-container .other-step-section .status-mark {
width: 74rpx;
height: 73rpx;
display: block;
position: absolute;
left: -3rpx;
top: -2rpx;
}
.page-container .other-step-container .other-step-section .date-container {
flex-shrink: 0;
}
.page-container .other-step-container .other-step-section .date-container .date-item {
font-size: 30rpx;
line-height: 1.36;
color: #357aa6;
}
.page-container .other-step-container .other-step-section .other-step-num {
flex: 1;
font-size: 36rpx;
line-height: 1;
color: #477595;
font-weight: bold;
text-align: right;
}
.activity-explain-popup-bg {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
align-items: center;
justify-content: center;
}
.activity-explain-popup-bg .activity-explain-popup-container {
width: 600rpx;
background: #fff;
border-radius: 20rpx;
padding: 50rpx 0;
box-sizing: border-box;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
}
.activity-explain-popup-bg .activity-explain-popup-container .popup-title {
font-size: 32rpx;
line-height: 1;
color: #333;
font-weight: bold;
}
.activity-explain-popup-bg .activity-explain-popup-container .popup-explain {
width: 520rpx;
font-size: 26rpx;
line-height: 44rpx;
color: #888;
margin-top: 22rpx;
}
.activity-explain-popup-bg .activity-explain-popup-container .hr {
width: 400rpx;
height: 1px;
background: #E5E5E5;
margin-top: 40rpx;
}
.activity-explain-popup-bg .activity-explain-popup-container .content-title {
width: 480rpx;
font-size: 28rpx;
line-height: 1;
color: #333;
margin: 50rpx 0 12rpx;
position: relative;
}
.activity-explain-popup-bg .activity-explain-popup-container .content-title.title1:after {
content: '';
width: 12rpx;
height: 12rpx;
background: #25D6DA;
border-radius: 50%;
position: absolute;
left: -32rpx;
top: 50%;
transform: translateY(-50%);
}
.activity-explain-popup-bg .activity-explain-popup-container .content-title.title2:after {
content: '';
width: 12rpx;
height: 12rpx;
background: #FFA0D1;
border-radius: 50%;
position: absolute;
left: -32rpx;
top: 50%;
transform: translateY(-50%);
}
.activity-explain-popup-bg .activity-explain-popup-container .content-text {
width: 480rpx;
font-size: 26rpx;
line-height: 44rpx;
color: #888;
}
.activity-explain-popup-bg .activity-explain-popup-container .activity-explain-close-btn {
width: 58rpx;
height: 58rpx;
display: block;
position: absolute;
bottom: -108rpx;
left: 50%;
transform: translateX(-50%);
overflow: visible;
}
{
"description": "项目配置文件",
"packOptions": {
"ignore": []
},
"setting": {
"urlCheck": false,
"es6": true,
"postcss": true,
"minified": true,
"newFeature": true,
"autoAudits": false
},
"compileType": "miniprogram",
"libVersion": "2.6.2",
"appid": "wx035eac0d4d63b3e4",
"projectname": "walk_daka",
"debugOptions": {
"hidedInDevtools": []
},
"scripts": {},
"condition": {
"search": {
"current": -1,
"list": [
{
"id": 0,
"name": "搜索直达",
"widgetData": "",
"service": {
"box_type": 10000,
"wording": "Demo",
"query_item": [
{
"query": "00700",
"box_qi": "{\"type\":10000,\"slot_list\":[{\"key\":\"stock_code\",\"value\":\"00700\"},{\"key\":\"stock_market\",\"value\":\"hk\"}]}"
},
{
"query": "腾讯股价",
"box_qi": "{\"type\":10000,\"slot_list\":[{\"key\":\"stock_code\",\"value\":\"00700\"},{\"key\":\"stock_market\",\"value\":\"hk\"}]}"
},
{
"query": "Goog",
"box_qi": "{\"type\":10000,\"slot_list\":[{\"key\":\"stock_code\",\"value\":\"GOOG\"},{\"key\":\"stock_market\",\"value\":\"us.OQ\"}]}"
}
]
},
"query": "Goog",
"customQuery": "说的分手",
"boxQI": "{\"type\":10000,\"slot_list\":[{\"key\":\"stock_code\",\"value\":\"GOOG\"},{\"key\":\"stock_market\",\"value\":\"us.OQ\"}]}",
"debugUrl": ""
}
]
},
"conversation": {
"current": -1,
"list": []
},
"plugin": {
"current": -1,
"list": []
},
"game": {
"list": []
},
"miniprogram": {
"current": 0,
"list": [
{
"id": -1,
"name": "authorization",
"pathName": "pages/authorization/authorization",
"query": "",
"scene": null
}
]
}
}
}
\ No newline at end of file
function formatTime(date) {
var year = date.getFullYear()
var month = date.getMonth() + 1
var day = date.getDate()
var hour = date.getHours()
var minute = date.getMinutes()
var second = date.getSeconds()
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}
function formatNumber(n) {
n = n.toString()
return n[1] ? n : '0' + n
}
module.exports = {
formatTime: formatTime
}
{
"name": "qcloud-weapp-client-sdk",
"description": "QCloud 微信小程序客户端 SDK",
"main": "index.js",
"authors": [
"Tencent Cloud"
],
"license": "MIT",
"keywords": [
"qcloud",
"weapp",
"wechat",
"sdk",
"client",
"auth",
"websocket"
],
"homepage": "https://github.com/tencentyun/weapp-client-sdk",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"typings.json",
"jsconfig.json",
"package.json",
".npmignore",
".travis.yml",
".gitignore"
],
"version": "0.8.2",
"_release": "0.8.2",
"_resolution": {
"type": "version",
"tag": "v0.8.2",
"commit": "0944545afe0b6bc4aec8c39e8437dbe95a07a8d9"
},
"_source": "https://github.com/tencentyun/weapp-client-sdk.git",
"_target": "*",
"_originalSource": "qcloud-weapp-client-sdk"
}
\ No newline at end of file
LICENSE - "MIT License"
Copyright (c) 2016 by Tencent Cloud
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
# 微信小程序客户端腾讯云增强 SDK
[![Build Status](https://travis-ci.org/tencentyun/wafer-client-sdk.svg?branch=master)](https://travis-ci.org/tencentyun/wafer-client-sdk)
[![Coverage Status](https://coveralls.io/repos/github/tencentyun/wafer-client-sdk/badge.svg?branch=master)](https://coveralls.io/github/tencentyun/wafer-client-sdk?branch=master)
[![License](https://img.shields.io/github/license/tencentyun/wafer-client-sdk.svg)](LICENSE)
本 项目是 [Wafer](https://github.com/tencentyun/wafer-solution) 的组成部分,为小程序客户端开发提供 SDK 支持会话服务和信道服务。
## SDK 获取与安装
解决方案[客户端 Demo](https://github.com/tencentyun/wafer-client-demo) 已经集成并使用最新版的 SDK,需要快速了解的可以从 Demo 开始。
如果需要单独开始,本 SDK 已经发布为 bower 模块,可以直接安装到小程序目录中。
```sh
npm install -g bower
bower install qcloud-weapp-client-sdk
```
安装之后,就可以使用 `require` 引用 SDK 模块:
```js
var qcloud = require('./bower_components/qcloud-weapp-client-sdk/index.js');
```
## 会话服务
[会话服务](https://github.com/tencentyun/wafer-solution/wiki/%E4%BC%9A%E8%AF%9D%E6%9C%8D%E5%8A%A1)让小程序拥有会话管理能力。
### 登录
登录可以在小程序和服务器之间建立会话,服务器由此可以获取到用户的标识和信息。
```js
var qcloud = require('./bower_components/qcloud-weapp-client-sdk/index.js');
// 设置登录地址
qcloud.setLoginUrl('https://199447.qcloud.la/login');
qcloud.login({
success: function (userInfo) {
console.log('登录成功', userInfo);
},
fail: function (err) {
console.log('登录失败', err);
}
});
```
本 SDK 需要配合云端 SDK 才能提供完整会话服务。通过 [setLoginUrl](#setLoginUrl) 设置登录地址,云服务器在该地址上使用云端 SDK 处理登录请求。
> `setLoginUrl` 方法设置登录地址之后会一直有效,因此你可以在微信小程序启动时设置。
登录成功后,可以获取到当前微信用户的基本信息。
### 请求
如果希望小程序的网络请求包含会话,登录之后使用 [request](#request) 方法进行网络请求即可。
```js
qcloud.request({
url: 'http://199447.qcloud.la/user',
success: function (response) {
console.log(response);
},
fail: function (err) {
console.log(err);
}
});
```
如果调用 `request` 之前还没有登录,则请求不会带有会话。`request` 方法也支持 `login` 参数支持在请求之前自动登录。
```js
// 使用 login 参数之前,需要设置登录地址
qcloud.setLoginUrl('https://199447.qcloud.la/login');
qcloud.request({
login: true,
url: 'http://199447.qcloud.la/user',
success: function (response) {
console.log(response);
},
fail: function (err) {
console.log(err);
}
});
```
关于会话服务详细技术说明,请参考 [Wiki](https://github.com/tencentyun/wafer-solution/wiki/%E4%BC%9A%E8%AF%9D%E6%9C%8D%E5%8A%A1)
## 信道服务
[信道服务](https://github.com/tencentyun/wafer-solution/wiki/%E4%BF%A1%E9%81%93%E6%9C%8D%E5%8A%A1)小程序支持利用腾讯云的信道资源使用 WebSocket 服务。
```js
// 创建信道,需要给定后台服务地址
var tunnel = this.tunnel = new qcloud.Tunnel('https://199447.qcloud.la/tunnel');
// 监听信道内置消息,包括 connect/close/reconnecting/reconnect/error
tunnel.on('connect', () => console.log('WebSocket 信道已连接'));
tunnel.on('close', () => console.log('WebSocket 信道已断开'));
tunnel.on('reconnecting', () => console.log('WebSocket 信道正在重连...'));
tunnel.on('reconnect', () => console.log('WebSocket 信道重连成功'));
tunnel.on('error', error => console.error('信道发生错误:', error));
// 监听自定义消息(服务器进行推送)
tunnel.on('speak', speak => console.log('收到 speak 消息:', speak));
// 打开信道
tunnel.open();
// 发送消息
tunnel.emit('speak', { word: "hello", who: { nickName: "techird" }});
// 关闭信道
tunnel.close();
```
信道服务同样需要业务服务器配合云端 SDK 支持,构造信道实例的时候需要提供业务服务器提供的信道服务地址。通过监听信道消息以及自定义消息来通过信道实现业务。
关于信道使用的更完整实例,建议参考客户端 Demo 中的[三木聊天室应用源码](https://github.com/tencentyun/wafer-client-demo/blob/master/pages/chat/chat.js)
关于信道服务详细技术说明,请参考 [Wiki](https://github.com/tencentyun/wafer-solution/wiki/%E4%BF%A1%E9%81%93%E6%9C%8D%E5%8A%A1)
## API
### setLoginUrl
设置会话服务登录地址。
#### 语法
```js
qcloud.setLoginUrl(loginUrl);
```
#### 参数
|参数         |类型 |说明
|-------------|---------------|--------------
|loginUrl |string |会话服务登录地址
### login
登录,建立微信小程序会话。
#### 语法
```js
qcloud.login(options);
```
#### 参数
|参数         |类型 |说明
|-------------|---------------|--------------
|options     |PlainObject   |会话服务登录地址
|options.success | () => void | 登录成功的回调
|options.error | (error) => void | 登录失败的回调
### request
进行带会话的请求。
#### 语法
```js
qcloud.request(options);
```
#### 参数
|参数         |类型 |说明
|-------------|---------------|--------------
|options     |PlainObject   | 会话服务登录地址
|options.login | bool         | 是否自动登录以获取会话,默认为 false
|options.url   | string       | 必填,要请求的地址
|options.header | PlainObject | 请求头设置,不允许设置 Referer
|options.method | string     | 请求的方法,默认为 GET
|options.success | (response) => void | 登录成功的回调。<ul><li>`response.statusCode`:请求返回的状态码</li><li>`response.data`:请求返回的数据</li></ul>
|options.error | (error) => void | 登录失败的回调
|options.complete | () => void | 登录完成后回调,无论成功还是失败
### Tunnel
表示一个信道。由于小程序的限制,同一时间只能有一个打开的信道。
#### constructor
##### 语法
```js
var tunnel = new Tunnel(tunnelUrl);
```
#### 参数
|参数         |类型 |说明
|-------------|---------------|--------------
|tunnelUrl   |String   | 会话服务登录地址
#### on
监听信道上的事件。信道上事件包括系统事件和服务器推送消息。
##### 语法
```js
tunnel.on(type, listener);
```
##### 参数
|参数         |类型 |说明
|-------------|---------------|--------------
|type       |string     | 监听的事件类型
|listener     |(message?: any) => void | 监听器,具体类型的事件发生时调用监听器。如果是消息,则会有消息内容。
##### 事件
|事件 |说明
|-------------|-------------------------------
|connect |信道连接成功后回调
|close |信道关闭后回调
|reconnecting |信道发生重连时回调
|reconnected |信道重连成功后回调
|error |信道发生错误后回调
|[message]   |信道服务器推送过来的消息类型,如果消息类型和上面内置的时间类型冲突,需要在监听的时候在消息类型前加 `@`
|\*           |监听所有事件和消息,监听器第一个参数接收到时间或消息类型 
#### open
打开信道,建立连接。由于小程序的限制,同一时间只能有一个打开的信道。
##### 语法
```js
tunnel.open();
```
#### emit
向信道推送消息。
##### 语法
```js
tunnel.emit(type, content);
```
##### 参数
|参数         |类型 |说明
|-------------|---------------|--------------
|type       |string       | 要推送的消息的类型
|content |any | 要推送的消息的内容
#### close
关闭信道
##### 语法
```js
tunnel.close();
```
## LICENSE
[MIT](LICENSE)
{
"name": "qcloud-weapp-client-sdk",
"description": "QCloud 微信小程序客户端 SDK",
"main": "index.js",
"authors": [
"Tencent Cloud"
],
"license": "MIT",
"keywords": [
"qcloud",
"weapp",
"wechat",
"sdk",
"client",
"auth",
"websocket"
],
"homepage": "",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests",
"typings.json",
"jsconfig.json",
"package.json",
".npmignore",
".travis.yml",
".gitignore"
]
}
var constants = require('./lib/constants');
var login = require('./lib/login');
var Session = require('./lib/session');
var request = require('./lib/request');
var Tunnel = require('./lib/tunnel');
var exports = module.exports = {
login: login.login,
setLoginUrl: login.setLoginUrl,
LoginError: login.LoginError,
clearSession: Session.clear,
request: request.request,
RequestError: request.RequestError,
Tunnel: Tunnel,
};
// 导出错误类型码
Object.keys(constants).forEach(function (key) {
if (key.indexOf('ERR_') === 0) {
exports[key] = constants[key];
}
});
\ No newline at end of file
module.exports = {
WX_HEADER_CODE: 'X-WX-Code',
WX_HEADER_ENCRYPTED_DATA: 'X-WX-Encrypted-Data',
WX_HEADER_IV: 'X-WX-IV',
WX_HEADER_ID: 'X-WX-Id',
WX_HEADER_SKEY: 'X-WX-Skey',
WX_SESSION_MAGIC_ID: 'F2C224D4-2BCE-4C64-AF9F-A6D872000D1A',
ERR_INVALID_PARAMS: 'ERR_INVALID_PARAMS',
ERR_WX_LOGIN_FAILED: 'ERR_WX_LOGIN_FAILED',
ERR_WX_GET_USER_INFO: 'ERR_WX_GET_USER_INFO',
ERR_LOGIN_TIMEOUT: 'ERR_LOGIN_TIMEOUT',
ERR_LOGIN_FAILED: 'ERR_LOGIN_FAILED',
ERR_LOGIN_SESSION_NOT_RECEIVED: 'ERR_LOGIN_MISSING_SESSION',
ERR_INVALID_SESSION: 'ERR_INVALID_SESSION',
ERR_CHECK_LOGIN_FAILED: 'ERR_CHECK_LOGIN_FAILED',
};
\ No newline at end of file
var utils = require('./utils');
var constants = require('./constants');
var Session = require('./session');
/***
* @class
* 表示登录过程中发生的异常
*/
var LoginError = (function () {
function LoginError(type, message) {
Error.call(this, message);
this.type = type;
this.message = message;
}
LoginError.prototype = new Error();
LoginError.prototype.constructor = LoginError;
return LoginError;
})();
/**
* 微信登录,获取 code 和 encryptData
*/
var getWxLoginResult = function getLoginCode(callback) {
wx.login({
success: function (loginResult) {
wx.getUserInfo({
success: function (userResult) {
callback(null, {
code: loginResult.code,
encryptedData: userResult.encryptedData,
iv: userResult.iv,
userInfo: userResult.userInfo,
});
},
fail: function (userError) {
var error = new LoginError(constants.ERR_WX_GET_USER_INFO, '获取微信用户信息失败,请检查网络状态');
error.detail = userError;
callback(error, null);
},
});
},
fail: function (loginError) {
var error = new LoginError(constants.ERR_WX_LOGIN_FAILED, '微信登录失败,请检查网络状态');
error.detail = loginError;
callback(error, null);
},
});
};
var noop = function noop() {};
var defaultOptions = {
method: 'GET',
success: noop,
fail: noop,
loginUrl: null,
};
/**
* @method
* 进行服务器登录,以获得登录会话
*
* @param {Object} options 登录配置
* @param {string} options.loginUrl 登录使用的 URL,服务器应该在这个 URL 上处理登录请求
* @param {string} [options.method] 请求使用的 HTTP 方法,默认为 "GET"
* @param {Function} options.success(userInfo) 登录成功后的回调函数,参数 userInfo 微信用户信息
* @param {Function} options.fail(error) 登录失败后的回调函数,参数 error 错误信息
*/
var login = function login(options) {
options = utils.extend({}, defaultOptions, options);
if (!defaultOptions.loginUrl) {
options.fail(new LoginError(constants.ERR_INVALID_PARAMS, '登录错误:缺少登录地址,请通过 setLoginUrl() 方法设置登录地址'));
return;
}
var doLogin = () => getWxLoginResult(function (wxLoginError, wxLoginResult) {
if (wxLoginError) {
options.fail(wxLoginError);
return;
}
var userInfo = wxLoginResult.userInfo;
// 构造请求头,包含 code、encryptedData 和 iv
var code = wxLoginResult.code;
var encryptedData = wxLoginResult.encryptedData;
var iv = wxLoginResult.iv;
var header = {};
header[constants.WX_HEADER_CODE] = code;
header[constants.WX_HEADER_ENCRYPTED_DATA] = encryptedData;
header[constants.WX_HEADER_IV] = iv;
header['x-vrp-rid'] = getApp().globalData.rid;
header['x-vrp-page'] = getApp().globalData.curPage;
header['x-vrp-pageval'] = getApp().globalData.pageVal;
// 请求服务器登录地址,获得会话信息
wx.request({
url: options.loginUrl,
header: header,
method: options.method,
data: options.data,
success: function (result) {
var data = result.data;
// 成功地响应会话信息
if (data && data[constants.WX_SESSION_MAGIC_ID]) {
if (data.session) {
data.session.userInfo = userInfo;
Session.set(data.session);
options.success(userInfo);
} else {
var errorMessage = '登录失败(' + data.error + '):' + (data.message || '未知错误');
var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, errorMessage);
options.fail(noSessionError);
}
// 没有正确响应会话信息
} else {
var errorMessage = '登录请求没有包含会话响应,请确保服务器处理 `' + options.loginUrl + '` 的时候正确使用了 SDK 输出登录结果';
var noSessionError = new LoginError(constants.ERR_LOGIN_SESSION_NOT_RECEIVED, errorMessage);
options.fail(noSessionError);
}
},
// 响应错误
fail: function (loginResponseError) {
var error = new LoginError(constants.ERR_LOGIN_FAILED, '登录失败,可能是网络错误或者服务器发生异常');
options.fail(error);
},
});
});
var session = Session.get();
if (session) {
wx.checkSession({
success: function () {
options.success(session.userInfo);
},
fail: function () {
Session.clear();
doLogin();
},
});
} else {
doLogin();
}
};
var setLoginUrl = function (loginUrl) {
defaultOptions.loginUrl = loginUrl;
};
module.exports = {
LoginError: LoginError,
login: login,
setLoginUrl: setLoginUrl,
};
\ No newline at end of file
var constants = require('./constants');
var utils = require('./utils');
var Session = require('./session');
var loginLib = require('./login');
var noop = function noop() {};
var buildAuthHeader = function buildAuthHeader(session) {
var header = {};
if (session && session.id && session.skey) {
header[constants.WX_HEADER_ID] = session.id;
header[constants.WX_HEADER_SKEY] = session.skey;
}
return header;
};
/***
* @class
* 表示请求过程中发生的异常
*/
var RequestError = (function () {
function RequestError(type, message) {
Error.call(this, message);
this.type = type;
this.message = message;
}
RequestError.prototype = new Error();
RequestError.prototype.constructor = RequestError;
return RequestError;
})();
function request(options) {
if (typeof options !== 'object') {
var message = '请求传参应为 object 类型,但实际传了 ' + (typeof options) + ' 类型';
throw new RequestError(constants.ERR_INVALID_PARAMS, message);
}
var requireLogin = options.login;
var success = options.success || noop;
var fail = options.fail || noop;
var complete = options.complete || noop;
var originHeader = options.header || {};
var needTry = true;
if (typeof options.needTry == 'boolean') {
console.log('here boolean', options.needTry);
needTry = options.needTry
}
// 成功回调
var callSuccess = function () {
success.apply(null, arguments);
complete.apply(null, arguments);
};
// 失败回调
var callFail = function (error) {
fail.call(null, error);
complete.call(null, error);
};
// 是否已经进行过重试
// console.log('needTry', !needTry);
var hasRetried = !needTry;
if (requireLogin) {
doRequestWithLogin();
} else {
doRequest();
}
// 登录后再请求
function doRequestWithLogin() {
loginLib.login({ success: doRequest, fail: callFail });
}
// 实际进行请求的方法
function doRequest() {
var authHeader = buildAuthHeader(Session.get());
wx.request(utils.extend({}, options, {
header: utils.extend({}, originHeader, authHeader),
success: function (response) {
var data = response.data;
// 如果响应的数据里面包含 SDK Magic ID,表示被服务端 SDK 处理过,此时一定包含登录态失败的信息
if (data && data[constants.WX_SESSION_MAGIC_ID]) {
// 清除登录态
Session.clear();
var error, message;
if (data.error === constants.ERR_INVALID_SESSION) {
// 如果是登录态无效,并且还没重试过,会尝试登录后刷新凭据重新请求
if (!hasRetried) {
console.log('success data', data)
hasRetried = true;
doRequestWithLogin();
return;
}
message = '登录态已过期';
error = new RequestError(data.error, message);
} else {
message = '鉴权服务器检查登录态发生错误(' + (data.error || 'OTHER') + '):' + (data.message || '未知错误');
error = new RequestError(constants.ERR_CHECK_LOGIN_FAILED, message);
}
console.log('err', error);
let res = {
...data,
...error
}
callFail(res);
return;
}
callSuccess.apply(null, arguments);
},
fail: callFail,
complete: noop,
}));
};
};
module.exports = {
RequestError: RequestError,
request: request,
};
\ No newline at end of file
var constants = require('./constants');
var SESSION_KEY = 'weapp_session_' + constants.WX_SESSION_MAGIC_ID;
var Session = {
get: function () {
return wx.getStorageSync(SESSION_KEY) || null;
},
set: function (session) {
wx.setStorageSync(SESSION_KEY, session);
},
clear: function () {
wx.removeStorageSync(SESSION_KEY);
},
};
module.exports = Session;
\ No newline at end of file
/**
* 拓展对象
*/
exports.extend = function extend(target) {
var sources = Array.prototype.slice.call(arguments, 1);
for (var i = 0; i < sources.length; i += 1) {
var source = sources[i];
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key];
}
}
}
return target;
};
/* istanbul ignore next */
const noop = () => void(0);
let onOpen, onClose, onMessage, onError;
/* istanbul ignore next */
function listen(listener) {
if (listener) {
onOpen = listener.onOpen;
onClose = listener.onClose;
onMessage = listener.onMessage;
onError = listener.onError;
} else {
onOpen = noop;
onClose = noop;
onMessage = noop;
onError = noop;
}
}
/* istanbul ignore next */
function bind() {
wx.onSocketOpen(result => onOpen(result));
wx.onSocketClose(result => onClose(result));
wx.onSocketMessage(result => onMessage(result));
wx.onSocketError(error => onError(error));
}
listen(null);
bind();
module.exports = { listen };
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment