From 90f84cc7df4c32c8146ab43510e018854172e575 Mon Sep 17 00:00:00 2001 From: 17792275749 <812100002@qq.com> Date: Mon, 24 Aug 2026 11:54:33 +0800 Subject: [PATCH] =?UTF-8?q?feat(user):=20=E8=A1=A5=E5=85=85=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E4=BF=A1=E6=81=AF=E9=A1=B5=E6=8E=A5=E5=85=A5=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=E8=AF=81=E5=B7=B2=E5=AD=98=E5=9C=A8=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=E5=BC=B9=E6=A1=86=E4=B8=8E=E7=99=BB=E5=BD=95=E5=BC=95=E5=AF=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- miniprogram/api/user.ts | 7 +- miniprogram/app.ts | 4 +- .../userInfoConfirmModal.json | 4 + .../userInfoConfirmModal.less | 139 ++++++++++++++++++ .../userInfoConfirmModal.ts | 63 ++++++++ .../userInfoConfirmModal.wxml | 42 ++++++ miniprogram/pages/login/login.ts | 10 +- .../supplementPersonal.json | 4 +- .../supplementPersonal/supplementPersonal.ts | 87 +++++++++-- .../supplementPersonal.wxml | 6 + .../utils/{common.ts => idCard/index.ts} | 46 +----- miniprogram/utils/idCard/types.ts | 11 ++ miniprogram/utils/request/index.ts | 2 +- 13 files changed, 353 insertions(+), 72 deletions(-) create mode 100644 miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.json create mode 100644 miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.less create mode 100644 miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.ts create mode 100644 miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.wxml rename miniprogram/utils/{common.ts => idCard/index.ts} (53%) create mode 100644 miniprogram/utils/idCard/types.ts diff --git a/miniprogram/api/user.ts b/miniprogram/api/user.ts index 545b924..0712402 100644 --- a/miniprogram/api/user.ts +++ b/miniprogram/api/user.ts @@ -30,6 +30,7 @@ export interface UserInfo { relationType: string | null sex_dictText: string connectDeviceInfo: string + flag?: number | null } export interface LoginResult { @@ -43,8 +44,8 @@ export const checkOpenIdLogin = (code: string) => /** 根据身份证号查询用户信息 */ export const getUserInfoByIdCard = (params: { sn?: string; idCard: string; realname: string }) => - get('weighingScale/v3/getUserInfoByIdcard', params, { loading: false }) + get('weighingScale/v6/getUserInfoByIdcard', params, { loading: true, loadingTitle: '正在查询用户信息' }) -/** 更新用户信息 */ +/** 更新当前登录微信用户信息 */ export const updateUser = (data: Record) => - put('weighingScale/v3/update/user', data) + put('weighingScale/v6/update/user', data) diff --git a/miniprogram/app.ts b/miniprogram/app.ts index 90b20b8..8b80267 100644 --- a/miniprogram/app.ts +++ b/miniprogram/app.ts @@ -4,8 +4,8 @@ onLaunch() { // 已登录则跳过登录页 const token = wx.getStorageSync('token') || null; - // const userInfo = wx.getStorageSync('userInfo') || null; - const userInfo = true; + const userInfo = wx.getStorageSync('userInfo') || null; + // const userInfo = true; if (token && userInfo) { wx.reLaunch({ url: '/pages/home/home' diff --git a/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.json b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.json new file mode 100644 index 0000000..59a1db6 --- /dev/null +++ b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.less b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.less new file mode 100644 index 0000000..53f8f43 --- /dev/null +++ b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.less @@ -0,0 +1,139 @@ +.confirm-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +} + +.modal-mask { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + opacity: 0; + transition: opacity 0.3s ease; + + &.anim-visible { + opacity: 1; + } +} + +.modal-content { + position: relative; + width: 660rpx; + background-color: #ffffff; + border-radius: 24rpx; + overflow: hidden; + transform: scale(0.9); + opacity: 0; + transition: all 0.3s ease; + + &.anim-visible { + transform: scale(1); + opacity: 1; + } +} + +.modal-header { + padding-top: 50rpx; + width: 100%; + margin-bottom: 30rpx; + + .modal-title { + width: 100%; + height: 36rpx; + font-weight: bolder; + font-size: 36rpx; + color: #252535; + line-height: 36rpx; + text-align: center; + margin-bottom: 24rpx; + } + + .modal-sub-title { + width: 100%; + height: 32rpx; + font-weight: 400; + font-size: 32rpx; + color: #1385FA; + line-height: 32rpx; + text-align: center; + } +} + +.modal-body { + width: 100%; + box-sizing: border-box; + padding: 0 30rpx; + margin-bottom: 30rpx; + + .modal-body-box { + width: 100%; + padding: 0 30rpx; + box-sizing: border-box; + border-radius: 16rpx; + background-color: #F6F6F6; + + .info-row { + width: 100%; + height: 80rpx; + display: flex; + padding: 10rpx 0; + align-items: center; + justify-content: space-between; + box-sizing: border-box; + border-bottom: 1px solid #EAECF1; + + .info-label { + color: #77849E; + height: 80rpx; + font-size: 28rpx; + line-height: 80rpx; + } + + .info-value { + color: #252535; + height: 80rpx; + font-weight: 500; + font-size: 28rpx; + line-height: 80rpx; + } + + &:last-of-type { + border-bottom: none; + } + } + } +} + +.modal-footer { + width: 100%; + height: 110rpx; + display: flex; + border-top: 1rpx solid #EAECF1; + + .modal-btn { + flex: 1; + height: 110rpx; + line-height: 110rpx; + font-size: 36rpx; + text-align: center; + } + + .modal-btn-cancel { + color: #77849E; + border-right: 1rpx solid #EAECF1; + } + + .modal-btn-confirm { + color: #1385FA; + font-weight: 500; + } +} diff --git a/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.ts b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.ts new file mode 100644 index 0000000..f9e878b --- /dev/null +++ b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.ts @@ -0,0 +1,63 @@ +/** + * 用户信息确认弹框组件 + * Properties: show(控制显示), userInfo(用户信息) + * Events: confirm(确认), cancel(取消) + */ +Component({ + properties: { + /** 是否显示弹框 */ + show: { + type: Boolean, + value: false + }, + /** 用户信息数据 */ + userInfo: { + type: Object, + value: {} + } + }, + + data: { + /** 控制 wx:if,DOM 是否存在 */ + innerShow: false, + /** 控制 --visible 类,触发 CSS transition */ + animVisible: false + }, + + observers: { + /** + * 监听外部 show 变化: + * 打开时先展示弹框再执行动画;关闭时移除动画类后移除 DOM + */ + show(val: boolean) { + if (val) { + this.setData({ innerShow: true }) + setTimeout(() => { + this.setData({ animVisible: true }) + }, 20) + } else { + this.setData({ animVisible: false }) + setTimeout(() => { + this.setData({ innerShow: false }) + }, 300) + } + } + }, + + methods: { + /** 点击遮罩关闭 */ + onTapMask() { + this.triggerEvent('cancel') + }, + + /** 点击取消按钮 */ + onTapCancel() { + this.triggerEvent('cancel') + }, + + /** 点击确认按钮 */ + onTapConfirm() { + this.triggerEvent('confirm') + } + } +}) diff --git a/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.wxml b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.wxml new file mode 100644 index 0000000..fd99f85 --- /dev/null +++ b/miniprogram/components/userInfoConfirmModal/userInfoConfirmModal.wxml @@ -0,0 +1,42 @@ + + + + + + 当前身份证号已存在 + 是否直接复用信息? + + + + + + + 姓名: + {{ userInfo.realname }} + + + 身份证号: + {{ userInfo.idCard }} + + + 性别: + {{ userInfo.sex_dictText }} + + + 身高: + {{ userInfo.height }}cm + + + 体重: + {{ userInfo.weight }}kg + + + + + + + 取消 + 确认 + + + diff --git a/miniprogram/pages/login/login.ts b/miniprogram/pages/login/login.ts index 0097799..c6980df 100644 --- a/miniprogram/pages/login/login.ts +++ b/miniprogram/pages/login/login.ts @@ -25,12 +25,12 @@ Page({ const { token, user } = res.result // 持久化登录凭证和用户信息 wx.setStorageSync('token', token) - // if (user) { - // wx.setStorageSync('userInfo', user) + if (user) { + wx.setStorageSync('userInfo', user) wx.reLaunch({ url: '/pages/home/home' }) - // } else { - // wx.reLaunch({ url: '/pages/supplementPersonal/supplementPersonal' }) - // } + } else { + wx.reLaunch({ url: '/pages/supplementPersonal/supplementPersonal' }) + } }).catch((err) => { wx.showToast({ title: err.msg || '登录失败,请重试', icon: 'none' }) }).finally(() => { diff --git a/miniprogram/pages/supplementPersonal/supplementPersonal.json b/miniprogram/pages/supplementPersonal/supplementPersonal.json index 40e0f04..5d7d9f0 100644 --- a/miniprogram/pages/supplementPersonal/supplementPersonal.json +++ b/miniprogram/pages/supplementPersonal/supplementPersonal.json @@ -1,5 +1,7 @@ { - "usingComponents": {}, + "usingComponents": { + "user-info-confirm-modal": "/components/userInfoConfirmModal/userInfoConfirmModal" + }, "navigationBarTextStyle": "black", "navigationBarBackgroundColor": "#FFFFFF", "navigationBarTitleText": "完善个人信息" diff --git a/miniprogram/pages/supplementPersonal/supplementPersonal.ts b/miniprogram/pages/supplementPersonal/supplementPersonal.ts index 2c8c1a8..d4bfb91 100644 --- a/miniprogram/pages/supplementPersonal/supplementPersonal.ts +++ b/miniprogram/pages/supplementPersonal/supplementPersonal.ts @@ -1,5 +1,5 @@ import { getUserInfoByIdCard, updateUser, type UserInfo } from '../../api/index' -import { parseIdCard } from '../../utils/common' +import { parseIdCard, isIdCardValid } from '../../utils/idCard/index' Page({ data: { @@ -7,34 +7,55 @@ Page({ idCard: '', gender: '', age: '', + sex: 0, + birthday: '', height: '', weight: '', + + /** 查询接口返回的原始用户信息 */ serverResult: null as UserInfo | null, + /** 身份证已存在(flag=101)时表单只读 */ + readonly: false, + /** 是否展示「复用信息」确认弹框 */ + showConfirmModal: false, + }, + + /** 姓名/身份证变动后,查询回填的信息失效,统一清空 */ + _clearQuery() { + this.setData({ + serverResult: null, + gender: '', + age: '', + sex: 0, + birthday: '', + height: '', + weight: '', + readonly: false, + }) }, onNameInput(e: WechatMiniprogram.Input) { this.setData({ name: e.detail.value.trim() }) + this._clearQuery() }, - /* 失焦时:清空查询结果 → 重新校验身份证,变更即失效 */ + // 失焦重新校验:姓名改完、身份证仍合法时重新发起查询 onNameBlur() { if (!this.data.name) return - this.setData({ serverResult: null }) this.validateIdCard() }, onIdCardInput(e: WechatMiniprogram.Input) { const idCard = (e.detail.value || '').trim().toUpperCase() - this.setData({ idCard, serverResult: null }) + this.setData({ idCard }) + this._clearQuery() if (idCard.length === 18) this.validateIdCard() }, /* 独立的身份证合法性校验,合法且姓名已填则请求接口 */ validateIdCard() { const { idCard, name } = this.data - const { gender, age } = parseIdCard(idCard) - this.setData({ gender, age }) - if (gender && name) this.fetchUser() + if (isIdCardValid(idCard) && name) this.fetchUser() }, fetchUser() { @@ -42,13 +63,51 @@ Page({ idCard: this.data.idCard, realname: this.data.name, }).then((res) => { - if (res.result) { - const { gender, age } = parseIdCard(res.result.idCard) - this.setData({ serverResult: res.result, gender, age }) + const serverResult = res.result + // 优先按服务器返回的身份证解析,未查到则回退用户输入 + const { gender, age, sex, birthday } = parseIdCard(serverResult?.idCard || this.data.idCard) + this.setData({ gender, age, sex, birthday }) + if (!serverResult) { + wx.showToast({ title: res.msg || '未查询到用户信息', icon: 'none' }) + return + } + // flag=101:身份证已存在,弹框确认是否复用 + if (serverResult.flag === 101) { + this.setData({ + serverResult, + name: serverResult.realname ?? '', + idCard: serverResult.idCard ?? '', + height: serverResult.height ? String(serverResult.height) : '', + weight: serverResult.weight ? String(serverResult.weight) : '', + readonly: true, + showConfirmModal: true, + }) + return + } + // flag=null:查到但未建档,回填已有身高体重 + if (serverResult.flag === null) { + this.setData({ + serverResult, + height: serverResult.height ? String(serverResult.height) : '', + weight: serverResult.weight ? String(serverResult.weight) : '', + }) + return } }).catch(() => {}) }, + // 复用已有信息:关闭弹框直接提交 + onConfirmModalConfirm() { + this.setData({ showConfirmModal: false }) + this.onSubmit() + }, + + // 不复用:清空整个表单重新填写 + onConfirmModalCancel() { + this.setData({ showConfirmModal: false, name: '', idCard: '' }) + this._clearQuery() + }, + onHeightInput(e: WechatMiniprogram.Input) { this.setData({ height: e.detail.value }) }, @@ -58,7 +117,7 @@ Page({ }, onSubmit() { - const { name, idCard, gender, height, weight, serverResult } = this.data + const { name, idCard, gender, sex, birthday, height, weight, serverResult } = this.data const heightNum = parseFloat(height) const weightNum = parseFloat(weight) @@ -79,16 +138,14 @@ Page({ return } - const parsed = parseIdCard(idCard) - updateUser({ ...serverResult, idCard, realname: name, height: heightNum, weight: weightNum || 0, - sex: parsed.sex, - birthday: parsed.birthday, + sex, + birthday, }).then((res) => { wx.setStorageSync('userInfo', res.result) wx.switchTab({ url: '/pages/home/home' }) diff --git a/miniprogram/pages/supplementPersonal/supplementPersonal.wxml b/miniprogram/pages/supplementPersonal/supplementPersonal.wxml index 2f77f3b..8bde356 100644 --- a/miniprogram/pages/supplementPersonal/supplementPersonal.wxml +++ b/miniprogram/pages/supplementPersonal/supplementPersonal.wxml @@ -18,6 +18,7 @@ placeholder="请填写真实姓名" placeholder-class="form-placeholder" value="{{ name }}" + disabled="{{ readonly }}" bind:input="onNameInput" bind:blur="onNameBlur" /> @@ -35,6 +36,7 @@ placeholder="请填写身份证号" placeholder-class="form-placeholder" value="{{ idCard }}" + disabled="{{ readonly }}" bind:input="onIdCardInput" /> @@ -77,6 +79,7 @@ placeholder="请填写身高" placeholder-class="form-placeholder" value="{{ height }}" + disabled="{{ readonly }}" bind:input="onHeightInput" /> cm @@ -95,6 +98,7 @@ placeholder="请填写体重" placeholder-class="form-placeholder" value="{{ weight }}" + disabled="{{ readonly }}" bind:input="onWeightInput" /> kg @@ -104,4 +108,6 @@ 完成并进入首页 + + diff --git a/miniprogram/utils/common.ts b/miniprogram/utils/idCard/index.ts similarity index 53% rename from miniprogram/utils/common.ts rename to miniprogram/utils/idCard/index.ts index 81a650b..ce45d57 100644 --- a/miniprogram/utils/common.ts +++ b/miniprogram/utils/idCard/index.ts @@ -1,47 +1,4 @@ -/** 身份证解析结果 */ -export interface IdCardParsed { - /** 性别文本(男/女),解析失败返回空字符串 */ - gender: string - /** 年龄文本,解析失败返回空字符串 */ - age: string - /** 生日 YYYY-MM-DD */ - birthday: string - /** 性别 1=男 2=女 */ - sex: number -} - -/** storage 中 userInfo 的类型 */ -export interface StorageUserInfo { - id: string - 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 | null - thirdId: string - realname: string - bmi: number - createBy: string - createTime: string - updateBy: string | null - updateTime: string | null - delFlag: number - sn: string | null - workNo: string | null - remark: string | null - relationType: string | null - sex_dictText: string - connectDeviceInfo: string -} +import type { IdCardParsed } from './types' /** 校验身份证号格式是否合法 */ export const isIdCardValid = (idCard: string): boolean => { @@ -69,7 +26,6 @@ export const parseIdCard = (idCard: string): IdCardParsed => { // 计算年龄 const now = new Date() - const birth = new Date(year, month - 1, day) let age = now.getFullYear() - year if (now.getMonth() < month - 1 || (now.getMonth() === month - 1 && now.getDate() < day)) { age-- diff --git a/miniprogram/utils/idCard/types.ts b/miniprogram/utils/idCard/types.ts new file mode 100644 index 0000000..6d400f9 --- /dev/null +++ b/miniprogram/utils/idCard/types.ts @@ -0,0 +1,11 @@ +/** 身份证解析结果 */ +export interface IdCardParsed { + /** 性别文本(男/女),解析失败返回空字符串 */ + gender: string + /** 年龄文本,解析失败返回空字符串 */ + age: string + /** 生日 YYYY-MM-DD */ + birthday: string + /** 性别 1=男 2=女 */ + sex: number +} diff --git a/miniprogram/utils/request/index.ts b/miniprogram/utils/request/index.ts index ac6da96..40bae7f 100644 --- a/miniprogram/utils/request/index.ts +++ b/miniprogram/utils/request/index.ts @@ -27,7 +27,7 @@ const request = ( const header: Record = { 'content-type': contentType } const token = wx.getStorageSync('token') as string - if (token) header['abtoken'] = token + if (token) header['X-Access-Token'] = token return new Promise((resolve, reject) => { wx.request({