目的:想要实现小程序的每日打卡通知功能。
配置
首先需要在微信小程序公众后台,申请订阅消息模板
uniCloud 配置 文档地址 https://doc.dcloud.net.cn/uniCloud/uni-subscribemsg.html
配置 uni-open-bridge 文档地址 https://doc.dcloud.net.cn/uniCloud/uni-open-bridge.html#uni-open-bridge%E7%9A%84%E4%BD%BF%E7%94%A8%E6%B5%81%E7%A8%8B下载插件 uni-open-bridge 到项目中。
uni-config-center 的 uni-id 下配置固定凭据 如果你没有 appid 和 secret ,需要先向微信申请
在项目的 uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json 文件中配置(没有就创建) uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json
{
"dcloudAppid": "__UNI__xxxxxx",
"mp-weixin": {
"tokenExpiresIn": 259200,
"oauth": {
"weixin": {
"appid": "",
"appsecret": ""
}
}
}
}
在 uni-config-center 目录下新建子目录 uni-open-bridge, 新增 config.json,配置 dcloudAppid ,详情见下面的示例代码 uniCloud/cloudfunctions/common/uni-config-center/uni-open-bridge/config.json
{
"schedule": {
"__UNI__xxxxxx": {
"enable": true,
"weixin-mp": {
"enable": true,
"tasks": ["accessToken"]
}
}
},
"ipWhiteList": ["0.0.0.0"]
}
实现步骤
- 必须要有点击事件,只有用户操作行为才能掉起微信的授权弹框
<template>
<view
v-if="!item.openSubscribe"
@click.stop="laqi(index)"
class="notification"
>
<uni-icons type="notification" size="24" color="#fff"></uni-icons>
</view>
<view v-else class="notification" @click.stop="cancelSubscribe(item)">
<uni-icons type="notification-filled" size="24" color="#fff"></uni-icons>
<text>{{ item.time }}</text>
</view>
<!-- 一开始使用picker,后来发现change事件不能拉起订阅弹框,必须点击 -->
<uni-popup ref="popup2" class="popupbottom" type="bottom">
<div class="cont2">
<div class="h">
<div @click="cancel" class="red">取消</div>
<div @click="confirm" class="blue">确定</div>
</div>
<picker-view @change="bindChange" class="picker-view">
<picker-view-column>
<view class="item" v-for="(item, index) in hour" :key="index">{{
item
}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(item, index) in minute" :key="index">{{
item
}}</view>
</picker-view-column>
</picker-view>
</div>
</uni-popup>
</template>
<style lang="scss" scoped>
.popupbottom {
.cont2 {
background: #fff;
height: 500rpx;
margin-bottom: -68rpx;
.h {
display: flex;
justify-content: space-between;
padding: 30rpx 40rpx;
.blue {
color: #1d8acc;
}
.red {
color: red;
}
}
}
.picker-view {
width: 750rpx;
height: 400rpx;
.item {
display: flex;
align-items: center;
justify-content: center;
}
}
}
</style>
- 在事件中首先发送订阅事件,弹起弹框,让用户同意
<script setup lang="ts">
import { ref } from "vue";
const hour = ref([]);
const minute = ref([]);
const popup2 = ref(null); //弹框
const i = ref(); //获取点击的是哪个打卡按钮
for (let i = 0; i < 24; i++) {
hour.value.push(i.toString().padStart(2, "0"));
}
for (let i = 0; i < 60; i++) {
minute.value.push(i.toString().padStart(2, "0"));
}
function laqi(index: number) {
uni.showLoading();
uni.getSetting({
withSubscriptions: true,
success(res) {
console.log(res);
if (!res.subscriptionsSetting.mainSwitch) {
// uni.openSetting({
// //打开设置页
// // success(res) {}
// });
console.log("没有开启权限");
uni.showToast({
icon: "none",
title:
"您没有开启权限,请点击右上角三个点,点击设置,点击订阅消息,开启开关",
});
} else {
uni.requestSubscribeMessage({
tmplIds: ["zcdi3QqihqCgMZLj6exxxx"], // 订阅消息模板ID, 在微信小程序公众后台,订阅消息->模板消息->选择模板->复制模板ID
success(res) {
uni.hideLoading();
if (res["zcdi3QqihqCgMZLj6exxxx"] == "accept") {
console.log("订阅成功", res);
popup2.value?.open();
i.value = index;
}
},
fail(err) {
uni.hideLoading();
console.log("requestSubscribeMessage err", err);
uni.showToast({
icon: "none",
title: "订阅失败",
});
},
});
}
},
fail(err) {
console.log("getSetting err", err);
uni.hideLoading();
uni.showToast({
icon: "none",
title: "获取设置信息失败",
});
},
});
}
</script>
(这是开发者工具的弹框样式,手机的不一样)
用户弹出时间选择框,选择时间
将打卡列表的按钮状态更改,并添加记录到新的表 打卡列表的按钮状态不详细介绍 添加记录到新的表: (这样的结构) 云函数:
"use strict";
exports.main = async (event, context) => {
//event为客户端上传的参数
console.log("event : ", event);
const db = uniCloud.database();
const collection = db.collection("clockInList");
const collection2 = db.collection("subscribeList");
const data = {
time: event.time,
openSubscribe: event.openSubscribe,
};
await collection.doc(event.id).update(data);
const data2 = {
openId: event.openId,
clockinId: event.id,
title: event.title,
time: event.time,
};
if (event.openSubscribe) {
await collection2.add(data2);
} else {
await collection2
.where({
clockinId: event.id,
})
.remove();
}
return {
code: 0,
data: {},
};
};
- 定时任务,每分钟定时检查是否有打卡记录,有则发送通知 云函数:
"use strict";
const UniSubscribemsg = require("uni-subscribemsg");
const moment = require("moment");
exports.main = async (event, context) => {
// 引入uni-subscribemsg公共模块
// 初始化实例
let uniSubscribemsg = new UniSubscribemsg({
dcloudAppid: "__UNI__7E3Exxx",
provider: "weixin-mp",
});
const db = uniCloud.database();
const time =
new Date().getTimezoneOffset() == 0
? moment().add(8, "hours").format("HH:mm")
: moment().format("HH:mm");
const collection2 = db.collection("subscribeList");
const res = await collection2
.where({
time: time,
})
.get();
console.log(res);
const arr = [
"立志不坚,终不济事。",
"凿不休则沟深,斧不止则薪多。",
"只要功夫深,铁棒磨成针。",
"成功之因,在于不屈不挠。",
"所谓天才,就是努力的力量。",
"耐心和恒心总会得到报酬的。",
"不经一翻彻骨寒,怎得梅花扑鼻香。",
"最难做的事是把容易做的事持之以恒。",
"读不在三更五鼓,功只怕一曝十寒。",
"黑发不知勤学早,白首方恨读书迟。",
"道足以忘物之得春,志足以一气之盛衰。",
"一日一钱,千日千钱,绳锯木断,水滴石穿。",
"奋斗这一件事是自有人类以来天天不息的。",
"日日行,不怕千万里;常常做,不怕千万事。",
"锲而舍之,朽木不折;锲而不舍,金石可镂。",
];
const msg = arr[Math.floor(Math.random() * arr.length)];
console.log(msg);
for (let item of res.data) {
// 发送订阅消息
console.log(item.openId, item.title);
const res = await uniSubscribemsg.sendSubscribeMessage({
touser: item.openId,
template_id: "zcdi3QqihqCgMZLj6epP3DDhbsZ90xxx",
page: "pages/clockIn/index", // 小程序页面地址
miniprogram_state: "formal", // 跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
lang: "zh_CN",
data: {
thing4: {
value: item.title,
},
thing3: {
// value: arr[index]
value: msg,
},
},
});
console.log("sendSubscribeMessage==>", item.openId, res);
}
return {
code: 0,
data: {},
};
};
注意: 在此云函数目录运行 npm install moment
设置定时:uniapp web 控制台-云函数/详情/定时器触发
["0 0/1 * * * ?"];
// 发送文字大于20个字会报错
result: {
errcode: 47003,
errmsg: 'argument invalid! data.thing3.value invalid rid: 66ae1a59-1fd52776-679954c5'
}
// 需要重新订阅
result: {
errcode: 43101,
errmsg: 'user refuse to accept the msg rid: 66bb66e4-101034a6-4ea10941'
}
如果云函数接口没有 await,可能会发生测试环境有效,生产环境出问题的情况。