auth-login-popup.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <template>
  2. <uni-popup ref="authPopup" type="bottom" :mask-click="false">
  3. <view class="auth-popup">
  4. <view class="close-icon" @tap="closePopup">
  5. <uni-icons type="close" size="24" color="#999"></uni-icons>
  6. </view>
  7. <view>
  8. <button class="login-btn" @tap="handleLogin">授权登录</button>
  9. <view class="agreement-text">
  10. 登录即代表您已同意
  11. <text class="agreement-link" @tap="goToPrivacy">《隐私政策》</text>
  12. <text class="agreement-link" @tap="goToService">《服务协议》</text>
  13. </view>
  14. </view>
  15. </view>
  16. </uni-popup>
  17. </template>
  18. <script>
  19. import {
  20. wxLogin
  21. } from '@/config/api.js';
  22. export default {
  23. name: 'AuthLoginPopup',
  24. data() {
  25. return {
  26. // 默认用户信息
  27. defaultUser: {
  28. }
  29. };
  30. },
  31. methods: {
  32. // 打开弹窗
  33. open() {
  34. this.$refs.authPopup.open();
  35. },
  36. // 关闭弹窗
  37. close() {
  38. this.$refs.authPopup.close();
  39. },
  40. // 关闭授权弹窗
  41. closePopup() {
  42. this.close();
  43. this.$emit('close');
  44. },
  45. // 处理微信授权登录
  46. async handleLogin() {
  47. uni.showLoading({
  48. title: '登录中...',
  49. mask: true
  50. });
  51. try {
  52. // 获取用户信息
  53. const profileRes = await this.getUserProfilePromise();
  54. console.log('用户信息:', profileRes);
  55. // 获取登录凭证
  56. const loginRes = await this.wxLoginPromise();
  57. console.log('登录凭证code:', loginRes.code);
  58. // 调用后端登录接口
  59. const loginData = await wxLogin({
  60. code: loginRes.code
  61. });
  62. console.log('登录响应:', loginData);
  63. // 检查登录是否成功
  64. if (loginData.openid || loginData.code === 200 || loginData.message === "登录成功") {
  65. // 如果用户信息为空,使用默认用户信息
  66. if (!loginData.user || loginData.user === null) {
  67. console.log('用户信息为空,使用默认用户信息');
  68. loginData.user = this.defaultUser;
  69. }
  70. // 保存 token
  71. const token = loginData.token || loginData.access_token;
  72. if (token) {
  73. uni.setStorageSync('access_token', token);
  74. uni.setStorageSync('token', token);
  75. }
  76. // 保存 refresh_token
  77. if (loginData.refresh_token) {
  78. uni.setStorageSync('refresh_token', loginData.refresh_token);
  79. }
  80. // 保存完整的登录数据
  81. uni.setStorageSync('loginData', loginData);
  82. // 保存用户信息
  83. uni.setStorageSync('user', loginData.user);
  84. // 保存 openid
  85. if (loginData.openid) {
  86. uni.setStorageSync('openid', loginData.openid);
  87. }
  88. // 更新 store 状态
  89. this.$store.commit('isLogin', true);
  90. if (loginData.refresh_token) {
  91. this.$store.commit('refresh_token', loginData.refresh_token);
  92. }
  93. uni.hideLoading();
  94. // 关闭授权弹窗
  95. this.close();
  96. // 显示登录成功提示
  97. uni.showToast({
  98. title: '登录成功',
  99. icon: 'success',
  100. duration: 1500
  101. });
  102. // 触发登录成功事件,传递用户信息
  103. this.$emit('success', loginData.user);
  104. } else {
  105. uni.hideLoading();
  106. uni.showToast({
  107. title: loginData.msg || loginData.message || '登录失败',
  108. icon: 'none'
  109. });
  110. this.$emit('fail', loginData);
  111. }
  112. } catch (error) {
  113. console.error('登录错误:', error);
  114. uni.hideLoading();
  115. uni.showToast({
  116. title: error.message || '登录失败,请重试',
  117. icon: 'none'
  118. });
  119. this.$emit('error', error);
  120. }
  121. },
  122. // 获取用户信息 Promise 封装
  123. getUserProfilePromise() {
  124. return new Promise((resolve, reject) => {
  125. uni.getUserProfile({
  126. desc: '用于完善会员资料',
  127. success: (res) => resolve(res),
  128. fail: (err) => {
  129. if (err.errMsg.includes('cancel')) {
  130. reject(new Error('用户取消授权'));
  131. } else {
  132. reject(new Error('获取用户信息失败'));
  133. }
  134. }
  135. });
  136. });
  137. },
  138. // 微信登录 Promise 封装
  139. wxLoginPromise() {
  140. return new Promise((resolve, reject) => {
  141. uni.login({
  142. provider: 'weixin',
  143. success: (res) => resolve(res),
  144. fail: (err) => reject(new Error('获取登录凭证失败'))
  145. });
  146. });
  147. },
  148. // 跳转到隐私政策
  149. goToPrivacy() {
  150. uni.navigateTo({
  151. url: '/pagesA/public/richtext'
  152. });
  153. },
  154. // 跳转到服务协议
  155. goToService() {
  156. uni.navigateTo({
  157. url: '/pagesA/public/richtext'
  158. });
  159. }
  160. }
  161. };
  162. </script>
  163. <style lang="scss" scoped>
  164. // 确保 uni-popup 组件的 z-index 足够高
  165. ::v-deep .uni-popup {
  166. z-index: 9999 !important;
  167. }
  168. ::v-deep .uni-popup__wrapper {
  169. z-index: 9999 !important;
  170. }
  171. .auth-popup {
  172. width: 100%;
  173. background-color: #fff;
  174. border-radius: 24rpx 24rpx 0 0;
  175. padding: 60rpx 40rpx 50rpx;
  176. box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
  177. position: relative;
  178. z-index: 10000;
  179. }
  180. .close-icon {
  181. position: absolute;
  182. top: 20rpx;
  183. right: 20rpx;
  184. width: 50rpx;
  185. height: 50rpx;
  186. display: flex;
  187. align-items: center;
  188. justify-content: center;
  189. cursor: pointer;
  190. z-index: 10001;
  191. &:active {
  192. opacity: 0.6;
  193. }
  194. }
  195. .login-btn {
  196. width: 100%;
  197. height: 88rpx;
  198. background-color: #FF5B05;
  199. color: white;
  200. border: none;
  201. border-radius: 44rpx;
  202. font-size: 32rpx;
  203. font-weight: 600;
  204. margin-bottom: 40rpx;
  205. }
  206. .login-btn:after {
  207. border: none;
  208. }
  209. .agreement-text {
  210. font-size: 24rpx;
  211. color: rgba(41, 44, 53, 0.6);
  212. line-height: 36rpx;
  213. text-align: center;
  214. }
  215. .agreement-link {
  216. color: #FF5B05;
  217. text-decoration: none;
  218. }
  219. </style>