index.js 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. let Parse = getApp().Parse;
  2. let company = getApp().globalData.company
  3. const request = require("../../utils/request");
  4. const qiniuUploader = require("../../utils/qiniuUploader");
  5. //获取应用实例
  6. const app = getApp()
  7. const real = require('../../utils/real')
  8. var timer
  9. function normalizeCnMobile(value) {
  10. if (value === null || value === undefined) return '';
  11. const digits = String(value).replace(/\D/g, '');
  12. if (!digits) return '';
  13. const last11 = digits.length >= 11 ? digits.slice(-11) : digits;
  14. if (/^1\d{10}$/.test(last11)) return last11;
  15. if (/^1\d{10}$/.test(digits)) return digits;
  16. return '';
  17. }
  18. function addCnMobileToSet(set, value) {
  19. const m = normalizeCnMobile(value);
  20. if (m) set.add(m);
  21. }
  22. async function localRejudgeIfNeeded() {
  23. try {
  24. const pending = wx.getStorageSync('traffic_deduct_pending');
  25. if (!pending) return;
  26. const storeId = pending && pending.storeId ? pending.storeId : null;
  27. const currentUser = Parse.User.current();
  28. const numsSet = new Set();
  29. addCnMobileToSet(numsSet, currentUser && currentUser.get('mobile'));
  30. addCnMobileToSet(numsSet, currentUser && currentUser.get('phone'));
  31. try { addCnMobileToSet(numsSet, wx.getStorageSync('user_mobile')); } catch (e) {}
  32. const uniqUserNumbers = Array.from(numsSet);
  33. console.log('🚦 [traffic] 授权页本地复判开始:', {
  34. storeId,
  35. userId: currentUser?.id,
  36. userNumbers: uniqUserNumbers
  37. });
  38. if (!storeId || !uniqUserNumbers.length) {
  39. console.log('🚦 [traffic] 本地复判条件不足,退出');
  40. return;
  41. }
  42. const phones = new Set();
  43. const mobiles = new Set();
  44. const tryPartnerQueries = [
  45. { key: 'storeId', value: storeId },
  46. { key: 'store', value: { __type: 'Pointer', className: 'ShopStore', objectId: storeId } },
  47. { key: 'shopStore', value: { __type: 'Pointer', className: 'ShopStore', objectId: storeId } }
  48. ];
  49. for (const item of tryPartnerQueries) {
  50. try {
  51. const q = new Parse.Query('Partner');
  52. q.equalTo(item.key, item.value);
  53. q.limit(200);
  54. q.select('phone', 'mobile', 'name');
  55. const list = await q.find();
  56. if (list && list.length) {
  57. list.forEach((p) => {
  58. addCnMobileToSet(phones, p.get('phone'));
  59. addCnMobileToSet(mobiles, p.get('mobile'));
  60. });
  61. }
  62. } catch (e) {}
  63. }
  64. console.log('🚦 [traffic] 授权页本地复判 Partner 集合:', {
  65. phoneCount: phones.size,
  66. mobileCount: mobiles.size
  67. });
  68. const phoneHit = uniqUserNumbers.some((n) => phones.has(n));
  69. const mobileHit = uniqUserNumbers.some((n) => mobiles.has(n));
  70. if (phoneHit || mobileHit) {
  71. console.log('🚦 [traffic] 授权页本地复判命中:', {
  72. reason: phoneHit ? 'partner_phone_match' : 'partner_mobile_match',
  73. matches: uniqUserNumbers.filter((n) => phoneHit ? phones.has(n) : mobiles.has(n))
  74. });
  75. try {
  76. currentUser.set('trafficExempt', true);
  77. currentUser.set('trafficExemptAt', new Date());
  78. currentUser.set('trafficExemptStoreId', storeId);
  79. await currentUser.save();
  80. } catch (e) {}
  81. wx.removeStorageSync('traffic_deduct_pending');
  82. } else {
  83. console.log('🚦 [traffic] 授权页本地复判未命中,等待首页复判继续');
  84. }
  85. } catch (e) {
  86. console.warn('🚦 [traffic] 授权页本地复判失败:', e?.message || e);
  87. }
  88. }
  89. /**
  90. * @class NovaAppAuth
  91. * @memberof module:components
  92. * @tutorial userauth
  93. * @desc 通用登录组件
  94. * # 登录组件相关数据表
  95. * - _User
  96. */
  97. Page({
  98. data: {
  99. phoneModal: false, //短信验证码登录弹窗
  100. logo: "https://file-cloud.fmode.cn/MldI5PBNt7/20210928/g0k1jb034826.png",
  101. name: "未来商城",
  102. desc: "江西脑控科技有限公司是国内领先的IT技术企业。专注于互联网+服务,面向政府提供区块链、大数据、物联网、人工智能解决方案",
  103. wxModel: false,
  104. avatarUrl: '',
  105. avatar: '',
  106. nickname: '',
  107. check: false,
  108. mobile: '', //手机号
  109. verilyCode: '', //验证码
  110. s: 0, //获取验证码倒计时 秒/s
  111. countDown: false,
  112. avatarKey: Date.now(), // 用于强制刷新头像显示
  113. isProcessingAuth: false, // 标记是否正在处理授权流程
  114. },
  115. onLoad: async function (options) {
  116. let Company = new Parse.Query('Company')
  117. Company.equalTo("objectId", company)
  118. let currentCompany = await Company.first()
  119. if (currentCompany && currentCompany.id) {
  120. this.setData({
  121. logo: currentCompany.get('logo'),
  122. name: currentCompany.get('name'),
  123. desc: currentCompany.get('desc')
  124. })
  125. }
  126. this.getUptoken()
  127. this.getAgreement() //用户协议
  128. },
  129. async getUptoken() {
  130. let res = await Parse.Cloud.run('qiniu_uptoken', {
  131. company: company
  132. })
  133. this.setData({
  134. uptokenURL: res.uptoken,
  135. domain: res.domain,
  136. uploadURL: res.zoneUrl
  137. })
  138. },
  139. async getAgreement() {
  140. let query = new Parse.Query('ContractAgreement')
  141. query.equalTo('type', 'wxapp')
  142. query.equalTo('company', company)
  143. query.select('title', 'content')
  144. let res = await query.first()
  145. if (res?.id) {
  146. this.setData({
  147. agreement: res.toJSON()
  148. })
  149. }
  150. },
  151. /* 是否同意授权协议 */
  152. getAgreementAuth() {
  153. if (!this.data.check && this.data.agreement) {
  154. wx.showModal({
  155. title: '提示',
  156. content: `请您仔细阅读并充分理解相关条款,点击同意即代表已阅读并同意《${this.data.agreement.title || '用户隐私协议'}》`,
  157. showCancel: true,
  158. cancelText: '取消',
  159. cancelColor: '#000000',
  160. confirmText: '同意',
  161. confirmColor: '#3CC51F',
  162. success: (result) => {
  163. if (result.confirm) {
  164. this.setData({
  165. check: true,
  166. })
  167. // this.getUserProfile()
  168. }
  169. },
  170. fail: () => { },
  171. complete: () => { }
  172. });
  173. return
  174. }
  175. // this.getUserProfile()
  176. return true
  177. },
  178. /* 判断是否绑定手机号 */
  179. async getUserProfile() {
  180. // 检查用户是否已登录
  181. let currentUser = Parse.User.current();
  182. if (!currentUser?.id) {
  183. console.log('⚠️ 用户未登录,需要先登录');
  184. // 不要在这里调用 checkAuth(true),因为它会触发微信官方的强制授权弹窗
  185. // 应该在外层(index.js)处理登录逻辑
  186. return false;
  187. }
  188. /* 如果手机号存在,已注册过判断是否上传过头像昵称信息 */
  189. if (currentUser?.get('mobile')) {
  190. wx.setStorageSync("userLogin", currentUser.id);
  191. // 检查是否需要完善信息(可选)
  192. // 如果用户没有昵称或昵称是默认的,提示完善信息
  193. const needProfile = !currentUser.get('nickname') ||
  194. currentUser.get('nickname') === '微信用户' ||
  195. currentUser.get('nickname') === '';
  196. if (needProfile) {
  197. // 显示自定义的完善信息弹窗(允许跳过)
  198. console.log('ℹ️ 显示自定义头像昵称弹窗');
  199. this.setData({
  200. wxModel: true
  201. });
  202. return false;
  203. }
  204. // 用户信息完整,返回上一页
  205. this.backLoad();
  206. return false;
  207. }
  208. // 用户已登录但没有手机号,允许继续
  209. return true;
  210. },
  211. /* 短信验证码登录弹窗 */
  212. async showDialogBtn() {
  213. let auth = this.getAgreementAuth()
  214. if (!auth) return
  215. let userProfile = await this.getUserProfile()
  216. if (!userProfile) return
  217. // 标记正在处理授权
  218. this.setData({
  219. isProcessingAuth: true,
  220. phoneModal: true
  221. })
  222. },
  223. async getPhoneNumber(e) {
  224. let auth = this.getAgreementAuth()
  225. if (!auth) return
  226. let userProfile = await this.getUserProfile()
  227. if (!userProfile) return
  228. // 标记正在处理授权
  229. this.setData({
  230. isProcessingAuth: true
  231. })
  232. let {
  233. code
  234. } = e.detail
  235. console.log(code);
  236. let phoneNumber = await request.getPhone(code)
  237. if(phoneNumber) this.authMobileUser(phoneNumber)
  238. },
  239. //合并User与绑定手机号逻辑
  240. async authMobileUser(mobile) {
  241. let currentUser = Parse.User.current()
  242. let queryMobUser = new Parse.Query('_User')
  243. queryMobUser.equalTo('mobile', mobile)
  244. queryMobUser.equalTo('company', company)
  245. queryMobUser.notEqualTo('type', 'admin')
  246. queryMobUser.notEqualTo('isDeleted', true)
  247. // queryMobUser.exists(`wxapp.${getApp().globalData.appid}`)
  248. let resMobUser = await queryMobUser.first()
  249. if (resMobUser?.id) {
  250. //请求User合并API,获取token重新登录,再更新昵称头像信息等
  251. let token = await this.getUpdateUserToken(currentUser.id, resMobUser.id)
  252. console.log(token);
  253. if (token) {
  254. Parse.User.become(token).then(async user => {
  255. // let user = Parse.User.current();
  256. wx.setStorageSync("userLogin", user.id);
  257. try {
  258. wx.setStorageSync('user_mobile', user.get('mobile') || '');
  259. } catch (e) {}
  260. try {
  261. if (typeof getApp().checkTrafficDeductPending === 'function') {
  262. console.log('🚦 [traffic] 触发暂缓扣减复判(merge-become 成功后)');
  263. await getApp().checkTrafficDeductPending();
  264. } else {
  265. await localRejudgeIfNeeded();
  266. }
  267. } catch (e) {
  268. console.warn('🚦 [traffic] 暂缓扣减复判触发失败:', e?.message || e);
  269. }
  270. try {
  271. if (typeof getApp().checkAndRecordPendingScan === 'function') {
  272. await getApp().checkAndRecordPendingScan();
  273. }
  274. } catch (e) {}
  275. if (!user.get('avatar') || user.get('nickname') == '微信用户' || !user.get('nickname')) {
  276. this.setData({
  277. phoneModal: false,
  278. wxModel: true
  279. })
  280. return
  281. }
  282. this.backLoad()
  283. return
  284. })
  285. .catch(async err => {
  286. console.log('❌ Parse.User.become 失败:', err);
  287. // 不要直接退出,而是尝试重新登录
  288. wx.showModal({
  289. title: '提示',
  290. content: '登录状态异常,是否重新登录?',
  291. showCancel: true,
  292. cancelText: '取消',
  293. confirmText: '重新登录',
  294. success: async (result) => {
  295. if (result.confirm) {
  296. // 清除登录状态
  297. wx.removeStorageSync("sessionToken");
  298. wx.removeStorageSync("userLogin");
  299. try {
  300. // 尝试重新登录
  301. await Parse.User.logOut();
  302. await getApp().checkAuth(true);
  303. // 重新获取用户信息
  304. const currentUser = Parse.User.current();
  305. if (currentUser && currentUser.get('mobile')) {
  306. wx.setStorageSync("userLogin", currentUser.id);
  307. this.backLoad();
  308. }
  309. } catch (reloginErr) {
  310. console.error('❌ 重新登录失败:', reloginErr);
  311. wx.showToast({
  312. title: '登录失败,请稍后重试',
  313. icon: 'none'
  314. });
  315. }
  316. } else {
  317. // 用户取消,返回上一页
  318. wx.navigateBack();
  319. }
  320. },
  321. });
  322. return
  323. })
  324. return
  325. }
  326. return
  327. }
  328. currentUser.set('mobile', mobile)
  329. await currentUser.save()
  330. try {
  331. wx.setStorageSync('user_mobile', mobile || '');
  332. if (typeof getApp().checkTrafficDeductPending === 'function') {
  333. console.log('🚦 [traffic] 触发暂缓扣减复判(绑定手机号后)');
  334. await getApp().checkTrafficDeductPending();
  335. } else {
  336. await localRejudgeIfNeeded();
  337. }
  338. } catch (e) {
  339. console.warn('🚦 [traffic] 暂缓扣减复判触发失败:', e?.message || e);
  340. }
  341. try {
  342. if (typeof getApp().checkAndRecordPendingScan === 'function') {
  343. await getApp().checkAndRecordPendingScan();
  344. }
  345. } catch (e) {}
  346. if (!currentUser.get('avatar') || currentUser.get('nickname') == '微信用户' || !currentUser.get('nickname')) {
  347. this.setData({
  348. phoneModal: false,
  349. wxModel: true
  350. })
  351. return
  352. }
  353. this.backLoad()
  354. return
  355. },
  356. async getUpdateUserToken(oldUser, newUserId) {
  357. return new Promise((resolve, reject) => {
  358. wx.login({
  359. success: function (res) {
  360. let parms = {
  361. oldUserId: oldUser,
  362. newUserId: newUserId,
  363. appId: getApp().globalData.appid,
  364. code: res.code,
  365. companyId: company,
  366. appType: getApp().globalData.appType || ''
  367. }
  368. if (res.code) {
  369. let url = 'https://server.fmode.cn/api/wxapp/combine/user'
  370. wx.request({
  371. url: url,
  372. data: parms,
  373. header: { 'content-type': 'application/json' },
  374. method: 'POST',
  375. async success(res) {
  376. console.log(res);
  377. let data = res.data
  378. if (data.code == 200) {
  379. wx.setStorageSync("sessionToken", data.data.token);
  380. // 用户合并成功后,更新 ScanRecord 表中的 user 字段
  381. console.log('🔄 [用户合并] 开始更新 ScanRecord 中的用户关联');
  382. console.log(' - 旧用户ID:', oldUser);
  383. console.log(' - 新用户ID:', newUserId);
  384. try {
  385. // 使用 Parse.User.become 切换到新用户
  386. await Parse.User.become(data.data.token);
  387. // 查询所有旧用户的扫码记录
  388. const ScanRecord = Parse.Object.extend('ScanRecord');
  389. const scanQuery = new Parse.Query(ScanRecord);
  390. scanQuery.equalTo('user', {
  391. __type: 'Pointer',
  392. className: '_User',
  393. objectId: oldUser
  394. });
  395. scanQuery.limit(1000); // 设置查询上限
  396. const oldRecords = await scanQuery.find();
  397. console.log(`📋 [查询结果] 找到 ${oldRecords.length} 条旧用户的扫码记录`);
  398. if (oldRecords.length > 0) {
  399. // 批量更新所有记录,将 user 指向新用户
  400. const updatePromises = oldRecords.map(record => {
  401. record.set('user', {
  402. __type: 'Pointer',
  403. className: '_User',
  404. objectId: newUserId
  405. });
  406. return record.save();
  407. });
  408. await Promise.all(updatePromises);
  409. console.log(`✅ [更新成功] 已将 ${oldRecords.length} 条扫码记录的用户更新为新用户`);
  410. } else {
  411. console.log('ℹ️ [无需更新] 旧用户没有扫码记录');
  412. }
  413. } catch (updateError) {
  414. console.error('❌ [更新失败] 更新 ScanRecord 失败:', updateError);
  415. console.error(' - 错误信息:', updateError.message);
  416. // 不影响主流程,继续返回 token
  417. }
  418. resolve(data.data.token)
  419. } else {
  420. console.log(data?.mess);
  421. wx.showModal({
  422. title: '提示',
  423. content: data?.mess,
  424. showCancel: false,
  425. cancelText: '取消',
  426. cancelColor: '#000000',
  427. confirmText: '确定',
  428. confirmColor: '#3CC51F',
  429. success: (result) => {
  430. if (result.confirm) {
  431. }
  432. },
  433. fail: () => { },
  434. complete: () => { }
  435. });
  436. resolve()
  437. }
  438. },
  439. });
  440. }
  441. },
  442. fail: function (err) {
  443. wx.showModal({
  444. title: '提示',
  445. content: '登录超时,请稍后重试',
  446. showCancel: false,
  447. cancelText: '取消',
  448. cancelColor: '#000000',
  449. confirmText: '确定',
  450. confirmColor: '#3CC51F',
  451. success: (result) => {
  452. if (result.confirm) {
  453. }
  454. },
  455. fail: () => { },
  456. complete: () => { }
  457. });
  458. console.warn('小程序wx.login失败');
  459. resolve()
  460. }
  461. });
  462. })
  463. },
  464. //获取验证码
  465. async getPhoneCode() {
  466. let { mobile, s, countDown } = this.data
  467. if (!real.isPoneAvailable(mobile)) {
  468. wx.showToast({
  469. title: '手机号格式有误',
  470. icon: 'error',
  471. duration: 1500,
  472. mask: false,
  473. });
  474. return
  475. }
  476. if(countDown || s > 0) return
  477. this.setData({
  478. countDown:true
  479. })
  480. let parsm = {
  481. company: company,
  482. mobile: mobile
  483. }
  484. let code = await this.getRequest(parsm, 'message')
  485. if(code?.code == 1){
  486. this.setData({
  487. s:60
  488. })
  489. this.decrementTime()
  490. }else{
  491. wx.showToast({
  492. title: '验证码获取失败',
  493. icon: 'error',
  494. image: '',
  495. duration: 1500,
  496. mask: false,
  497. });
  498. this.setData({
  499. countDown:false
  500. })
  501. }
  502. },
  503. //接口请求
  504. getRequest(parsm, apig) {
  505. return new Promise((resolve, rej) => {
  506. let url = 'https://server.fmode.cn/api/apig/'
  507. wx.request({
  508. url: url + apig,
  509. data: parsm,
  510. header: { 'content-type': 'application/json' },
  511. method: 'POST',
  512. dataType: 'json',
  513. responseType: 'text',
  514. success: (result) => {
  515. console.log(result);
  516. resolve(result.data)
  517. },
  518. fail: () => {
  519. resolve(false)
  520. },
  521. complete: () => { }
  522. });
  523. })
  524. },
  525. // 倒计时
  526. decrementTime(){
  527. timer = setTimeout(() => {
  528. let { s } = this.data
  529. if(s == 0){
  530. this.setData({ countDown:false })
  531. timer && clearTimeout(timer)
  532. return
  533. }
  534. s--
  535. this.setData({
  536. s:s
  537. })
  538. this.decrementTime()
  539. }, 1000);
  540. },
  541. //验证码绑定手机号登录
  542. async completePhone() {
  543. let { mobile, verilyCode } = this.data
  544. if(!real.isPoneAvailable(mobile) || !verilyCode.toString().trim()){
  545. wx.showToast({
  546. title: '手机号或验证码不正确',
  547. icon: 'none',
  548. image: '',
  549. duration: 1500,
  550. mask: false,
  551. });
  552. return
  553. }
  554. // 标记正在处理授权
  555. this.setData({
  556. isProcessingAuth: true
  557. })
  558. let parsm = {
  559. mobile: mobile,
  560. code:verilyCode
  561. }
  562. let isVerify = await this.getRequest(parsm, 'verifyCode')
  563. console.log(isVerify);
  564. if(isVerify.code == 200){
  565. this.authMobileUser(mobile.toString())
  566. }else{
  567. // 验证失败,重置标记
  568. this.setData({
  569. isProcessingAuth: false
  570. })
  571. wx.showToast({
  572. title:isVerify?.msg || '验证码错误',
  573. icon: 'none',
  574. image: '',
  575. duration: 1500,
  576. mask: false,
  577. });
  578. return
  579. }
  580. },
  581. //关闭手机号授权弹窗
  582. hideModal: function () {
  583. this.setData({
  584. phoneModal: false,
  585. isProcessingAuth: false // 重置标记
  586. })
  587. },
  588. backLoad() {
  589. console.log('===========================================');
  590. console.log('======= backLoad 方法调用 =======');
  591. let pages = getCurrentPages();
  592. console.log('当前页面栈层数:', pages.length);
  593. console.log('当前页面路由:', pages[pages.length - 1]?.route);
  594. // 检查是否有 returnUrl 参数(从 H5 页面传递过来)
  595. const currentPage = pages[pages.length - 1];
  596. const returnUrl = currentPage?.options?.returnUrl;
  597. if (returnUrl) {
  598. console.log('📍 检测到 returnUrl 参数:', returnUrl);
  599. // 如果是 web-view 页面,跳转回去
  600. const decodedUrl = decodeURIComponent(returnUrl);
  601. console.log('🔙 准备跳转回 H5 页面:', decodedUrl);
  602. let nextUrl = `/common-page/pages/web-view/index?path=${encodeURIComponent(decodedUrl)}`;
  603. const storeId = currentPage?.options?.storeId;
  604. const storeName = currentPage?.options?.storeName;
  605. if (storeId) {
  606. nextUrl += `&storeId=${storeId}`;
  607. }
  608. if (storeName) {
  609. nextUrl += `&storeName=${storeName}`;
  610. }
  611. wx.navigateTo({
  612. url: nextUrl,
  613. success: () => {
  614. console.log('✅ 跳转回 H5 页面成功');
  615. console.log('===========================================');
  616. },
  617. fail: (err) => {
  618. console.error('❌ 跳转回 H5 页面失败:', err);
  619. // 降级:正常返回上一页
  620. this.normalBackLoad();
  621. }
  622. });
  623. return;
  624. }
  625. // 没有 returnUrl,执行正常的返回逻辑
  626. this.normalBackLoad();
  627. },
  628. normalBackLoad() {
  629. let pages = getCurrentPages();
  630. // 如果页面栈只有1层或没有上一页,直接跳转到首页
  631. if (pages.length <= 1) {
  632. console.log('⚠️ 没有上一个页面,直接跳转到首页');
  633. this.goToHome();
  634. return;
  635. }
  636. // 有上一个页面
  637. let beforePage = pages[pages.length - 2];
  638. console.log('上一个页面路由:', beforePage.route);
  639. // 尝试调用上一个页面的 onLoad 方法(如果存在)
  640. if (beforePage && typeof beforePage.onLoad === 'function') {
  641. try {
  642. let options = beforePage.options || { isInit: true };
  643. beforePage.onLoad(options);
  644. console.log('✅ 已调用上一个页面的 onLoad');
  645. } catch (err) {
  646. console.warn('⚠️ 调用上一个页面 onLoad 失败:', err);
  647. }
  648. }
  649. // 返回上一页
  650. console.log('🔙 执行 navigateBack...');
  651. wx.navigateBack({
  652. delta: 1,
  653. success: () => {
  654. console.log('✅ navigateBack 成功');
  655. console.log('===========================================');
  656. },
  657. fail: (err) => {
  658. console.error('❌ navigateBack 失败:', err);
  659. console.log('⚠️ 尝试使用 reLaunch 跳转到首页');
  660. this.goToHome();
  661. }
  662. });
  663. },
  664. // 跳转到首页
  665. goToHome() {
  666. console.log('===========================================');
  667. console.log('======= goToHome 方法调用 =======');
  668. const app = getApp();
  669. console.log('globalData.rootPage:', app.globalData.rootPage);
  670. console.log('globalData.defaultTabBar:', app.globalData.defaultTabBar);
  671. // 尝试多个可能的首页路径
  672. let rootPage = app.globalData.rootPage
  673. || app.globalData.defaultTabBar?.list?.[0]?.pagePath
  674. || '/pages/index/index'
  675. || '/index/index';
  676. // 确保路径以 / 开头
  677. if (!rootPage.startsWith('/')) {
  678. rootPage = '/' + rootPage;
  679. }
  680. console.log('准备跳转到:', rootPage);
  681. wx.reLaunch({
  682. url: rootPage,
  683. success: () => {
  684. console.log('✅ reLaunch 成功');
  685. console.log('===========================================');
  686. },
  687. fail: (err) => {
  688. console.error('❌ reLaunch 失败:', err);
  689. console.log('⚠️ 尝试使用 switchTab');
  690. // 如果是 tabBar 页面,尝试使用 switchTab
  691. wx.switchTab({
  692. url: rootPage,
  693. success: () => {
  694. console.log('✅ switchTab 成功');
  695. console.log('===========================================');
  696. },
  697. fail: (err2) => {
  698. console.error('❌ switchTab 也失败:', err2);
  699. console.log('⚠️ 最后尝试:直接 navigateBack');
  700. // 最后的兜底:直接返回
  701. wx.navigateBack({
  702. delta: 1,
  703. fail: (err3) => {
  704. console.error('❌ 所有跳转方式都失败了:', err3);
  705. console.log('===========================================');
  706. // 显示错误提示
  707. wx.showModal({
  708. title: '提示',
  709. content: '页面跳转失败,请手动返回',
  710. showCancel: false
  711. });
  712. }
  713. });
  714. }
  715. });
  716. }
  717. });
  718. },
  719. goBack: function () {
  720. wx.navigateBack({
  721. delta: 1,
  722. })
  723. },
  724. //手动获取微信头像
  725. onChooseAvatar(e) {
  726. console.log('=== onChooseAvatar 触发 ===');
  727. console.log('事件对象:', e);
  728. const { avatarUrl } = e.detail;
  729. console.log('获取到的头像URL:', avatarUrl);
  730. console.log('头像URL类型:', typeof avatarUrl);
  731. console.log('头像URL长度:', avatarUrl ? avatarUrl.length : 0);
  732. if (avatarUrl) {
  733. // 微信头像URL可能是临时路径,需要先下载
  734. // 临时路径格式:http://tmp/xxx.jpg
  735. // 相机拍照路径格式:wxfile://tmp_xxx.jpg
  736. this.setData({
  737. avatarUrl: avatarUrl,
  738. avatarKey: Date.now() // 更新key强制刷新
  739. }, () => {
  740. console.log('✅ 头像URL已更新到data:', this.data.avatarUrl);
  741. console.log('✅ avatarKey已更新:', this.data.avatarKey);
  742. });
  743. } else {
  744. console.error('❌ 未获取到头像URL');
  745. wx.showToast({
  746. title: '开发工具不支持,请真机测试',
  747. icon: 'none',
  748. duration: 2000
  749. });
  750. }
  751. },
  752. //获取昵称
  753. onChangeName(e) {
  754. console.log('=== 昵称输入事件 ===');
  755. console.log('事件对象:', e);
  756. // 注意:type="nickname" 的 input 会在用户输入时自动更新 model:value
  757. // 这里只是记录日志,实际的值更新由 model:value 自动处理
  758. if (e.detail && e.detail.value !== undefined) {
  759. console.log('昵称值:', e.detail.value);
  760. }
  761. },
  762. //确定头像昵称
  763. async onComplete() {
  764. console.log('=== onComplete 方法被调用 ===');
  765. let {
  766. nickname,
  767. avatarUrl
  768. } = this.data
  769. const finalNickname = (nickname || '').toString().trim();
  770. console.log('昵称:', nickname);
  771. console.log('头像URL:', avatarUrl);
  772. let user = Parse.User.current();
  773. if (!user) {
  774. console.error('❌ 用户未登录,无法保存信息');
  775. wx.showToast({
  776. title: '用户未登录',
  777. icon: 'none'
  778. });
  779. return;
  780. }
  781. console.log('当前用户ID:', user.id);
  782. if (!finalNickname) {
  783. wx.showToast({
  784. title: '请输入微信昵称',
  785. icon: 'none'
  786. });
  787. return;
  788. }
  789. // 显示加载提示
  790. wx.showLoading({
  791. title: '保存中...',
  792. mask: true
  793. });
  794. try {
  795. if (avatarUrl) {
  796. console.log('📤 开始上传头像...');
  797. let avatar = await this.updataAvatar(avatarUrl);
  798. if (avatar) {
  799. user.set("avatar", avatar);
  800. console.log('✅ 头像上传成功:', avatar);
  801. } else {
  802. console.warn('⚠️ 头像上传失败');
  803. }
  804. }
  805. user.set("nickname", finalNickname);
  806. console.log('✅ 昵称已设置:', finalNickname);
  807. await user.save();
  808. console.log('✅ 用户信息保存成功');
  809. wx.hideLoading();
  810. // 关闭弹窗
  811. this.onClose();
  812. // 延迟一下再跳转,确保弹窗关闭动画完成
  813. setTimeout(() => {
  814. console.log('🚀 准备跳转...');
  815. try {
  816. const u = Parse.User.current();
  817. if (u && u.id) {
  818. wx.setStorageSync('userLogin', u.id);
  819. }
  820. } catch (e) {}
  821. this.backLoad();
  822. }, 300);
  823. } catch (err) {
  824. wx.hideLoading();
  825. console.error('❌ 保存用户信息失败:', err);
  826. wx.showModal({
  827. title: '提示',
  828. content: '保存失败,请重试',
  829. showCancel: false,
  830. confirmText: '重试'
  831. });
  832. }
  833. },
  834. //关闭头像昵称填写弹窗
  835. onClose() {
  836. this.setData({
  837. wxModel: false,
  838. isProcessingAuth: false // 重置标记
  839. })
  840. },
  841. //上传头像
  842. updataAvatar(url) {
  843. let that = this;
  844. return new Promise((resolve, rejcet) => {
  845. qiniuUploader.upload(
  846. url,
  847. async (res) => {
  848. let img = res.imageURL;
  849. resolve(img)
  850. },
  851. (error) => {
  852. console.log("error: " + error);
  853. resolve(false)
  854. }, {
  855. region: "SCN",
  856. uploadURL: that.data.uploadURL,
  857. domain: that.data.domain,
  858. uptoken: that.data.uptokenURL,
  859. }
  860. );
  861. })
  862. },
  863. //选择勾选用户协议
  864. onCheckAgreement() {
  865. this.setData({
  866. check: !this.data.check
  867. })
  868. },
  869. //附件下载
  870. openFile() {
  871. let {
  872. agreement
  873. } = this.data
  874. let url = agreement.content,
  875. name = agreement.title
  876. const _this = this;
  877. let rep = this.getFileType(url)
  878. console.log(url, name);
  879. wx.showLoading({
  880. title: '加载中',
  881. })
  882. wx.downloadFile({
  883. url: url, //要预览的PDF的地址
  884. filePath: wx.env.USER_DATA_PATH + `/${name}.${rep}`,
  885. success: function (res) {
  886. console.log(res);
  887. if (res.statusCode === 200) { //成功
  888. var Path = res.filePath //返回的文件临时地址,用于后面打开本地预览所用
  889. console.log(Path)
  890. wx.openDocument({
  891. filePath: Path, //要打开的文件路径
  892. showMenu: true,
  893. success: function (res) {
  894. wx.hideLoading()
  895. console.log(res, '打开PDF成功');
  896. },
  897. fail: function (res) {
  898. console.log(res)
  899. wx.hideLoading()
  900. }
  901. })
  902. }
  903. },
  904. fail: function (res) {
  905. wx.hideLoading()
  906. console.log(res); //失败
  907. },
  908. })
  909. },
  910. //解析文件类型
  911. getFileType(url) {
  912. let pdfReg = /^.+(\.pdf)$/
  913. let txtReg = /^.+(\.txt)$/
  914. let wordReg = /^.+(\.doc|\.docx)$/
  915. let excelReg = /^.+(\.xls|\.xlsx)$/
  916. let jpgPng = /^.+(\.png)$/
  917. let jpgJpg = /^.+(\.jpg)$/
  918. let jpgJpeg = /^.+(\.jpeg)$/
  919. if (pdfReg.test(url)) {
  920. return 'pdf'
  921. }
  922. if (txtReg.test(url)) {
  923. return 'txt'
  924. }
  925. if (wordReg.test(url)) {
  926. return 'docx'
  927. }
  928. if (excelReg.test(url)) {
  929. return 'xls'
  930. }
  931. if (jpgPng.test(url)) {
  932. return 'png'
  933. }
  934. if (jpgJpg.test(url)) {
  935. return 'jpg'
  936. }
  937. if (jpgJpeg.test(url)) {
  938. return 'jpeg'
  939. }
  940. },
  941. onShow: function () {
  942. console.log('=== onShow 方法被调用 ===');
  943. // 如果正在处理授权流程,不要自动返回
  944. if (this.data.isProcessingAuth) {
  945. console.log('ℹ️ 正在处理授权流程,不自动返回');
  946. return;
  947. }
  948. // 如果有弹窗显示(手机号弹窗或头像昵称弹窗),不要自动返回
  949. if (this.data.phoneModal || this.data.wxModel) {
  950. console.log('ℹ️ 有弹窗显示,不自动返回');
  951. return;
  952. }
  953. let userLogin = wx.getStorageSync('userLogin');
  954. let currentUser = Parse.User.current();
  955. console.log('userLogin 存储:', userLogin);
  956. console.log('当前用户:', currentUser ? currentUser.id : '无');
  957. console.log('手机号:', currentUser?.get('mobile') || '无');
  958. // 检查是否是从其他页面跳转过来的(有 returnUrl 参数)
  959. const pages = getCurrentPages();
  960. const currentPage = pages[pages.length - 1];
  961. const hasReturnUrl = currentPage?.options?.returnUrl;
  962. if (hasReturnUrl) {
  963. console.log('ℹ️ 检测到 returnUrl 参数,用户需要完成登录流程');
  964. // 不要自动返回,让用户完成登录
  965. return;
  966. }
  967. // 只有当用户已登录且有手机号,并且不是从其他页面跳转过来时才返回
  968. if (userLogin && currentUser && currentUser.get('mobile')) {
  969. console.log('✅ 用户已完整登录,返回上一页');
  970. wx.navigateBack({
  971. fail: () => {
  972. console.log('⚠️ 返回失败,可能是首页');
  973. }
  974. });
  975. } else {
  976. console.log('ℹ️ 用户未完整登录,显示授权页面');
  977. }
  978. },
  979. onReady: function () {
  980. },
  981. })