uni-id-pages-fab-login.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <template>
  2. <view>
  3. <view class="fab-login-box">
  4. <view class="item" v-for="(item,index) in servicesList" :key="index"
  5. @click="item.path?navigateTo(item.path):login_before(item.id,false)">
  6. <image class="logo" :src="item.logo" mode="scaleToFill"></image>
  7. <text class="login-title">{{item.text}}</text>
  8. </view>
  9. </view>
  10. <!-- #ifdef MP-WEIXIN -->
  11. <uni-id-pages-user-profile @next="doUserProfileNext" ref="userProfile"></uni-id-pages-user-profile>
  12. <!-- #endif -->
  13. </view>
  14. </template>
  15. <script>
  16. import config from '@/uni_modules/uni-id-pages/config.js'
  17. //前一个窗口的页面地址。控制点击切换快捷登录方式是创建还是返回
  18. import loginSuccess from '../../common/loginSuccess.js';
  19. const db = uniCloud.database();
  20. const usersTable = db.collection('uni-id-users')
  21. let allServicesList = []
  22. export default {
  23. computed: {
  24. agreements() {
  25. if (!config.agreements) {
  26. return []
  27. }
  28. let {
  29. serviceUrl,
  30. privacyUrl
  31. } = config.agreements
  32. return [{
  33. url: serviceUrl,
  34. title: "用户服务协议"
  35. },
  36. {
  37. url: privacyUrl,
  38. title: "隐私政策条款"
  39. }
  40. ]
  41. },
  42. agree: {
  43. get() {
  44. return this.getParentComponent().agree
  45. },
  46. set(agree) {
  47. console.log('setAgree', agree);
  48. return this.getParentComponent().agree = agree
  49. }
  50. }
  51. },
  52. data() {
  53. return {
  54. servicesList: [{
  55. "id": "username",
  56. "text": "账号登录",
  57. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/user.png",
  58. "path": "/uni_modules/uni-id-pages/pages/login/login-withpwd"
  59. },
  60. {
  61. "id": "smsCode",
  62. "text": "短信验证码",
  63. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/sms.png",
  64. "path": "/uni_modules/uni-id-pages/pages/login/login-withoutpwd?type=smsCode"
  65. },
  66. {
  67. "id": "weixin",
  68. "text": "微信登录",
  69. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/weixin.png",
  70. },
  71. {
  72. "id": "apple",
  73. "text": "苹果登录",
  74. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/apple.png",
  75. },
  76. {
  77. "id": "univerify",
  78. "text": "一键登录",
  79. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/univerify.png",
  80. },
  81. {
  82. "id": "taobao",
  83. "text": "淘宝登录", //暂未提供该登录方式的接口示例
  84. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/taobao.png",
  85. },
  86. {
  87. "id": "facebook",
  88. "text": "脸书登录", //暂未提供该登录方式的接口示例
  89. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/facebook.png",
  90. },
  91. {
  92. "id": "alipay",
  93. "text": "支付宝登录", //暂未提供该登录方式的接口示例
  94. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/alipay.png",
  95. },
  96. {
  97. "id": "qq",
  98. "text": "QQ登录", //暂未提供该登录方式的接口示例
  99. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/qq.png",
  100. },
  101. {
  102. "id": "google",
  103. "text": "谷歌登录", //暂未提供该登录方式的接口示例
  104. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/google.png",
  105. },
  106. {
  107. "id": "douyin",
  108. "text": "抖音登录", //暂未提供该登录方式的接口示例
  109. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/douyin.png",
  110. },
  111. {
  112. "id": "sinaweibo",
  113. "text": "新浪微博", //暂未提供该登录方式的接口示例
  114. "logo": "/uni_modules/uni-id-pages/static/uni-fab-login/sinaweibo.png",
  115. }
  116. ],
  117. univerifyStyle: { //一键登录弹出窗的样式配置参数
  118. "fullScreen": true, // 是否全屏显示,true表示全屏模式,false表示非全屏模式,默认值为false。
  119. "backgroundColor": "#ffffff", // 授权页面背景颜色,默认值:#ffffff
  120. "buttons": { // 自定义登录按钮
  121. "iconWidth": "45px", // 图标宽度(高度等比例缩放) 默认值:45px
  122. "list": []
  123. },
  124. "privacyTerms": {
  125. "defaultCheckBoxState": false, // 条款勾选框初始状态 默认值: true
  126. "textColor": "#BBBBBB", // 文字颜色 默认值:#BBBBBB
  127. "termsColor": "#5496E3", // 协议文字颜色 默认值: #5496E3
  128. "prefix": "我已阅读并同意", // 条款前的文案 默认值:“我已阅读并同意”
  129. "suffix": "并使用本机号码登录", // 条款后的文案 默认值:“并使用本机号码登录”
  130. "privacyItems": []
  131. }
  132. }
  133. }
  134. },
  135. watch: {
  136. agree(agree) {
  137. this.univerifyStyle.privacyTerms.defaultCheckBoxState = agree
  138. }
  139. },
  140. async created() {
  141. let servicesList = this.servicesList
  142. let loginTypes = config.loginTypes
  143. servicesList = servicesList.filter(item => {
  144. // #ifndef APP
  145. //非app端去掉apple登录
  146. if (item.id == 'apple') {
  147. return false
  148. }
  149. // #endif
  150. // #ifdef APP
  151. //去掉非ios系统上的apple登录
  152. if (item.id == 'apple' && uni.getSystemInfoSync().osName != 'ios') {
  153. return false
  154. }
  155. // #endif
  156. return loginTypes.includes(item.id)
  157. })
  158. //处理一键登录
  159. if (loginTypes.includes('univerify')) {
  160. this.univerifyStyle.privacyTerms.privacyItems = this.agreements
  161. //设置一键登录功能底下的快捷登录按钮
  162. servicesList.forEach(({
  163. id,
  164. logo,
  165. path
  166. }) => {
  167. if (id != 'univerify') {
  168. this.univerifyStyle.buttons.list.push({
  169. "iconPath": logo,
  170. "provider": id,
  171. path //路径用于点击快捷按钮时判断是跳转页面
  172. })
  173. }
  174. })
  175. }
  176. // console.log(servicesList);
  177. //去掉当前页面对应的登录选项
  178. this.servicesList = servicesList.filter(item => {
  179. let path = item.path ? item.path.split('?')[0] : '';
  180. return path != this.getRoute(1)
  181. })
  182. //console.log('servicesList', servicesList, this.servicesList);
  183. },
  184. methods: {
  185. getParentComponent(){
  186. // #ifndef H5
  187. return this.$parent;
  188. // #endif
  189. // #ifdef H5
  190. return this.$parent.$parent;
  191. // #endif
  192. },
  193. setUserInfo(e) {
  194. console.log('setUserInfo', e);
  195. },
  196. getRoute(n = 0) {
  197. let pages = getCurrentPages();
  198. // console.log('route-pages-length', pages.length);
  199. if (n > pages.length) {
  200. return ''
  201. }
  202. return '/' + pages[pages.length - n].route
  203. },
  204. navigateTo(path) {
  205. //console.log('比较', this.getRoute(1),this.getRoute(2), path)
  206. if (this.getRoute(1) == path.split('?')[0] && this.getRoute(1) ==
  207. '/uni_modules/uni-id-pages/pages/login/login-withoutpwd') {
  208. //如果要被打开的页面已经打开,且这个页面是 /uni_modules/uni-id-pages/pages/index/index 则把类型参数传给他
  209. let type = path.split('?')[1].split('=')[1]
  210. uni.$emit('uni-id-pages-set-login-type', type)
  211. } else if (this.getRoute(2) == path) { // 如果上一个页面就是,马上要打开的页面,直接返回。防止重复开启
  212. uni.navigateBack();
  213. } else if (this.getRoute(1) != path) {
  214. //console.log(3);
  215. uni.navigateTo({
  216. url: path,
  217. animationType: 'slide-in-left',
  218. complete(e) {
  219. // console.log(e);
  220. }
  221. })
  222. } else {
  223. console.log('出乎意料的情况,path:' + path);
  224. }
  225. },
  226. async login_before(type, navigateBack = true) {
  227. console.log(type);
  228. //提示空实现
  229. if (["qq",
  230. "xiaomi",
  231. "sinaweibo",
  232. "taobao",
  233. "facebook",
  234. "google",
  235. "alipay",
  236. "douyin",
  237. ].includes(type)) {
  238. return uni.showToast({
  239. title: '该登录方式暂未实现,欢迎提交pr',
  240. icon: 'none'
  241. });
  242. }
  243. //检查当前环境是否支持这种登录方式
  244. // #ifdef APP
  245. let isAppExist = true
  246. await new Promise((callback) => {
  247. plus.oauth.getServices(oauthServices => {
  248. let index = oauthServices.findIndex(e => e.id == type)
  249. if(index != -1){
  250. isAppExist = oauthServices[index].nativeClient
  251. callback()
  252. }else{
  253. return uni.showToast({
  254. title: '当前设备不支持此登录,请选择其他登录方式',
  255. icon: 'none'
  256. });
  257. }
  258. }, err => {
  259. throw new Error('获取服务供应商失败:' + JSON.stringify(err))
  260. })
  261. })
  262. // #endif
  263. if (
  264. // #ifdef APP
  265. !isAppExist
  266. // #endif
  267. //非app端使用了,app特有登录方式
  268. // #ifndef APP
  269. ["univerify","apple"].includes(type)
  270. // #endif
  271. ) {
  272. return uni.showToast({
  273. title: '当前设备不支持此登录,请选择其他登录方式',
  274. icon: 'none'
  275. });
  276. }
  277. //判断是否需要弹出隐私协议授权框
  278. console.log(type, this.agree);
  279. let needAgreements = (config?.agreements?.scope || []).includes('register')
  280. console.log({
  281. needAgreements
  282. });
  283. if (type != 'univerify' && needAgreements && !this.agree) {
  284. let agreementsRef = this.getParentComponent().$refs.agreements
  285. return agreementsRef.popup(() => {
  286. console.log(type, navigateBack);
  287. this.login_before(type, navigateBack)
  288. })
  289. }
  290. // #ifdef H5
  291. if(type == 'weixin'){
  292. // console.log('开始微信网页登录');
  293. let redirectUrl = location.protocol +'//'+
  294. document.domain +
  295. (window.location.href.includes('#')?'/#':'') +
  296. '/uni_modules/uni-id-pages/pages/login/login-withoutpwd?is_weixin_redirect=true&type=weixin'
  297. console.log('redirectUrl----',redirectUrl);
  298. let ua = window.navigator.userAgent.toLowerCase();
  299. if (ua.match(/MicroMessenger/i) == 'micromessenger'){
  300. // console.log('在微信公众号内');
  301. return window.open(`https://open.weixin.qq.com/connect/oauth2/authorize?
  302. appid=${config.appid.weixin.h5}
  303. &redirect_uri=${encodeURIComponent(redirectUrl)}
  304. &response_type=code
  305. &scope=snsapi_userinfo
  306. &state=STATE&connect_redirect=1#wechat_redirect`);
  307. }else{
  308. // console.log('非微信公众号内');
  309. return location.href = `https://open.weixin.qq.com/connect/qrconnect?appid=${config.appid.weixin.web}
  310. &redirect_uri=${encodeURIComponent(redirectUrl)}
  311. &response_type=code&scope=snsapi_login&state=STATE#wechat_redirect`
  312. }
  313. }
  314. // #endif
  315. uni.showLoading({
  316. mask: true
  317. })
  318. if (type == 'univerify') {
  319. let univerifyManager = uni.getUniverifyManager()
  320. let onButtonsClickFn = async res => {
  321. console.log('点击了第三方登录,provider:', res, res.provider, this.univerifyStyle.buttons.list);
  322. //同步一键登录弹出层隐私协议框是否打勾
  323. let agree = (await uni.getCheckBoxState())[1].state
  324. //console.log('agree',agree);
  325. this.agree = agree
  326. let {
  327. path
  328. } = this.univerifyStyle.buttons.list[res.index]
  329. if (path) {
  330. this.navigateTo(path)
  331. closeUniverify()
  332. } else {
  333. if (agree) {
  334. closeUniverify()
  335. setTimeout(() => {
  336. //console.log('login_before');
  337. this.login_before(res.provider)
  338. }, 500)
  339. } else {
  340. //console.log("你未同意隐私政策协议");
  341. uni.showToast({
  342. title: "你未同意隐私政策协议",
  343. icon: 'none'
  344. });
  345. }
  346. }
  347. }
  348. function closeUniverify() {
  349. uni.hideLoading()
  350. univerifyManager.close()
  351. // 取消订阅自定义按钮点击事件
  352. univerifyManager.offButtonsClick(onButtonsClickFn)
  353. }
  354. // 订阅自定义按钮点击事件
  355. univerifyManager.onButtonsClick(onButtonsClickFn)
  356. // 调用一键登录弹框
  357. return univerifyManager.login({
  358. "univerifyStyle": this.univerifyStyle,
  359. success: res => {
  360. console.log('login success', res)
  361. this.login(res.authResult, 'univerify')
  362. },
  363. fail(err) {
  364. uni.showToast({
  365. title: JSON.stringify(err),
  366. icon: 'none'
  367. });
  368. },
  369. complete: async e => {
  370. console.log(e);
  371. uni.hideLoading()
  372. //同步一键登录弹出层隐私协议框是否打勾
  373. this.agree = (await uni.getCheckBoxState())[1].state
  374. // 取消订阅自定义按钮点击事件
  375. univerifyManager.offButtonsClick(onButtonsClickFn)
  376. }
  377. })
  378. }
  379. uni.login({
  380. "provider": type,
  381. "onlyAuthorize": true,
  382. // #ifdef APP
  383. "univerifyStyle": this.univerifyStyle,
  384. // #endif
  385. success: async e => {
  386. console.log(e);
  387. if (type == 'apple') {
  388. let res = await this.getUserInfo({
  389. provider: "apple"
  390. })
  391. Object.assign(e.authResult, res.userInfo)
  392. uni.hideLoading()
  393. }
  394. this.login(type == 'weixin' ? {
  395. code: e.code
  396. } : e.authResult, type)
  397. },
  398. fail: async (err) => {
  399. console.log(err);
  400. uni.hideLoading()
  401. }
  402. })
  403. },
  404. login(params, type) { //联网验证登录
  405. console.log('执行登录开始----');
  406. console.log({
  407. params,
  408. type
  409. });
  410. //toLowerCase
  411. let action = 'loginBy' + type.trim().toLowerCase().replace(type[0], type[0].toUpperCase())
  412. const uniIdCo = uniCloud.importObject("uni-id-co",{
  413. customUI:true
  414. })
  415. uniIdCo[action](params).then(result => {
  416. console.log("login-result", result);
  417. uni.showToast({
  418. title: '登录成功',
  419. icon: 'none'
  420. });
  421. // #ifdef MP-WEIXIN
  422. //如果是微信小程序端的微信登录,且为首次登录,就弹出获取微信昵称+头像用于绑定资料
  423. if (type == 'weixin' && result.type == "register") {
  424. loginSuccess({
  425. ...result,
  426. showToast: false,
  427. autoBack: false
  428. })
  429. return this.$refs.userProfile.open(result.uid)
  430. }
  431. // #endif
  432. // #ifdef H5
  433. result.loginType = type
  434. // #endif
  435. loginSuccess(result)
  436. })
  437. .catch(e=>{
  438. console.log(e);
  439. uni.showModal({
  440. content: e.message,
  441. confirmText:"知道了",
  442. showCancel: false
  443. });
  444. })
  445. .finally(e => {
  446. if (type == 'univerify') {
  447. uni.closeAuthView()
  448. }
  449. uni.hideLoading()
  450. })
  451. },
  452. doUserProfileNext() {
  453. try {
  454. loginSuccess()
  455. } catch (e) {
  456. console.log(e);
  457. }
  458. },
  459. async getUserInfo(e) {
  460. return new Promise((resolve, reject) => {
  461. uni.getUserInfo({
  462. ...e,
  463. success: (res) => {
  464. resolve(res);
  465. },
  466. fail: (err) => {
  467. uni.showModal({
  468. content: JSON.stringify(err),
  469. showCancel: false
  470. });
  471. reject(err);
  472. }
  473. })
  474. })
  475. }
  476. }
  477. }
  478. </script>
  479. <style lang="scss">
  480. /* #ifndef APP-NVUE */
  481. .fab-login-box,
  482. .item {
  483. display: flex;
  484. box-sizing: border-box;
  485. flex-direction: column;
  486. }
  487. /* #endif */
  488. .fab-login-box {
  489. flex-direction: row;
  490. flex-wrap: wrap;
  491. width: 750rpx;
  492. justify-content: space-around;
  493. position: fixed;
  494. left: 0;
  495. }
  496. .item {
  497. flex-direction: column;
  498. justify-content: center;
  499. align-items: center;
  500. height: 200rpx;
  501. cursor: pointer;
  502. }
  503. /* #ifndef APP-NVUE */
  504. @media screen and (min-width: 690px) {
  505. .fab-login-box {
  506. max-width: 500px;
  507. margin-left: calc(50% - 250px);
  508. }
  509. .item {
  510. height: 160rpx;
  511. }
  512. }
  513. @media screen and (max-width: 690px) {
  514. .fab-login-box {
  515. bottom: 10rpx;
  516. }
  517. }
  518. /* #endif */
  519. .logo {
  520. width: 60rpx;
  521. height: 60rpx;
  522. max-width: 40px;
  523. max-height: 40px;
  524. border-radius: 100%;
  525. border: solid 1px #F6F6F6;
  526. }
  527. .login-title {
  528. text-align: center;
  529. margin-top: 6px;
  530. color: #999;
  531. font-size: 10px;
  532. width: 70px;
  533. }
  534. </style>