diff --git a/src/api/BaseApi.ts b/src/api/BaseApi.ts index 84cc6e7..c8bf3ba 100644 --- a/src/api/BaseApi.ts +++ b/src/api/BaseApi.ts @@ -14,6 +14,15 @@ class Base extends Abstract { return this.getRequest({ url, isMsg: false }); } + /** + * 修改本人联系方式(手机号/邮箱,至少一项非空;后端经 UserBase 开放接口落库)。 + * 成功返回更新后的最新用户信息(BoaUser,含新 phone/email),供调用方刷新 store 规避 CAS principal 旧值。 + */ + updateContact(phone: string, email: string) { + const url = path + "/user/UpdateContact"; + return this.postRequest({ url, data: { phone, email } }); + } + /** *获取用户全部角色 * @memberof Basic diff --git a/src/views/Settings.vue b/src/views/Settings.vue index b3bb459..d61b6e8 100644 --- a/src/views/Settings.vue +++ b/src/views/Settings.vue @@ -88,8 +88,8 @@
- - + +
@@ -533,7 +533,7 @@ import { Message } from "view-ui-plus"; /** * 设置中心(/settings):左 216px 分组导航卡 + 右内容卡(B 方案左右双卡)。 * 五个 pane 已对接真实数据(设置对接 2026-09-30,零新接口): - * profile 读 store.userInfo(保存占位——接口待 UserBase 支持本人自助修改,本轮不做); + * profile 读 store.userInfo(手机/邮箱本人可改:UpdateContact 直写 UserBase,2026-10-02 任务B); * password 走 updatePassword + 确认登出流程(照抄 UserInfoModal); * avatar 移植 UserInfoModal 直传上传(预设色块/「使用默认头像」保持占位); * logo/wallpaper 管理列表 + 直传 + 删除照抄 SysSetting.vue,LOGO 保存走 saveSchLogo(schId 后端按登录人推断), @@ -662,7 +662,48 @@ function resetProfileForm() { profileForm.mobile = profileView.value.phone; profileForm.email = profileView.value.email; } -/** 保存占位:接口待 UserBase 支持本人自助修改,本轮不做(拍板结论) */ + +/* ===== 手机/邮箱保存(任务B 2026-10-02):/api/user/UpdateContact → UserBase /oapi/user/self/contact ===== */ +const profileSaving = ref(false); +/** 前端校验与后端同口径(UserBase ValidatorUtil.isPhone / CONTACT_EMAIL),双端拦截不一致数据 */ +const PHONE_RE = /^1[3-9]\d{9}$/; +const EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)+$/; + +async function onSaveProfile() { + const phone = profileForm.mobile.trim(); + const email = profileForm.email.trim(); + if (!phone && !email) { + Message.warning("手机号与邮箱至少填写一项"); + return; + } + if (phone && !PHONE_RE.test(phone)) { + Message.warning("手机号格式不正确"); + return; + } + if (email && email.length > 100) { + Message.warning("邮箱长度不能超过100字符"); + return; + } + if (email && !EMAIL_RE.test(email)) { + Message.warning("邮箱格式不正确"); + return; + } + if (profileSaving.value) return; + profileSaving.value = true; + try { + // 成功返回 UserBase 更新后的最新 BoaUser:合并进 store.userInfo 即时回显, + // 规避 CAS principal 缓存旧值(UserBase 侧缓存已随更新驱逐,下次 GetUserInfo 也是新值) + const fresh: any = await BaseApi.updateContact(phone, email); + if (fresh && typeof fresh === "object") { + store.userInfo = { ...store.userInfo, ...fresh }; + } + Message.success("保存成功"); + } catch (error) { + Message.error("保存失败:" + error); + } finally { + profileSaving.value = false; + } +} /* ===== password:updatePassword + 确认登出(流程照抄 UserInfoModal) ===== */ const passwordForm = reactive({ old: "", next: "", confirm: "" });