公众号使用云开发 / 极简示例 / HTML

包含了登录逻辑、访问云资源、使用 JSSDK 的示例:

真机调试时需要注意 第三方 Cookie 使用限制

  1. <html>
  2. <head>
  3. <title>云开发 Web 能力极简示例</title>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  5. <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
  6. <script>
  7. window.onerror = e => {
  8. alert('window error ' + e)
  9. }
  10. </script>
  11. <!-- 调试用的移动端 console -->
  12. <script src="https://cdn.jsdelivr.net/npm/eruda"></script>
  13. <script>eruda.init();</script>
  14. <!-- 公众号 JSSDK -->
  15. <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  16. <!-- 云开发 Web SDK -->
  17. <script src="https://res.wx.qq.com/open/js/cloudbase/1.1.0/cloud.js"></script>
  18. <script>
  19. // 公众号基本信息:请修改下列字段使用本示例
  20. const WX_RESOURCE_APPID = 'wxaaaaaaaaaaaaaaaa' /* 资源方 AppID */
  21. const WX_RESOURCE_ENVID = 'cloud1' /* 资源方云开发环境 ID */
  22. const WX_OFFICIAL_ACCOUNT_APPID = 'wxaaaaaaaaaaaaaaaa'/* 公众号 AppID */
  23. const WX_AUTH_TYPE = 'snsapi_base' /* 选择需要的授权方式,snsapi_base 或 snsapi_userinfo */
  24. const WX_REDIRECT_URI = 'https://example.com/' /* 回调 URL */
  25. // 示例配置
  26. const isPersistLoginInfo = false // 是否需要保留登录态信息到 window 中,只有 snsapi_userinfo 方式需要
  27. if (window.wx) {
  28. window.cloud = wx.cloud
  29. }
  30. var urlSearch = new URLSearchParams(location.search)
  31. var accessToken = urlSearch.get('access_token')
  32. var refreshToken = urlSearch.get('refresh_token')
  33. /**
  34. * 检查/发起登录
  35. * 1. 函数会检查当前是否已登录(checkLogin)
  36. * 2. 如果未登录,会 10s 后自动发起登录(startLogin)
  37. * 3. 如果已登录,会初始化实例,使用指定的小程序云开发资源
  38. */
  39. window.doLogin = async () => {
  40. try {
  41. const checkLoginOptions = {
  42. provider: 'OfficialAccount',
  43. appid: WX_OFFICIAL_ACCOUNT_APPID,
  44. }
  45. if (urlSearch.get('oauthredirect') === '1') {
  46. checkLoginOptions.accessToken = accessToken
  47. checkLoginOptions.refreshToken = refreshToken
  48. }
  49. console.log(`checkLogin options: `, checkLoginOptions)
  50. const result = await cloud.checkLogin(checkLoginOptions)
  51. console.log(`checkLogin result: `, result)
  52. isPersistLoginInfo && window.checkLoginRes = result
  53. if (result.errCode === 0 && result.loggedIn) {
  54. console.log(`checkLogin success`)
  55. const instance = window.instance = new cloud.Cloud({
  56. appid: WX_OFFICIAL_ACCOUNT_APPID,
  57. resourceAppid: WX_RESOURCE_APPID,
  58. resourceEnv: WX_RESOURCE_ENVID,
  59. })
  60. const initResult = await instance.init()
  61. console.log(`instance inited`, initResult)
  62. console.log(`can use cloud instance to access resource now !`)
  63. const els = [...document.getElementsByClassName('display-none')]
  64. els.forEach(el => el.classList.remove('display-none'))
  65. } else {
  66. console.log(`checkLogin with sdk errCode ${result.errCode} errMsg ${result.errMsg}, will start oauth in 10s`)
  67. setTimeout(() => {
  68. try {
  69. cloud.startLogin({
  70. provider: 'OfficialAccount',
  71. appid: WX_OFFICIAL_ACCOUNT_APPID,
  72. scope: WX_AUTH_TYPE,
  73. redirectUri: WX_REDIRECT_URI,
  74. })
  75. } catch (e) {
  76. console.error(`startLogin fail: ${e}`)
  77. console.warn(`will start OfficialAccount OAuth login in 10s.`)
  78. }
  79. }, 10000)
  80. }
  81. } catch (e) {
  82. console.error(e)
  83. }
  84. }
  85. /**
  86. * 获取用户信息的示例
  87. * 1. 需在登录后调用
  88. * 2. 网页授权的方式需为 snsapi_userinfo
  89. * 3. 从 checkLogin 结果中获取用户信息 cloudID(已在 doLogin 函数中先暂存到全局变量 checkLoginRes)
  90. * 4. 调用云函数换取 cloudID 信息
  91. */
  92. window.getUserInfo = async () => {
  93. if (!checkLoginRes) throw new Error('获取登录信息失败,请确认授权方式以及是否保存了登录信息')
  94. try {
  95. if (checkLoginRes.cloudID) {
  96. const res = await instance.callFunction({
  97. name: 'echo',
  98. data: {
  99. userInfoData: new instance.CloudID(checkLoginRes.cloudID),
  100. },
  101. })
  102. const cloudData = res.result.userInfoData
  103. if (cloudData.data) {
  104. const userInfoImg = document.querySelector('#userinfo .avatar')
  105. userInfoImg.src = cloudData.data.avatarUrl
  106. const userInfoName = document.querySelector('#userinfo .name')
  107. userInfoName.innerText = cloudData.data.nickName
  108. const userInfoOpenID = document.querySelector('#userinfo .openid')
  109. userInfoOpenID.innerText = cloudData.data.openId
  110. const userInfoDiv = document.getElementById('userinfo')
  111. userInfoDiv.style.cssText = ''
  112. } else {
  113. console.warn(`cloudID data error: `, cloudData)
  114. alert(`cloudID 信息获取错误,请查看调试器报错信息`)
  115. }
  116. } else {
  117. alert(`找不到 cloudID,请确认网页授权方式为 snsnapi_userinfo`)
  118. }
  119. } catch (e) {
  120. console.error(`error: ${e} ${e.stack}`)
  121. }
  122. }
  123. /**
  124. * 使用 JSSDK 的示例
  125. * 1. 需在登录后调用
  126. * 2. 首先会使用云开发 web sdk 提供的 getJSSDKSignature 方法获取网页所需的 wx.config 的签名
  127. * 3. 调用 wx.config
  128. * 4. wx.config 成功之后尝试调用选择图片和获取地理位置作为示例
  129. */
  130. window.useJSSDK = async () => {
  131. try {
  132. const instance = window.instance
  133. console.log(`url: ${location.href}`)
  134. const res = await instance.getJSSDKSignature({
  135. url: location.href,
  136. })
  137. console.log(`jssdk sign res: ${JSON.stringify(res)}`)
  138. const configOpt = {
  139. debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  140. appId: WX_OFFICIAL_ACCOUNT_APPID, // 必填,公众号的唯一标识
  141. timestamp: res.timestamp + '', // 必填,生成签名的时间戳
  142. nonceStr: res.nonceStr, // 必填,生成签名的随机串
  143. signature: res.signature,// 必填,签名
  144. jsApiList: ['chooseImage', 'getLocation'] // 必填,需要使用的JS接口列表
  145. }
  146. console.log(`wx.config opt ${JSON.stringify(configOpt)}`)
  147. wx.config(configOpt)
  148. console.log(`wx.config executed`)
  149. wx.ready(() => {
  150. console.log(`wx.ready triggered`)
  151. setTimeout(() => {
  152. wx.chooseImage({
  153. count: 5,
  154. sizeType: ['original', 'compressed'],
  155. sourceType: ['album', 'camera'],
  156. success : function(res) {
  157. alert('已选择 ' + res.localIds.length + ' 张图片');
  158. },
  159. fail: function(err) {
  160. console.error(`chooseImage fail ${JSON.stringify(err)}`)
  161. },
  162. })
  163. wx.getLocation({
  164. type: 'wgs84', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02'
  165. success: function (res) {
  166. var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90
  167. var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。
  168. var speed = res.speed; // 速度,以米/每秒计
  169. var accuracy = res.accuracy; // 位置精度
  170. console.log(`getLocation ${JSON.stringify(res)}`)
  171. },
  172. fail: err => {
  173. console.log(`getLocation fail ${JSON.stringify(err)}`)
  174. }
  175. });
  176. }, 2000)
  177. })
  178. wx.error(err => {
  179. console.error(`wx.error ${JSON.stringify(err)}`)
  180. })
  181. } catch (e) {
  182. console.error(`error: ${e} ${e.stack}`)
  183. }
  184. }
  185. /**
  186. * 带登录态访问资源方的云开发资源,调用方式见文档,与小程序一致
  187. */
  188. window.accessResource = async () => {
  189. try {
  190. const c = window.instance
  191. await runWithLogs(() => c.database().collection('test').where({}).get(), `start db`, `db res`)
  192. } catch (e) {
  193. console.error('logs', `error: ${e}`)
  194. }
  195. }
  196. /**
  197. * 未登录模式下访问小程序云开发的资源示例
  198. */
  199. window.accessResourceWithoutAuth = async () => {
  200. var c = new cloud.Cloud({
  201. identityless: true, // 表示是未登录模式
  202. resourceAppid: WX_RESOURCE_APPID,
  203. resourceEnv: WX_RESOURCE_ENVID,
  204. })
  205. await c.init()
  206. await runWithLogs(() => c.database().collection('test').where({}).get(), `start db`, `db res`)
  207. }
  208. window.runWithLogs = async (fn, before, after) => {
  209. try {
  210. console.log(before)
  211. const res = await fn()
  212. console.log(`${after}: ${JSON.stringify(res)}`)
  213. } catch (e) {
  214. console.error(`error: ${e}`)
  215. }
  216. }
  217. </script>
  218. <style>
  219. .display-none {
  220. display: none;
  221. }
  222. .userinfo {
  223. display: flex;
  224. flex-direction: column;
  225. align-items: center;
  226. }
  227. .userinfo .avatar {
  228. width: max-content;
  229. box-shadow: inset 0 -3em 3em rgba(0,0,0,0.1), 0 0 0 2px rgb(255,255,255), 0.3em 0.3em 1em rgba(0,0,0,0.3);
  230. }
  231. .userinfo .name {
  232. border-bottom: 2px solid gray;
  233. padding: 0 5px 5px 5px;
  234. }
  235. </style>
  236. </head>
  237. <body>
  238. <h2>云开发 Web 能力极简示例</h2>
  239. <p>请注意按照文档说明配置好,主要包括:</p>
  240. <p>1. 配置好公众号的授权回调域名及 JS 安全域名</p>
  241. <p>2. 配置好云开发授权关系(小程序授权云开发资源给公众号)</p>
  242. <p>3. 将代码中相应需要填入小程序/公众号 AppID 的地方进行相应改动</p>
  243. <p>4. 编写部署好小程序云开发对应云环境的 cloudbase_auth 云函数和 echo 云函数</p>
  244. <p>5. 准备好后,页面加载后先点击登录,登录后再执行访问资源等其他操作,日志可在调试器查看</p>
  245. <div id="userinfo" class="userinfo" style="display: none">
  246. <img class="avatar" />
  247. <p class="name"></p>
  248. <p class="openid"></p>
  249. </div>
  250. <p><a href="javascript:;" onclick="doLogin()">登录(云开发公众号网页授权)</a></p>
  251. <p><a href="javascript:;" class="display-none" onclick="getUserInfo()">获取用户信息</a></p>
  252. <p><a href="javascript:;" class="display-none" onclick="useJSSDK()">使用 JSSDK</a></p>
  253. <p><a href="javascript:;" class="display-none" onclick="accessResource()">访问云资源</a></p>
  254. <p><a href="javascript:;" class="display-none" onclick="accessResourceWithoutAuth()">未登录模式下访问云资源</a></p>
  255. </body>
  256. </html>