diff --git a/miniprogram/pages/supplementPersonal/supplementPersonal.ts b/miniprogram/pages/supplementPersonal/supplementPersonal.ts index 180efcb..c26ec6c 100644 --- a/miniprogram/pages/supplementPersonal/supplementPersonal.ts +++ b/miniprogram/pages/supplementPersonal/supplementPersonal.ts @@ -1,149 +1,266 @@ +import { get, put } from '../../utils/request/index' + /** 表单数据模型 */ interface PersonalForm { /** 姓名 */ name: string /** 身份证号 */ idCard: string - /** 性别(由身份证解析,只读) */ + /** 性别(由身份证解析,只读) */ gender: string - /** 年龄(由身份证解析,只读) */ + /** 年龄(由身份证解析,只读) */ age: string /** 身高(cm) */ height: string + /** 体重(kg) */ + weight: string } -/** 身份证解析结果 */ +/** + * 身份证解析结果 + * sex / birthday 用于接口提交,gender / age 用于页面展示 + */ interface IdCardParsed { - /** 性别,空字符串表示解析失败 */ - gender: string - /** 年龄,空字符串表示解析失败 */ - age: string + gender: string // '男' | '女' | '' + age: string // 周岁字符串,解析失败为 '' + sex: number // 1=男 2=女,0=解析失败 + birthday: string // YYYY-MM-DD,解析失败为 '' +} + +/** GET /weighingScale/v3/getUserInfoByIdcard 返回的 result 结构 */ +interface UserInfoFromServer { + id: string | null + userId: string + parentUserId: string | null + scaleDeviceId: string + deviceId: string | null + deviceType: string + dataType: number + userType: string | null + idCard: string + height: number + weight: number + birthday: string + sex: number + avatar: string | null + phone: string + thirdId: string | null + realname: string + bmi: number + workNo: string + remark: string | null + relationType: string | null + sn: string + delFlag: number + sex_dictText: string +} + +/** PUT /weighingScale/v3/update/user 请求体 */ +type UpdateUserPayload = UserInfoFromServer & { + /** storage 中取出的 connectDeviceInfo 对象,JSON.stringify 后传入 */ + connectDeviceInfo: string } /** * 完善个人信息页 - * 收集姓名、身份证号、身高,并根据身份证自动解析性别与年龄 - * 提交后进入首页(tabBar) + * 收集姓名、身份证号、身高、体重,身份证满 18 位且姓名非空时自动调接口校验并回填性别年龄 */ Page({ - data: { - // 表单数据 - form: { - name: '', // 姓名 - idCard: '', // 身份证号 - gender: '', // 性别(由身份证解析,只读) - age: '', // 年龄(由身份证解析,只读) - height: '' // 身高(cm) - } as PersonalForm - }, + data: { + form: { + name: '', + idCard: '', + gender: '', + age: '', + height: '', + weight: '' + } as PersonalForm, + /** 正在请求身份证查询接口 */ + querying: false, + /** 接口返回的用户信息,提交时透传给更新接口 */ + serverResult: null as UserInfoFromServer | null + }, - /** - * 姓名输入 - */ - onNameInput(e: WechatMiniprogram.Input) { - this.setData({ - 'form.name': e.detail.value - }); - }, + /** + * 姓名输入 + * 如果身份证已合法则联动触发服务端查询 + */ + onNameInput(e: WechatMiniprogram.Input) { + const name = e.detail.value + this.setData({ 'form.name': name }) + if (!name.trim()) { + this.setData({ serverResult: null }) + return + } + if (this.isIdCardValid(this.data.form.idCard)) { + this.fetchUserInfoByIdCard(name.trim(), this.data.form.idCard) + } + }, - /** - * 身份证号输入 - * 长度满 18 位时自动解析性别与年龄,否则清空 - */ - onIdCardInput(e: WechatMiniprogram.Input) { - const idCard = (e.detail.value || '').trim().toUpperCase(); - const parsed = this.parseIdCard(idCard); + /** + * 身份证号输入 + * 本地解析性别年龄;满足合法格式且姓名非空时触发服务端查询 + */ + onIdCardInput(e: WechatMiniprogram.Input) { + const idCard = (e.detail.value || '').trim().toUpperCase() + const parsed = this.parseIdCard(idCard) + this.setData({ + 'form.idCard': idCard, + 'form.gender': parsed.gender, + 'form.age': parsed.age + }) + if (!parsed.gender) { + // 身份证不合法,清除上次服务端结果 + this.setData({ serverResult: null }) + return + } + if (this.data.form.name.trim()) { + this.fetchUserInfoByIdCard(this.data.form.name.trim(), idCard) + } + }, - this.setData({ - 'form.idCard': idCard, - 'form.gender': parsed.gender, - 'form.age': parsed.age - }); - }, + /** 身高输入 */ + onHeightInput(e: WechatMiniprogram.Input) { + this.setData({ 'form.height': e.detail.value }) + }, - /** - * 身高输入 - */ - onHeightInput(e: WechatMiniprogram.Input) { - this.setData({ - 'form.height': e.detail.value - }); - }, + /** 体重输入 */ + onWeightInput(e: WechatMiniprogram.Input) { + this.setData({ 'form.weight': e.detail.value }) + }, - /** - * 解析身份证号,返回性别与年龄 - * @param idCard 18 位身份证号 - */ - parseIdCard(idCard: string): IdCardParsed { - // 简单校验:18 位且前 17 位为数字 - const reg = /^\d{17}[\dX]$/; - if (!reg.test(idCard)) { - return { gender: '', age: '' }; - } + /** + * 校验身份证格式:18 位,前 17 位数字,末位数字或 X + * @param idCard 待校验的身份证号 + */ + isIdCardValid(idCard: string): boolean { + return /^\d{17}[\dX]$/.test(idCard) + }, - // 性别:第 17 位奇数为男,偶数为女 - const genderCode = parseInt(idCard.charAt(16), 10); - const gender = genderCode % 2 === 1 ? '男' : '女'; + /** + * 解析身份证,返回性别、年龄、sex 数值、出生日期 + * @param idCard 18 位身份证号 + */ + parseIdCard(idCard: string): IdCardParsed { + const empty: IdCardParsed = { gender: '', age: '', sex: 0, birthday: '' } + if (!this.isIdCardValid(idCard)) return empty - // 出生日期:第 7-14 位 - const year = parseInt(idCard.substr(6, 4), 10); - const month = parseInt(idCard.substr(10, 2), 10); - const day = parseInt(idCard.substr(12, 2), 10); + // 性别:第 17 位奇数为男,偶数为女 + const genderCode = parseInt(idCard.charAt(16), 10) + const sex = genderCode % 2 === 1 ? 1 : 2 + const gender = sex === 1 ? '男' : '女' - // 基本合法性校验 - if ( - !year || !month || !day || - month < 1 || month > 12 || - day < 1 || day > 31 - ) { - return { gender: '', age: '' }; - } + // 出生日期:第 7-14 位 + const year = parseInt(idCard.substr(6, 4), 10) + const month = parseInt(idCard.substr(10, 2), 10) + const day = parseInt(idCard.substr(12, 2), 10) - // 计算周岁(看是否过了今年生日) - const now = new Date(); - let age = now.getFullYear() - year; - const nowMonth = now.getMonth() + 1; - const nowDay = now.getDate(); - if (nowMonth < month || (nowMonth === month && nowDay < day)) { - age -= 1; - } + if (!year || month < 1 || month > 12 || day < 1 || day > 31) return empty - if (age < 0 || age > 150) { - return { gender: '', age: '' }; - } + // 计算周岁 + const now = new Date() + let age = now.getFullYear() - year + const nowMonth = now.getMonth() + 1 + const nowDay = now.getDate() + if (nowMonth < month || (nowMonth === month && nowDay < day)) age -= 1 + if (age < 0 || age > 150) return empty - return { gender, age: String(age) }; - }, + const mm = String(month).padStart(2, '0') + const dd = String(day).padStart(2, '0') + const birthday = `${year}-${mm}-${dd}` - /** - * 提交表单 - * 必填:姓名、身份证号(18 位且解析成功)、身高 - * 未通过时 toast 提示,通过后进入首页(tabBar) - */ - onSubmit() { - const { name, idCard, gender, age, height } = this.data.form; - const heightNum = parseFloat(height); + return { gender, age: String(age), sex, birthday } + }, - if (!name.trim()) { - wx.showToast({ title: '请填写姓名', icon: 'none' }); - return; - } - if (idCard.length !== 18) { - wx.showToast({ title: '请填写正确的身份证号', icon: 'none' }); - return; - } - if (!gender || !age) { - wx.showToast({ title: '身份证号有误,请检查', icon: 'none' }); - return; - } - if (!height || isNaN(heightNum) || heightNum <= 0) { - wx.showToast({ title: '请填写身高', icon: 'none' }); - return; - } + /** + * 调用服务端接口校验身份证与姓名,成功后用接口返回的身份证重新计算性别年龄 + * @param name 用户填写的姓名 + * @param idCard 用户填写的身份证号 + */ + fetchUserInfoByIdCard(name: string, idCard: string) { + if (this.data.querying) return + this.setData({ querying: true, serverResult: null }) - // TODO: 此处后续接入用户信息保存接口 - wx.switchTab({ - url: '/pages/home/home' - }); - } -}); + get('weighingScale/v3/getUserInfoByIdcard', { + sn: '', + idCard, + realname: name + }, { loading: false }) + .then(res => { + const serverResult = res.result + // 用接口返回的身份证重新计算性别和年龄(以服务端为准) + const parsed = this.parseIdCard(serverResult.idCard) + this.setData({ + serverResult, + 'form.gender': parsed.gender, + 'form.age': parsed.age + }) + }) + .catch(() => { + // 查询失败静默处理,保留本地解析结果 + }) + .finally(() => { + this.setData({ querying: false }) + }) + }, + + /** + * 提交表单 + * 校验全部必填项 → 构建 payload(透传 serverResult + 用户手动输入)→ PUT 更新接口 + */ + onSubmit() { + const { name, idCard, gender, age, height, weight } = this.data.form + const heightNum = parseFloat(height) + const weightNum = parseFloat(weight) + + if (!name.trim()) { + wx.showToast({ title: '请填写姓名', icon: 'none' }) + return + } + if (idCard.length !== 18) { + wx.showToast({ title: '请填写正确的身份证号', icon: 'none' }) + return + } + if (!gender || !age) { + wx.showToast({ title: '身份证号有误,请检查', icon: 'none' }) + return + } + if (!height || isNaN(heightNum) || heightNum <= 0) { + wx.showToast({ title: '请填写身高', icon: 'none' }) + return + } + if (!weight || isNaN(weightNum) || weightNum <= 0) { + wx.showToast({ title: '请填写体重', icon: 'none' }) + return + } + if (!this.data.serverResult) { + wx.showToast({ title: '身份信息未验证,请检查姓名与身份证号', icon: 'none' }) + return + } + + const parsed = this.parseIdCard(idCard) + + // connectDeviceInfo 存储时已 JSON.parse 为对象,提交时还原为 JSON 字符串 + const connectDeviceInfoRaw = wx.getStorageSync('connectDeviceInfo') + const connectDeviceInfo: string = connectDeviceInfoRaw + ? JSON.stringify(connectDeviceInfoRaw) + : '' + + const serverResult = this.data.serverResult as UserInfoFromServer + const payload: UpdateUserPayload = { + ...serverResult, + idCard, + realname: name.trim(), + height: heightNum, + weight: weightNum, + sex: parsed.sex, + birthday: parsed.birthday, + connectDeviceInfo + } + + put('weighingScale/v3/update/user', payload) + .then(() => { + wx.switchTab({ url: '/pages/home/home' }) + }) + } +}) diff --git a/miniprogram/pages/supplementPersonal/supplementPersonal.wxml b/miniprogram/pages/supplementPersonal/supplementPersonal.wxml index 05ce9b3..afc690f 100644 --- a/miniprogram/pages/supplementPersonal/supplementPersonal.wxml +++ b/miniprogram/pages/supplementPersonal/supplementPersonal.wxml @@ -98,6 +98,26 @@ + + + + + * + 体重 + + + + + kg + + +