From fd93f37d725d338166b3126805858e71daa64e1e Mon Sep 17 00:00:00 2001 From: 17792275749 <812100002@qq.com> Date: Thu, 11 Jun 2026 10:16:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor(editMember):=20=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E9=A1=B5=E7=8B=AC=E7=AB=8B=EF=BC=8C=E5=8C=BA?= =?UTF-8?q?=E5=88=86=E5=AE=B6=E5=BA=AD/=E9=9D=9E=E5=AE=B6=E5=BA=AD?= =?UTF-8?q?=E5=8F=8C=E6=A8=A1=E5=BC=8F=E8=A1=A8=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 家庭用户:昵称(必填)、身份证(选填)、性别(切换按钮)、出生年月(date picker)、身高(必填)、体重(选填) - 非家庭用户:姓名、单位、性别、年龄只读展示,身高(必填)、体重(选填) - 提交时完整展开接口原始数据覆盖修改字段 - 去掉新增页专属的抽屉选择、确认弹框、身份证校验 Co-Authored-By: Claude Opus 4.7 --- miniprogram/pages/editMember/editMember.json | 9 +- miniprogram/pages/editMember/editMember.less | 69 ++-- miniprogram/pages/editMember/editMember.ts | 390 ++++--------------- miniprogram/pages/editMember/editMember.wxml | 313 ++++++++------- 4 files changed, 306 insertions(+), 475 deletions(-) diff --git a/miniprogram/pages/editMember/editMember.json b/miniprogram/pages/editMember/editMember.json index 71ddaee..238b4ac 100644 --- a/miniprogram/pages/editMember/editMember.json +++ b/miniprogram/pages/editMember/editMember.json @@ -1,8 +1,3 @@ { - "navigationBarTitleText": "编辑用户", - "usingComponents": { - "select-member-drawer": "/components/selectMemberDrawer/selectMemberDrawer", - "user-info-confirm-modal": "/components/userInfoConfirmModal/userInfoConfirmModal" - } - } - \ No newline at end of file + "navigationBarTitleText": "编辑用户" +} diff --git a/miniprogram/pages/editMember/editMember.less b/miniprogram/pages/editMember/editMember.less index e6f235a..4068476 100644 --- a/miniprogram/pages/editMember/editMember.less +++ b/miniprogram/pages/editMember/editMember.less @@ -1,4 +1,4 @@ -.add-member { +.edit-member { width: 100%; height: 100%; display: flex; @@ -113,7 +113,25 @@ font-size: 26rpx; } - /* 输入框 + 后缀容器(身高 cm) */ + /* 日期选择器 */ + .form-picker { + width: 100%; + height: 88rpx; + padding: 0 30rpx; + color: #252535; + font-size: 26rpx; + line-height: 88rpx; + border-radius: 16rpx; + box-sizing: border-box; + background-color: #FFFFFF; + border: 2rpx solid #E6E6EA; + } + + .form-picker-placeholder { + color: #B6B6B6; + } + + /* 输入框 + 后缀容器(身高 cm / 体重 kg) */ .form-input-wrap { position: relative; width: 630rpx; @@ -133,7 +151,7 @@ } } - /* 只读区域(性别 / 年龄) */ + /* 只读区域(单位 / 性别 / 年龄 / 出生年月) */ .form-readonly { width: 630rpx; height: 88rpx; @@ -143,16 +161,12 @@ border-radius: 16rpx; display: flex; align-items: center; + border: 2rpx solid #E6E6EA; .readonly-text { color: #252535; font-size: 26rpx; } - - .readonly-placeholder { - color: #B6B6B6; - font-size: 26rpx; - } } .form-readonly-with-suffix { @@ -165,21 +179,32 @@ line-height: 88rpx; } } - } - /* 从已有用户中选择(蓝色描边幽灵按钮) */ - .ghost-btn { - color: #1385FA; - width: 580rpx; - height: 88rpx; - font-size: 32rpx; - font-weight: bold; - text-align: center; - line-height: 84rpx; - border-radius: 44rpx; - box-sizing: border-box; - border: 2rpx solid #1385FA; - margin: 0 auto; + /* 性别切换按钮(家庭用户) */ + .gender-toggle { + display: flex; + gap: 24rpx; + + .gender-option { + width: 300rpx; + height: 88rpx; + font-size: 28rpx; + text-align: center; + line-height: 88rpx; + border-radius: 16rpx; + box-sizing: border-box; + color: #77849E; + background-color: #FFFFFF; + border: 2rpx solid #E6E6EA; + } + + .gender-option-active { + color: #FFFFFF; + font-weight: bold; + background-color: #1385FA; + border-color: #1385FA; + } + } } } diff --git a/miniprogram/pages/editMember/editMember.ts b/miniprogram/pages/editMember/editMember.ts index 4808516..0305a5b 100644 --- a/miniprogram/pages/editMember/editMember.ts +++ b/miniprogram/pages/editMember/editMember.ts @@ -1,92 +1,32 @@ import { get, put } from '../../utils/request/index' -import { lefuService } from '../../lefu/index' - -/** GET /weighingScale/v3/selectUserBySn/user 返回的单条结构(编辑回填用) */ -interface SysUserDevice { - id: string - realname: string - idCard: string - height: number - weight: number - sex: number - birthday: string -} - -/** 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 -} /** 表单数据模型 */ interface MemberForm { - /** 姓名 */ name: string - /** 身份证号 */ idCard: string - /** 性别(由身份证解析,只读) */ gender: string - /** 年龄(由身份证解析,只读) */ age: string - /** 身高(cm) */ height: string - /** 体重(kg) */ weight: string -} - -/** 身份证解析结果 */ -interface IdCardParsed { - gender: string - age: string + /** 1=男 2=女 */ sex: number - birthday: string -} - -/** 从抽屉组件 select 事件接收的用户数据(完整 item) */ -interface ExistingMember { - id: string - name: string - gender: '男' | '女' - age: number - height: number - weight: number - idCard: string - avatar?: string - [key: string]: any } /** - * 添加/编辑用户页 - * URL 参数含 userId 时为编辑模式,隐藏「从已有用户中选择」按钮并预填表单 + * 编辑用户页 + * 根据用户 dataType 区分非家庭/家庭两种编辑模式 */ Page({ data: { - /** 编辑模式标记 */ - isEdit: false, - /** 编辑时的用户 ID */ userId: '', + /** dataType === 1 为家庭用户 */ + isFamily: false, + /** 单位(非家庭用户) */ + workNo: '', + /** 出生年月 YYYY-MM-DD(家庭用户) */ + birthday: '', + /** 接口返回的原始用户数据,提交时以此为 base 覆盖修改字段 */ + rawUser: null as Record | null, form: { name: '', @@ -94,75 +34,77 @@ Page({ gender: '', age: '', height: '', - weight: '' + weight: '', + sex: 0, } as MemberForm, - - /** 从抽屉选中的完整用户数据,提交时作为 base spread */ - selectedMember: null as ExistingMember | null, - showSelectDrawer: false, - /** 正在请求身份证查询接口 */ - querying: false, - /** 是否已完成一次身份证查询(成功 200 即视为完成,无论 result 是否为 null) */ - hasQueried: false, - /** 接口返回的用户信息,姓名 + 身份证联动校正性别年龄 */ - serverResult: null as UserInfoFromServer | null, - /** 表单只读状态(code=101 时置为 true) */ - readonly: false, - /** 是否显示确认弹框 */ - showConfirmModal: false, }, onLoad(options: Record) { const { userId } = options - if (!userId) return - this.setData({ isEdit: true, userId }) - get('weighingScale/v3/select/userinfo/byId', { id: userId }, { loading: false }) + if (!userId) { + wx.showToast({ title: '缺少用户信息', icon: 'none' }) + setTimeout(() => wx.navigateBack(), 1500) + return + } + this.setData({ userId }) + get>('weighingScale/v3/select/userinfo/byId', { id: userId }, { loading: true }) .then((res: any) => { - const u: SysUserDevice = res.result + const u = res.result if (!u) return - const parsed = this.parseIdCard(u.idCard ?? '') + const isFamily = u.dataType === 1 + const sex = u.sex ?? 0 + const gender = sex === 1 ? '男' : sex === 2 ? '女' : '' + const age = this.calcAge(u.birthday ?? '') + this.setData({ + rawUser: u, + isFamily, + workNo: u.workNo ?? '', + birthday: u.birthday ?? '', 'form.name': u.realname ?? '', 'form.idCard': u.idCard ?? '', - 'form.gender': parsed.gender, - 'form.age': parsed.age, + 'form.gender': gender, + 'form.age': String(age), 'form.height': String(u.height ?? ''), 'form.weight': String(u.weight ?? ''), + 'form.sex': sex, }) - if ((u.realname ?? '').trim() && this.isIdCardValid(u.idCard ?? '')) { - this.fetchUserInfoByIdCard(u.realname.trim(), u.idCard) - } + }) + .catch(() => { + wx.showToast({ title: '加载用户信息失败', icon: 'none' }) }) }, + /** 从生日字符串计算周岁 */ + calcAge(birthday: string): number { + if (!birthday) return 0 + const parts = birthday.split('-').map(Number) + const [year, month, day] = parts + if (!year || !month || !day) return 0 + 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 + return Math.max(0, age) + }, + onNameInput(e: WechatMiniprogram.Input) { - const name = e.detail.value - this.setData({ - 'form.name': name, - 'form.gender': '', - 'form.age': '', - serverResult: null, - hasQueried: false, - readonly: false - }) - if (name.trim() && this.isIdCardValid(this.data.form.idCard)) { - this.fetchUserInfoByIdCard(name.trim(), this.data.form.idCard) - } + this.setData({ 'form.name': e.detail.value }) }, onIdCardInput(e: WechatMiniprogram.Input) { - const idCard = (e.detail.value || '').trim().toUpperCase() + this.setData({ 'form.idCard': (e.detail.value || '').trim().toUpperCase() }) + }, + + /** 家庭用户性别切换 */ + onGenderTap(e: WechatMiniprogram.TouchEvent) { + const value = e.currentTarget.dataset.value as string + const sex = value === '男' ? 1 : 2 this.setData({ - 'form.idCard': idCard, - 'form.gender': '', - 'form.age': '', - serverResult: null, - hasQueried: false, - readonly: false + 'form.gender': value, + 'form.sex': sex, }) - if (this.data.form.name.trim() && this.isIdCardValid(idCard)) { - this.fetchUserInfoByIdCard(this.data.form.name.trim(), idCard) - } }, onHeightInput(e: WechatMiniprogram.Input) { @@ -173,156 +115,13 @@ Page({ this.setData({ 'form.weight': e.detail.value }) }, - /** - * 校验身份证格式:18 位,前 17 位数字,末位数字或 X - */ - isIdCardValid(idCard: string): boolean { - return /^\d{17}[\dX]$/.test(idCard) - }, - - /** - * 解析身份证,返回性别、年龄、sex 数值、出生日期 - */ - parseIdCard(idCard: string): IdCardParsed { - const empty: IdCardParsed = { gender: '', age: '', sex: 0, birthday: '' } - if (!this.isIdCardValid(idCard)) return empty - - const genderCode = parseInt(idCard.charAt(16), 10) - const sex = genderCode % 2 === 1 ? 1 : 2 - const gender = sex === 1 ? '男' : '女' - - const year = parseInt(idCard.substr(6, 4), 10) - const month = parseInt(idCard.substr(10, 2), 10) - const day = parseInt(idCard.substr(12, 2), 10) - if (!year || month < 1 || month > 12 || day < 1 || day > 31) return empty - - const now = new Date() - let age = now.getFullYear() - year - if (now.getMonth() + 1 < month || (now.getMonth() + 1 === month && now.getDate() < day)) age -= 1 - if (age < 0 || age > 150) return empty - - const mm = String(month).padStart(2, '0') - const dd = String(day).padStart(2, '0') - return { gender, age: String(age), sex, birthday: `${year}-${mm}-${dd}` } - }, - - /** - * 调用服务端接口校验身份证与姓名,成功后用接口返回的身份证重新计算性别年龄 - * result 为 null 视为「查无此人」,标记 hasQueried 允许新增提交 - * result.code 101 视为「信息已存在」,弹框提示并锁定表单 - * result.code 102 视为正常,走原有逻辑 - */ - fetchUserInfoByIdCard(name: string, idCard: string) { - if (this.data.querying) return - this.setData({ querying: true, serverResult: null, hasQueried: false }) - - get('weighingScale/v3/getUserInfoByIdcard', { - sn: lefuService.deviceInfo?.serialNumber ?? '', - idCard, - realname: name - }, { loading: true }) - .then(res => { - const serverResult = res.result - if (!serverResult) { - // 查无此人:用本地身份证解析补偿性别年龄 - wx.showToast({ - title: res.message || '未查询到用户信息', - icon: 'none' - }) - const parsed = this.parseIdCard(idCard) - this.setData({ - hasQueried: true, - 'form.gender': parsed.gender, - 'form.age': parsed.age, - }) - return - } - if (serverResult.flag == 101) { - const parsed = this.parseIdCard(serverResult.idCard) - this.setData({ - serverResult, - hasQueried: true, - readonly: true, - showConfirmModal: true, - 'form.name': serverResult.realname ?? '', - 'form.idCard': serverResult.idCard ?? '', - 'form.gender': parsed.gender, - 'form.age': parsed.age, - 'form.height': serverResult.height ? String(serverResult.height) : '', - 'form.weight': serverResult.weight ? String(serverResult.weight) : '' - }) - return - } - if (serverResult.flag === null) { - const parsed = this.parseIdCard(serverResult.idCard) - const data: Record = { - serverResult, - hasQueried: true, - 'form.gender': parsed.gender, - 'form.age': parsed.age - } - if (serverResult.height) { - data['form.height'] = String(serverResult.height) - } - if (serverResult.weight) { - data['form.weight'] = String(serverResult.weight) - } - this.setData(data) - } - }) - .catch(() => { - // 查询失败保留本地解析结果,hasQueried 维持 false 以便用户重试时再次触发 - }) - .finally(() => { - this.setData({ querying: false }) - }) - }, - - onTapSelectExisting() { - this.setData({ showSelectDrawer: true }) - }, - - onSelectMemberClose() { - this.setData({ showSelectDrawer: false }) - }, - - onSelectMember(e: WechatMiniprogram.CustomEvent) { - const member = e.detail - const parsed = this.parseIdCard(member.idCard) + /** 出生年月变更,同步更新年龄 */ + onBirthdayChange(e: WechatMiniprogram.PickerChange) { + const birthday = e.detail.value as string + const age = this.calcAge(birthday) this.setData({ - showSelectDrawer: false, - selectedMember: member, - 'form.name': member.name, - 'form.idCard': member.idCard, - 'form.gender': parsed.gender, - 'form.age': parsed.age, - 'form.height': String(member.height), - 'form.weight': String(member.weight), - }) - if (member.name.trim() && this.isIdCardValid(member.idCard)) { - this.fetchUserInfoByIdCard(member.name.trim(), member.idCard) - } - }, - - /** 确认弹框 - 确认 */ - onConfirmModalConfirm() { - this.setData({ showConfirmModal: false }) - this.onSubmit() - }, - - /** 确认弹框 - 取消 */ - onConfirmModalCancel() { - this.setData({ - showConfirmModal: false, - readonly: false, - form: { - name: '', - idCard: '', - gender: '', - age: '', - height: '', - weight: '' - } + birthday, + 'form.age': String(age), }) }, @@ -331,67 +130,50 @@ Page({ }, onSubmit() { - const { name, idCard, gender, age, height, weight } = this.data.form + const { name, idCard, gender, height, weight, sex } = this.data.form + const { isFamily, rawUser } = this.data const heightNum = parseFloat(height) const weightNum = parseFloat(weight) - if (!name.trim()) { - wx.showToast({ title: '请填写姓名', icon: 'none' }) + // 昵称(仅家庭用户必填) + if (isFamily && !name.trim()) { + wx.showToast({ title: '请填写昵称', icon: 'none' }) return } - if (idCard.length !== 18) { - wx.showToast({ title: '请填写正确的身份证号', icon: 'none' }) + // 家庭用户性别必选 + if (isFamily && !gender) { + wx.showToast({ title: '请选择性别', icon: 'none' }) return } - if (!gender || !age) { - wx.showToast({ title: '身份证号有误,请检查', icon: 'none' }) + // 家庭用户出生年月必填 + if (isFamily && !this.data.birthday.trim()) { + 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.hasQueried) { - wx.showToast({ title: this.data.querying ? '身份信息验证中,请稍候' : '请检查姓名与身份证号', icon: 'none' }) - return - } - const parsed = this.parseIdCard(idCard) - const userInfo = wx.getStorageSync('userInfo') const connectDeviceInfoRaw = wx.getStorageSync('connectDeviceInfo') - const serverResult = this.data.serverResult - // selectedMember(抽屉选中) → serverResult(接口校验,可能为 null) → 表单/本地解析(用户输入) 依次覆盖 + // 以接口原始返回为 base,覆盖编辑后的字段 const payload: Record = { - ...(this.data.selectedMember ?? {}), - ...(serverResult ?? {}), - parentUserId: userInfo?.userId ?? '', - sn: lefuService.deviceInfo?.serialNumber ?? '', - scaleDeviceId: lefuService.deviceInfo?.serialNumber ?? '', + ...(rawUser ?? {}), + id: this.data.userId, realname: name.trim(), idCard, - sex: parsed.sex, - birthday: parsed.birthday, + sex: this.data.form.sex, + birthday: this.data.birthday, height: heightNum, - weight: weightNum, - connectDeviceInfo: connectDeviceInfoRaw ? JSON.stringify(connectDeviceInfoRaw) : '', + weight: isNaN(weightNum) ? 0 : weightNum, + connectDeviceInfo: connectDeviceInfoRaw ? JSON.stringify(connectDeviceInfoRaw) : (rawUser?.connectDeviceInfo ?? ''), } - if (this.data.isEdit) { - payload.id = this.data.userId - } - - const successMsg = this.data.isEdit ? '保存成功' : '添加成功' - const failMsg = this.data.isEdit ? '保存失败,请重试' : '添加失败,请重试' put('weighingScale/v3/edit/user', payload) .then(() => { - wx.showToast({ title: successMsg, icon: 'success' }) + wx.showToast({ title: '保存成功', icon: 'success' }) setTimeout(() => { - // 通知上一页(equipmentMember)刷新用户列表 const pages = getCurrentPages() const prevPage = pages[pages.length - 2] as any if (prevPage && prevPage.data?.sn) { @@ -401,7 +183,7 @@ Page({ }, 1500) }) .catch(() => { - wx.showToast({ title: failMsg, icon: 'none' }) + wx.showToast({ title: '保存失败,请重试', icon: 'none' }) }) } }) diff --git a/miniprogram/pages/editMember/editMember.wxml b/miniprogram/pages/editMember/editMember.wxml index 0ae86ce..d6d8706 100644 --- a/miniprogram/pages/editMember/editMember.wxml +++ b/miniprogram/pages/editMember/editMember.wxml @@ -1,157 +1,186 @@ - + - - - ! - 最多支持 10 位用户 - + + + ! + 最多支持 10 位用户 + - - - - - + + + + - - - - 姓名 - * - - - - - + + - - - - 身份证号 - * - - - - - - - - - - 性别 - - - - - {{ form.gender }} - - - 自动生成,无需填写 - + + + + 姓名 + + + + {{ form.name }} + + - - - - - - 年龄 - - - - - {{ form.age }} - - - 自动生成,无需填写 - - + + + + 单位 + + + + {{ workNo }} + + - - - - - - 身高 - * - - - - - cm + + + + 性别 + + + + {{ form.gender }} + + - - - - - - 体重 - * - - - - - kg + + + + 年龄 + + + + {{ form.age }} + + + - + + + + + 身高 + * + + + + + cm + + + + + + + + 体重 + + + + + kg + + + + + + + + + + + + + 昵称 + * + + + + + + + + + + 身份证号码 + + + + + + + + + + 性别 + * + + + + + + + + + + + + + 出生年月 + * + + + + + {{ birthday || '请选择出生年月' }} + + + + + + + + + 身高 + * + + + + + cm + + + + + + + + 体重 + + + + + kg + + + + + + + - - - 从已有用户中选择 - - - - - - - - 取消 - 保存用户信息 - - - - - - - - - + + + 取消 + 保存用户信息 +