login.js 12 KB

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