login.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. "use strict";
  2. const common_vendor = require("../../common/vendor.js");
  3. const utils_auth = require("../../utils/auth.js");
  4. const utils_api = require("../../utils/api.js");
  5. const UserInfoPopup = () => "../../components/UserInfoPopup.js";
  6. const _sfc_main = {
  7. components: {
  8. UserInfoPopup
  9. },
  10. data() {
  11. return {
  12. showOneClickLogin: true,
  13. // 是否显示一键登录按钮
  14. agreedToTerms: false,
  15. // 是否同意协议
  16. loginCode: "",
  17. // 微信登录code
  18. tempUserData: null
  19. // 临时存储的用户数据
  20. };
  21. },
  22. methods: {
  23. /**
  24. * 返回上一页
  25. */
  26. handleBack() {
  27. const pages = getCurrentPages();
  28. if (pages.length > 1) {
  29. common_vendor.index.navigateBack();
  30. } else {
  31. common_vendor.index.switchTab({
  32. url: "/pages/index/index"
  33. });
  34. }
  35. },
  36. /**
  37. * 第一步:微信一键登录(老用户静默登录)
  38. */
  39. async handleWxLogin() {
  40. if (!this.agreedToTerms) {
  41. common_vendor.index.showToast({
  42. title: "请先阅读并同意用户协议",
  43. icon: "none",
  44. duration: 2e3
  45. });
  46. return;
  47. }
  48. try {
  49. common_vendor.index.showLoading({ title: "登录中..." });
  50. const loginRes = await this.wxLoginAsync();
  51. this.loginCode = loginRes.code;
  52. console.log("[登录] 获取到微信code:", this.loginCode);
  53. const result = await utils_auth.wxSilentLogin(this.loginCode);
  54. common_vendor.index.hideLoading();
  55. if (result && result.isSign === "true") {
  56. console.log("[登录] 老用户登录成功");
  57. this.handleLoginSuccess();
  58. } else if (result && result.isSign === "false") {
  59. console.log("[登录] 新用户,需要授权手机号");
  60. this.showOneClickLogin = false;
  61. } else if (result && result.code === 103) {
  62. common_vendor.index.showModal({
  63. title: "账号异常",
  64. content: "您的账号已被禁用,如有疑问请联系客服",
  65. showCancel: false
  66. });
  67. } else {
  68. throw new Error("登录接口返回数据异常");
  69. }
  70. } catch (error) {
  71. common_vendor.index.hideLoading();
  72. console.error("[登录] 微信登录失败:", error);
  73. common_vendor.index.showToast({
  74. title: error.message || "登录失败,请重试",
  75. icon: "none",
  76. duration: 2e3
  77. });
  78. }
  79. },
  80. /**
  81. * 第二步:获取手机号授权(新用户)
  82. */
  83. async handleGetPhoneNumber(e) {
  84. console.log("[登录] 手机号授权回调:", e);
  85. if (e.detail.errMsg !== "getPhoneNumber:ok") {
  86. if (e.detail.errMsg.includes("no permission")) {
  87. common_vendor.index.showModal({
  88. title: "权限不足",
  89. content: '获取手机号功能需要:\n1. 小程序企业认证\n2. 开通"手机号快速验证组件"权限\n3. 在真机上测试',
  90. showCancel: false
  91. });
  92. } else {
  93. common_vendor.index.showToast({
  94. title: "授权失败,请重试",
  95. icon: "none"
  96. });
  97. }
  98. return;
  99. }
  100. try {
  101. common_vendor.index.showLoading({ title: "验证中..." });
  102. const loginRes = await this.wxLoginAsync();
  103. const newLoginCode = loginRes.code;
  104. const params = {
  105. loginCode: newLoginCode,
  106. phoneCode: e.detail.code,
  107. encryptedData: e.detail.encryptedData,
  108. iv: e.detail.iv
  109. };
  110. console.log("[登录] 发送手机号验证请求");
  111. const result = await utils_auth.wxPhoneLogin(params);
  112. common_vendor.index.hideLoading();
  113. if (result && result.isSign === "false") {
  114. console.log("[登录] 需要完善用户信息");
  115. this.tempUserData = result;
  116. this.$refs.userInfoPopup.open(result);
  117. } else if (result && result.isSign === "true") {
  118. console.log("[登录] 已注册用户,登录成功");
  119. this.handleLoginSuccess();
  120. } else {
  121. throw new Error("验证接口返回数据异常");
  122. }
  123. } catch (error) {
  124. common_vendor.index.hideLoading();
  125. console.error("[登录] 手机号验证失败:", error);
  126. let errorMsg = "验证失败,请重试";
  127. if (error.message) {
  128. if (error.message.includes("48001") || error.message.includes("未开通手机号快速验证组件")) {
  129. common_vendor.index.showModal({
  130. title: "权限未开通",
  131. content: '小程序未开通"手机号快速验证组件"权限\n\n请前往微信公众平台:\n开发 → 开发管理 → 接口设置\n开通"手机号快速验证组件"',
  132. showCancel: false
  133. });
  134. return;
  135. } else if (error.message.includes("40029") || error.message.includes("code无效")) {
  136. errorMsg = "phoneCode已失效,请重新授权";
  137. } else if (error.message.includes("40001") || error.message.includes("access_token")) {
  138. errorMsg = "access_token无效,请重试";
  139. } else if (error.message.includes("45011")) {
  140. errorMsg = "操作过于频繁,请稍后重试";
  141. } else {
  142. errorMsg = error.message;
  143. }
  144. }
  145. common_vendor.index.showToast({
  146. title: errorMsg,
  147. icon: "none",
  148. duration: 3e3
  149. });
  150. }
  151. },
  152. /**
  153. * 第三步:完善用户信息(首次登录)
  154. */
  155. async handleUserInfoConfirm(userInfo) {
  156. try {
  157. common_vendor.index.showLoading({ title: "注册中..." });
  158. const completeInfo = {
  159. openid: this.tempUserData.openid,
  160. unionid: this.tempUserData.unionid,
  161. phoneNumber: this.tempUserData.phoneNumber,
  162. nickname: userInfo.nickname,
  163. avatarUrl: userInfo.avatarUrl
  164. // 微信临时URL,先存着
  165. };
  166. console.log("[登录] 提交用户信息");
  167. await utils_auth.wxCompleteUserInfo(completeInfo);
  168. if (userInfo.tempAvatarPath) {
  169. console.log("[登录] 开始上传头像到服务器, tempAvatarPath:", userInfo.tempAvatarPath);
  170. try {
  171. const uploadedUrl = await this.uploadAvatarWithToken(userInfo.tempAvatarPath);
  172. console.log("[登录] 头像上传成功, uploadedUrl:", uploadedUrl);
  173. if (uploadedUrl) {
  174. console.log("[登录] 调用updateUserProfile更新头像");
  175. const updateResult = await utils_api.updateUserProfile({ avatar: uploadedUrl });
  176. console.log("[登录] updateUserProfile返回:", updateResult);
  177. const userInfoLocal = common_vendor.index.getStorageSync("user_info");
  178. if (userInfoLocal) {
  179. const parsed = JSON.parse(userInfoLocal);
  180. parsed.avatar = uploadedUrl;
  181. common_vendor.index.setStorageSync("user_info", JSON.stringify(parsed));
  182. }
  183. console.log("[登录] 头像上传成功:", uploadedUrl);
  184. }
  185. } catch (uploadErr) {
  186. console.warn("[登录] 头像上传失败:", uploadErr);
  187. }
  188. }
  189. common_vendor.index.hideLoading();
  190. console.log("[登录] 注册成功");
  191. this.handleLoginSuccess();
  192. } catch (error) {
  193. common_vendor.index.hideLoading();
  194. console.error("[登录] 注册失败:", error);
  195. common_vendor.index.showToast({
  196. title: error.message || "注册失败,请重试",
  197. icon: "none",
  198. duration: 2e3
  199. });
  200. }
  201. },
  202. /**
  203. * 带token上传头像
  204. */
  205. uploadAvatarWithToken(filePath) {
  206. return new Promise((resolve, reject) => {
  207. common_vendor.index.uploadFile({
  208. url: utils_api.uploadFile.url,
  209. filePath,
  210. name: "file",
  211. header: {
  212. "Authorization": `Bearer ${common_vendor.index.getStorageSync("user_token") || ""}`
  213. },
  214. success: (res) => {
  215. const data = JSON.parse(res.data);
  216. if (data.code === 200 && data.data && data.data.url) {
  217. resolve(data.data.url);
  218. } else {
  219. reject(new Error(data.message || "上传失败"));
  220. }
  221. },
  222. fail: (err) => {
  223. reject(err);
  224. }
  225. });
  226. });
  227. },
  228. /**
  229. * 登录成功后的处理
  230. */
  231. handleLoginSuccess() {
  232. common_vendor.index.showToast({
  233. title: "登录成功",
  234. icon: "success",
  235. duration: 1500
  236. });
  237. setTimeout(() => {
  238. const pages = getCurrentPages();
  239. if (pages.length > 1) {
  240. common_vendor.index.navigateBack();
  241. } else {
  242. common_vendor.index.switchTab({
  243. url: "/pages/index/index"
  244. });
  245. }
  246. }, 1500);
  247. },
  248. /**
  249. * 协议勾选变化
  250. */
  251. handleAgreementChange(e) {
  252. this.agreedToTerms = e.detail.value.length > 0;
  253. },
  254. /**
  255. * 显示协议内容
  256. */
  257. showAgreement(type) {
  258. const title = type === "user" ? "用户协议" : "隐私政策";
  259. common_vendor.index.showModal({
  260. title,
  261. content: "这里显示协议内容...",
  262. showCancel: false
  263. });
  264. },
  265. /**
  266. * 封装 wx.login 为 Promise
  267. */
  268. wxLoginAsync() {
  269. return new Promise((resolve, reject) => {
  270. common_vendor.index.login({
  271. provider: "weixin",
  272. success: (res) => {
  273. resolve(res);
  274. },
  275. fail: (err) => {
  276. reject(err);
  277. }
  278. });
  279. });
  280. }
  281. }
  282. };
  283. if (!Array) {
  284. const _component_user_info_popup = common_vendor.resolveComponent("user-info-popup");
  285. _component_user_info_popup();
  286. }
  287. function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
  288. return common_vendor.e({
  289. a: common_vendor.o((...args) => $options.handleBack && $options.handleBack(...args)),
  290. b: $data.showOneClickLogin
  291. }, $data.showOneClickLogin ? {
  292. c: common_vendor.o((...args) => $options.handleWxLogin && $options.handleWxLogin(...args))
  293. } : {
  294. d: common_vendor.o((...args) => $options.handleGetPhoneNumber && $options.handleGetPhoneNumber(...args))
  295. }, {
  296. e: $data.agreedToTerms,
  297. f: common_vendor.o(($event) => $options.showAgreement("user")),
  298. g: common_vendor.o(($event) => $options.showAgreement("privacy")),
  299. h: common_vendor.o((...args) => $options.handleAgreementChange && $options.handleAgreementChange(...args)),
  300. i: common_vendor.sr("userInfoPopup", "cdfe2409-0"),
  301. j: common_vendor.o($options.handleUserInfoConfirm)
  302. });
  303. }
  304. const MiniProgramPage = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-cdfe2409"], ["__file", "D:/program/gupiao-wx/src/pages/login/login.vue"]]);
  305. wx.createPage(MiniProgramPage);