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

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