login.js 12 KB

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