auth-login-popup.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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="handleLoginClick">授权登录</button>
  9. <view class="agreement-wrapper">
  10. <view class="checkbox-wrapper" @tap="toggleAgreement">
  11. <view class="zen-checkbox" :class="{ 'checked': isAgree }">
  12. <view class="zen-circle-inner" v-if="isAgree"></view>
  13. </view>
  14. </view>
  15. <view class="agreement-text">
  16. 登录即代表您已同意
  17. <text class="agreement-link" @tap.stop="goToPrivacy">《隐私政策》</text>
  18. <text class="agreement-link" @tap.stop="goToService">《服务协议》</text>
  19. </view>
  20. </view>
  21. </view>
  22. </view>
  23. </uni-popup>
  24. </template>
  25. <script>
  26. import {
  27. wxLogin
  28. } from '@/config/api.js';
  29. export default {
  30. name: 'AuthLoginPopup',
  31. data() {
  32. return {
  33. // 默认用户信息
  34. defaultUser: {
  35. },
  36. // 是否同意协议
  37. isAgree: false
  38. };
  39. },
  40. methods: {
  41. // 切换协议同意状态
  42. toggleAgreement() {
  43. this.isAgree = !this.isAgree;
  44. },
  45. // 处理登录按钮点击
  46. handleLoginClick() {
  47. if (!this.isAgree) {
  48. // 未勾选协议,弹出确认框
  49. let that = this;
  50. uni.showModal({
  51. title: '提示',
  52. content: '登录需要您同意《隐私政策》和《服务协议》,是否同意?',
  53. confirmText: '同意',
  54. cancelText: '取消',
  55. success: function(res) {
  56. if (res.confirm) {
  57. // 用户点击同意,自动勾选协议并执行登录
  58. that.isAgree = true;
  59. that.handleLogin();
  60. } else if (res.cancel) {
  61. console.log('用户点击取消');
  62. }
  63. }
  64. });
  65. } else {
  66. // 已勾选协议,直接登录
  67. this.handleLogin();
  68. }
  69. },
  70. // 打开弹窗
  71. open() {
  72. this.$refs.authPopup.open();
  73. },
  74. // 关闭弹窗
  75. close() {
  76. this.$refs.authPopup.close();
  77. },
  78. // 关闭授权弹窗
  79. closePopup() {
  80. this.close();
  81. this.$emit('close');
  82. },
  83. // 处理微信授权登录
  84. async handleLogin() {
  85. uni.showLoading({
  86. title: '登录中...',
  87. mask: true
  88. });
  89. try {
  90. // 获取用户信息
  91. const profileRes = await this.getUserProfilePromise();
  92. console.log('用户信息:', profileRes);
  93. // 获取登录凭证
  94. const loginRes = await this.wxLoginPromise();
  95. console.log('登录凭证code:', loginRes.code);
  96. // 调用后端登录接口
  97. const loginData = await wxLogin({
  98. code: loginRes.code
  99. });
  100. console.log('登录响应:', loginData);
  101. // 检查登录是否成功
  102. if (loginData.openid || loginData.code === 200 || loginData.message === "登录成功") {
  103. // 如果用户信息为空,使用默认用户信息
  104. if (!loginData.user || loginData.user === null) {
  105. console.log('用户信息为空,使用默认用户信息');
  106. loginData.user = this.defaultUser;
  107. }
  108. // 保存 token
  109. const token = loginData.token || loginData.access_token;
  110. if (token) {
  111. uni.setStorageSync('access_token', token);
  112. uni.setStorageSync('token', token);
  113. }
  114. // 保存 refresh_token
  115. if (loginData.refresh_token) {
  116. uni.setStorageSync('refresh_token', loginData.refresh_token);
  117. }
  118. // 保存完整的登录数据
  119. uni.setStorageSync('loginData', loginData);
  120. // 保存用户信息
  121. uni.setStorageSync('user', loginData.user);
  122. // 保存 openid
  123. if (loginData.openid) {
  124. uni.setStorageSync('openid', loginData.openid);
  125. }
  126. // 更新 store 状态
  127. this.$store.commit('isLogin', true);
  128. if (loginData.refresh_token) {
  129. this.$store.commit('refresh_token', loginData.refresh_token);
  130. }
  131. uni.hideLoading();
  132. // 关闭授权弹窗
  133. this.close();
  134. // 显示登录成功提示
  135. uni.showToast({
  136. title: '登录成功',
  137. icon: 'success',
  138. duration: 1500
  139. });
  140. // 触发登录成功事件,传递用户信息
  141. this.$emit('success', loginData.user);
  142. } else {
  143. uni.hideLoading();
  144. uni.showToast({
  145. title: loginData.msg || loginData.message || '登录失败',
  146. icon: 'none'
  147. });
  148. this.$emit('fail', loginData);
  149. }
  150. } catch (error) {
  151. console.error('登录错误:', error);
  152. uni.hideLoading();
  153. uni.showToast({
  154. title: error.message || '登录失败,请重试',
  155. icon: 'none'
  156. });
  157. this.$emit('error', error);
  158. }
  159. },
  160. // 获取用户信息 Promise 封装
  161. getUserProfilePromise() {
  162. return new Promise((resolve, reject) => {
  163. uni.getUserProfile({
  164. desc: '用于完善会员资料',
  165. success: (res) => resolve(res),
  166. fail: (err) => {
  167. if (err.errMsg.includes('cancel')) {
  168. reject(new Error('用户取消授权'));
  169. } else {
  170. reject(new Error('获取用户信息失败'));
  171. }
  172. }
  173. });
  174. });
  175. },
  176. // 微信登录 Promise 封装
  177. wxLoginPromise() {
  178. return new Promise((resolve, reject) => {
  179. uni.login({
  180. provider: 'weixin',
  181. success: (res) => resolve(res),
  182. fail: (err) => reject(new Error('获取登录凭证失败'))
  183. });
  184. });
  185. },
  186. // 跳转到隐私政策
  187. goToPrivacy() {
  188. uni.navigateTo({
  189. url: '/pagesA/public/richtext'
  190. });
  191. },
  192. // 跳转到服务协议
  193. goToService() {
  194. uni.navigateTo({
  195. url: '/pagesA/public/richtext'
  196. });
  197. }
  198. }
  199. };
  200. </script>
  201. <style lang="scss" scoped>
  202. // 确保 uni-popup 组件的 z-index 足够高
  203. ::v-deep .uni-popup {
  204. z-index: 9999 !important;
  205. }
  206. ::v-deep .uni-popup__wrapper {
  207. z-index: 9999 !important;
  208. }
  209. .auth-popup {
  210. width: 100%;
  211. background-color: #fff;
  212. border-radius: 24rpx 24rpx 0 0;
  213. padding: 60rpx 40rpx 50rpx;
  214. box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.1);
  215. position: relative;
  216. z-index: 10000;
  217. }
  218. .close-icon {
  219. position: absolute;
  220. top: 20rpx;
  221. right: 20rpx;
  222. width: 50rpx;
  223. height: 50rpx;
  224. display: flex;
  225. align-items: center;
  226. justify-content: center;
  227. cursor: pointer;
  228. z-index: 10001;
  229. &:active {
  230. opacity: 0.6;
  231. }
  232. }
  233. .login-btn {
  234. width: 100%;
  235. height: 88rpx;
  236. background-color: #FF5B05;
  237. color: white;
  238. border: none;
  239. border-radius: 44rpx;
  240. font-size: 32rpx;
  241. font-weight: 600;
  242. margin-bottom: 40rpx;
  243. }
  244. .login-btn:after {
  245. border: none;
  246. }
  247. .agreement-wrapper {
  248. display: flex;
  249. align-items: flex-start;
  250. justify-content: center;
  251. gap: 16rpx;
  252. padding: 0 20rpx;
  253. }
  254. .checkbox-wrapper {
  255. flex-shrink: 0;
  256. padding-top: 4rpx;
  257. }
  258. .zen-checkbox {
  259. width: 36rpx;
  260. height: 36rpx;
  261. border: 2rpx solid rgba(255, 91, 5, 0.4);
  262. border-radius: 18rpx;
  263. display: flex;
  264. align-items: center;
  265. justify-content: center;
  266. transition: all 0.3s ease;
  267. background: rgba(255, 255, 255, 0.9);
  268. &.checked {
  269. background: #FF5B05;
  270. border-color: #FF5B05;
  271. }
  272. .zen-circle-inner {
  273. width: 16rpx;
  274. height: 16rpx;
  275. border-radius: 50%;
  276. background: #fff;
  277. }
  278. }
  279. .agreement-text {
  280. font-size: 24rpx;
  281. color: rgba(41, 44, 53, 0.6);
  282. line-height: 36rpx;
  283. flex: 1;
  284. text-align: left;
  285. }
  286. .agreement-link {
  287. color: #FF5B05;
  288. text-decoration: none;
  289. }
  290. </style>