与牧同行-小程序用户端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

471 lines
12 KiB

  1. // pages/auth/real-name-auth.js
  2. import http from '../../../utils/api'
  3. Page({
  4. data: {
  5. // 表单数据
  6. name: '',
  7. idNumber: '',
  8. // 焦点状态
  9. nameFocus: false,
  10. idNumberFocus: false,
  11. // 错误提示
  12. nameError: '',
  13. idNumberError: '',
  14. // 提示显示控制
  15. showNameHint: false,
  16. showIdNumberHint: false,
  17. // 验证状态
  18. isNameValid: false,
  19. isIdNumberValid: false,
  20. // 协议状态
  21. agreed: false,
  22. // 提交状态
  23. canSubmit: false,
  24. isSubmitting: false,
  25. // 进度条
  26. currentStep: 1,
  27. lineProgress1: 0,
  28. lineProgress2: 0,
  29. // 弹窗数据
  30. showModal: false,
  31. modalTitle: '',
  32. modalContent: '',
  33. // 成功弹窗数据
  34. showSuccessModal: false,
  35. maskedIdNumber: '',
  36. // 错误弹窗数据
  37. showErrorModal: false,
  38. errorTitle: '',
  39. errorMessage: ''
  40. },
  41. onLoad() {
  42. // 页面加载时启动进度条动画
  43. setTimeout(() => {
  44. this.setData({ lineProgress1: 50 });
  45. }, 600);
  46. },
  47. // 姓名输入处理
  48. onNameInput(e) {
  49. const value = e.detail.value.trim();
  50. let error = '';
  51. let showHint = true;
  52. let isValid = false;
  53. if (value) {
  54. if (!/^[\u4e00-\u9fa5]{2,10}$/.test(value)) {
  55. error = '姓名应为2-10个汉字';
  56. } else {
  57. isValid = true;
  58. // 验证通过时保持提示显示,但无错误
  59. }
  60. } else {
  61. // 内容为空时显示正常提示
  62. error = '';
  63. }
  64. this.setData({
  65. name: value,
  66. nameError: error,
  67. showNameHint: showHint,
  68. isNameValid: isValid
  69. }, () => {
  70. this.checkForm();
  71. });
  72. },
  73. onNameFocus() {
  74. this.setData({
  75. nameFocus: true,
  76. showNameHint: true // 获得焦点时显示提示
  77. });
  78. },
  79. onNameBlur() {
  80. const { name } = this.data;
  81. this.setData({
  82. nameFocus: false,
  83. // 失去焦点时,如果内容为空或验证失败,保持提示显示
  84. showNameHint: !!(name && !this.data.isNameValid)
  85. });
  86. },
  87. // 清除姓名
  88. clearName() {
  89. this.setData({
  90. name: '',
  91. nameError: '',
  92. showNameHint: false,
  93. isNameValid: false
  94. }, () => {
  95. this.checkForm();
  96. });
  97. },
  98. // 身份证号输入处理
  99. onIdNumberInput(e) {
  100. const value = e.detail.value.trim().toUpperCase();
  101. let error = '';
  102. let showHint = true;
  103. let isValid = false;
  104. if (value) {
  105. if (value.length < 18) {
  106. error = '还需输入' + (18 - value.length) + '位';
  107. } else if (value.length === 18) {
  108. if (this.validateIdNumber(value)) {
  109. isValid = true;
  110. // 验证通过时保持提示显示,但无错误
  111. } else {
  112. error = '身份证号格式不正确';
  113. }
  114. }
  115. } else {
  116. // 内容为空时显示正常提示
  117. error = '';
  118. }
  119. this.setData({
  120. idNumber: value,
  121. idNumberError: error,
  122. showIdNumberHint: showHint,
  123. isIdNumberValid: isValid
  124. }, () => {
  125. this.checkForm();
  126. });
  127. },
  128. onIdNumberFocus() {
  129. this.setData({
  130. idNumberFocus: true,
  131. showIdNumberHint: true // 获得焦点时显示提示
  132. });
  133. },
  134. onIdNumberBlur() {
  135. const { idNumber } = this.data;
  136. let error = '';
  137. let isValid = false;
  138. // 最终验证
  139. if (idNumber) {
  140. if (idNumber.length < 18) {
  141. error = '还需输入' + (18 - idNumber.length) + '位';
  142. } else if (idNumber.length === 18) {
  143. if (this.validateIdNumber(idNumber)) {
  144. isValid = true;
  145. } else {
  146. error = '身份证号格式不正确';
  147. }
  148. }
  149. }
  150. this.setData({
  151. idNumberFocus: false,
  152. idNumberError: error,
  153. isIdNumberValid: isValid,
  154. // 失去焦点时,如果内容为空或验证失败,保持提示显示
  155. showIdNumberHint: !!(idNumber && !isValid)
  156. });
  157. },
  158. // 清除身份证号
  159. clearIdNumber() {
  160. this.setData({
  161. idNumber: '',
  162. idNumberError: '',
  163. showIdNumberHint: false,
  164. isIdNumberValid: false
  165. }, () => {
  166. this.checkForm();
  167. });
  168. },
  169. // 身份证验证函数
  170. validateIdNumber(idNumber) {
  171. // 基础格式验证
  172. if (!/^\d{17}[\dXx]$/.test(idNumber)) {
  173. return false;
  174. }
  175. // 地区码验证(简化的验证,实际应该更严谨)
  176. const areaCode = idNumber.substring(0, 2);
  177. const validAreaCodes = ['11', '12', '13', '14', '15', '21', '22', '23',
  178. '31', '32', '33', '34', '35', '36', '37', '41',
  179. '42', '43', '44', '45', '46', '50', '51', '52',
  180. '53', '54', '61', '62', '63', '64', '65'];
  181. if (!validAreaCodes.includes(areaCode)) {
  182. return false;
  183. }
  184. // 出生日期验证
  185. const year = parseInt(idNumber.substring(6, 10));
  186. const month = parseInt(idNumber.substring(10, 12));
  187. const day = parseInt(idNumber.substring(12, 14));
  188. const currentYear = new Date().getFullYear();
  189. if (year < 1900 || year > currentYear) return false;
  190. if (month < 1 || month > 12) return false;
  191. if (day < 1 || day > 31) return false;
  192. // 校验码验证
  193. const checkCode = idNumber.charAt(17).toUpperCase();
  194. const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
  195. const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
  196. let sum = 0;
  197. for (let i = 0; i < 17; i++) {
  198. sum += parseInt(idNumber.charAt(i)) * weights[i];
  199. }
  200. const mod = sum % 11;
  201. return checkCode === checkCodes[mod];
  202. },
  203. // 协议处理
  204. toggleAgreement() {
  205. const newAgreed = !this.data.agreed;
  206. this.setData({
  207. agreed: newAgreed
  208. }, () => {
  209. this.checkForm();
  210. });
  211. },
  212. // 显示协议弹窗
  213. showAgreementModal() {
  214. const title = '用户服务协议';
  215. const content = `欢迎您使用"与牧同行"服务!\n\n在使用我们的服务前,请您仔细阅读并同意以下协议:\n\n1. 服务条款\n您在使用与牧同行提供的各项服务时,应当遵守相关法律法规,不得从事任何非法行为。\n\n2. 用户责任\n您需要保证所提供信息的真实性、准确性和完整性。\n\n3. 服务变更\n我们保留随时修改、暂停或终止服务的权利。\n\n4. 免责声明\n在法律法规允许的最大范围内,我们对因使用服务而产生的任何间接损失不承担责任。\n\n5. 法律适用\n本协议的订立、执行和解释及争议的解决均适用中华人民共和国法律。\n\n请您确保已阅读并理解以上条款。`;
  216. this.showModal(title, content);
  217. },
  218. // 显示隐私弹窗
  219. showPrivacyModal() {
  220. const title = '隐私保护协议';
  221. const content = `我们非常重视您的隐私保护,请您仔细阅读以下隐私政策:\n\n1. 信息收集\n我们仅收集为您提供服务所必需的信息,包括姓名、身份证号等实名信息。\n\n2. 信息使用\n您的信息仅用于身份验证和服务提供,不会用于其他商业用途。\n\n3. 信息保护\n我们采用先进的安全技术保护您的信息,防止未经授权的访问、使用或泄露。\n\n4. 信息共享\n除非获得您的明确同意,我们不会向第三方共享您的个人信息。\n\n5. 您的权利\n您可以随时查看、更正或删除您的个人信息。\n\n6. 政策更新\n我们可能会不时更新本隐私政策,更新后的政策将在本页面公布。\n\n我们承诺严格遵守相关法律法规,保护您的个人信息安全。`;
  222. this.showModal(title, content);
  223. },
  224. // 显示弹窗
  225. showModal(title, content) {
  226. this.setData({
  227. modalTitle: title,
  228. modalContent: content,
  229. showModal: true
  230. });
  231. },
  232. // 关闭弹窗
  233. closeModal() {
  234. this.setData({ showModal: false });
  235. },
  236. // 显示错误弹窗
  237. showErrorModal(title, message) {
  238. this.setData({
  239. errorTitle: title || '认证失败',
  240. errorMessage: message || '请检查信息后重试',
  241. showErrorModal: true
  242. });
  243. },
  244. // 关闭错误弹窗
  245. closeErrorModal() {
  246. this.setData({
  247. showErrorModal: false,
  248. isSubmitting: false // 关闭错误弹窗时重置提交状态
  249. });
  250. },
  251. // 阻止事件冒泡
  252. stopPropagation(e) {
  253. // 阻止冒泡
  254. },
  255. // 检查表单
  256. checkForm() {
  257. const {
  258. isNameValid,
  259. isIdNumberValid,
  260. agreed
  261. } = this.data;
  262. const isValid = isNameValid &&
  263. isIdNumberValid &&
  264. agreed;
  265. this.setData({
  266. canSubmit: isValid
  267. });
  268. },
  269. // 提交认证
  270. async submitAuth() {
  271. if (!this.data.canSubmit || this.data.isSubmitting) return;
  272. // 最终验证
  273. const { name, idNumber } = this.data;
  274. if (!/^[\u4e00-\u9fa5]{2,10}$/.test(name)) {
  275. this.setData({
  276. nameError: '姓名应为2-10个汉字',
  277. showNameHint: true,
  278. canSubmit: false
  279. });
  280. return;
  281. }
  282. if (!this.validateIdNumber(idNumber)) {
  283. this.setData({
  284. idNumberError: '身份证号格式不正确',
  285. showIdNumberHint: true,
  286. canSubmit: false
  287. });
  288. return;
  289. }
  290. this.setData({
  291. isSubmitting: true,
  292. currentStep: 2,
  293. lineProgress1: 100,
  294. lineProgress2: 50
  295. });
  296. // 显示加载动画
  297. wx.showLoading({
  298. title: '正在验证...',
  299. mask: true
  300. });
  301. try {
  302. // 调用实名认证接口
  303. const result = await this.callRealNameApi(name, idNumber);
  304. wx.hideLoading();
  305. if (result.success) {
  306. // 处理身份证号脱敏显示
  307. const maskedId = idNumber.substring(0, 4) + '**********' + idNumber.substring(14);
  308. // 显示成功弹窗
  309. this.showSuccessModal(maskedId);
  310. this.setData({
  311. isSubmitting: false,
  312. currentStep: 3,
  313. lineProgress2: 100
  314. });
  315. } else {
  316. // 接口返回失败
  317. this.handleApiError(result);
  318. }
  319. } catch (error) {
  320. wx.hideLoading();
  321. this.setData({
  322. isSubmitting: false,
  323. currentStep: 1,
  324. lineProgress2: 0
  325. });
  326. this.showErrorModal('网络错误', '认证请求失败,请检查网络连接后重试');
  327. console.error('实名认证错误:', error);
  328. }
  329. },
  330. // 调用实名认证API
  331. async callRealNameApi(realName, idCard) {
  332. return new Promise((resolve, reject) => {
  333. http.realName({
  334. data: {
  335. realName: realName, // 姓名字段
  336. idCard: idCard // 身份证号字段
  337. },
  338. success: (res) => {
  339. console.log('API响应:', res);
  340. // 根据实际API响应结构调整
  341. if (res.code === 200 || res.code === 0) {
  342. resolve({
  343. success: true,
  344. data: res.data || {}
  345. });
  346. } else {
  347. resolve({
  348. success: false,
  349. code: res.code,
  350. message: res.msg || '认证失败'
  351. });
  352. }
  353. },
  354. fail: (err) => {
  355. reject(err);
  356. }
  357. });
  358. });
  359. },
  360. // 处理API错误
  361. handleApiError(result) {
  362. console.log('API错误:', result);
  363. this.setData({
  364. isSubmitting: false,
  365. currentStep: 1,
  366. lineProgress2: 0
  367. });
  368. // 根据错误码显示不同提示
  369. let title = '认证失败';
  370. let message = result.message || '请检查信息后重试';
  371. // 根据不同的错误码定制提示信息
  372. if (result.code === 500) {
  373. message = result.message || '服务器内部错误,请稍后重试';
  374. } else if (result.code === 400) {
  375. message = result.message || '请求参数错误,请检查填写的信息';
  376. } else if (result.code === 401) {
  377. message = result.message || '身份验证失败,请重新登录';
  378. } else if (result.code === 403) {
  379. message = result.message || '认证信息不正确,请核对姓名和身份证号';
  380. }
  381. this.showErrorModal(title, message);
  382. },
  383. // 显示成功弹窗
  384. showSuccessModal(maskedId) {
  385. this.setData({
  386. maskedIdNumber: maskedId,
  387. showSuccessModal: true
  388. });
  389. },
  390. // 关闭成功弹窗
  391. closeSuccessModal() {
  392. this.setData({ showSuccessModal: false });
  393. },
  394. // 返回
  395. goToHome() {
  396. this.closeSuccessModal();
  397. wx.switchTab({
  398. url: '/pages/personal/personal'
  399. });
  400. }
  401. });