Files
jiapu/public/js/profile-pages.js
T
2026-07-31 11:31:26 +08:00

323 lines
9.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
(function (root, factory) {
// 个人资料模块同时支持浏览器页面和 Node 测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.ProfilePages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.ProfilePages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function trimOrUndefined(value) {
var text = value === undefined || value === null ? '' : String(value).trim();
return text || undefined;
}
function profileValue(profile, key, fallback) {
var value = profile && profile[key];
return value === undefined || value === null || value === '' ? fallback : value;
}
function getAvatarText(profile) {
// 头像字只取展示名第一个字符,避免接口没有头像时页面空白。
var displayName = profileValue(profile, 'nickName', '家');
return String(displayName).charAt(0) || '家';
}
function getFinalRegionCode(profile) {
var source = profile || {};
return source.districtCode || source.cityCode || source.provinceCode || '';
}
function buildRegionText(profile) {
return getFinalRegionCode(profile) ? '加载中...' : '待填写';
}
function buildProfileView(profile) {
// 页面展示模型和接口返回分开,后续改文案不影响接口契约。
return {
displayName: profileValue(profile, 'nickName', '未设置昵称'),
avatarText: getAvatarText(profile),
phone: profileValue(profile, 'phone', '未绑定'),
sex: profileValue(profile, 'sex', '待填写'),
birthday: profileValue(profile, 'birthday', '待填写'),
regionText: buildRegionText(profile || {})
};
}
function buildProfileUpdateBody(values) {
// 字段名称严格对应 PC YAML 里的 ProfileUpdateBody。
var source = values || {};
var body = {};
var avatar = trimOrUndefined(source.avatar);
[
'nickName',
'realName',
'sex',
'birthday',
'email'
].forEach(function (field) {
var value = trimOrUndefined(source[field]);
if (value !== undefined) body[field] = value;
});
if (avatar !== undefined) {
if (!/^\d+$/.test(avatar)) throw new Error('头像 OSS ID 必须是整数');
// YAML 将 avatar 标成 int64,但其示例已超过 JS 安全整数;浏览器按十进制字符串保留精度。
body.avatar = avatar;
}
return body;
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function shouldRedirectToLogin(api, error) {
var status = error && (error.status || error.code);
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
}
function redirectToLogin() {
if (!root.location) return;
if (typeof root.location.replace === 'function') {
root.location.replace('login.html');
return;
}
root.location.href = 'login.html';
}
function redirectUnauthorized(api, error) {
if (!shouldRedirectToLogin(api, error)) return false;
if (api && api.clearToken) api.clearToken();
redirectToLogin();
return true;
}
function query(selector, rootNode) {
if (!documentRef) return null;
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
if (!documentRef) return [];
return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector));
}
function showMessage(message) {
if (root.layui && root.layui.layer) {
root.layui.layer.msg(message);
return;
}
if (root.alert) {
root.alert(message);
}
}
function setStatus(form, message) {
var statusNode = query('[data-profile-status]', form);
if (statusNode) {
statusNode.textContent = message || '';
}
}
function setSubmitting(form, isSubmitting) {
queryAll('button[type="submit"]', form).forEach(function (button) {
button.disabled = isSubmitting;
});
}
function renderProfile(view) {
// 所有资料展示点用 data-profile-text 标记,避免按中文文案查找节点。
queryAll('[data-profile-text]').forEach(function (node) {
var key = node.getAttribute('data-profile-text');
var value = view[key];
if (value !== undefined && value !== null && value !== '') {
node.textContent = value;
}
});
}
function setProfileLoadStatus(message) {
var statusNode = query('[data-profile-load-status]');
if (statusNode) statusNode.textContent = message || '';
}
function buildUnavailableProfileView() {
return {
displayName: '账户资料暂时无法读取',
avatarText: '',
phone: '暂无数据',
birthday: '暂无数据',
regionText: '暂无数据'
};
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
values[field.name] = field.value;
});
return values;
}
function fillProfileForm(profile) {
var form = query('[data-profile-form]');
var source = profile || {};
if (!form) return;
queryAll('[name]', form).forEach(function (field) {
if (source[field.name] !== undefined && source[field.name] !== null) {
field.value = source[field.name];
}
});
}
function buildRegionPathText(items) {
return (Array.isArray(items) ? items : []).map(function (item) {
return item && item.regionName || '';
}).filter(Boolean).join(' / ');
}
async function renderRegionText(profile, requestId) {
var api = getApi();
var regionCode = getFinalRegionCode(profile);
var path;
var text = '待填写';
if (!regionCode || !api || !api.regionPath) {
renderProfile({ regionText: text });
return;
}
try {
path = await api.regionPath(regionCode);
text = buildRegionPathText(path) || text;
} catch (error) {
if (redirectUnauthorized(api, error)) return;
}
if (requestId === undefined || requestId === latestProfileRequestId) {
renderProfile({ regionText: text });
}
}
async function fillRegionPicker(profile) {
var regionPages = root.RegionPages;
if (!regionPages || !regionPages.applyProfileRegionSelection) return;
try {
await regionPages.applyProfileRegionSelection({
provinceCode: trimOrUndefined(profile && profile.provinceCode),
cityCode: trimOrUndefined(profile && profile.cityCode),
districtCode: trimOrUndefined(profile && profile.districtCode)
});
} catch (error) {
var api = getApi();
if (!redirectUnauthorized(api, error)) showMessage(error.message || '地区资料加载失败');
}
}
var latestProfileRequestId = 0;
async function loadProfile() {
var api = getApi();
var profile;
var requestId = latestProfileRequestId + 1;
latestProfileRequestId = requestId;
if (!api || !documentRef || !documentRef.querySelector('[data-profile-page]')) return;
if (redirectUnauthorized(api)) return;
setProfileLoadStatus('正在读取账户资料…');
try {
profile = await api.currentProfile();
if (requestId !== latestProfileRequestId) return;
renderProfile(buildProfileView(profile));
fillProfileForm(profile);
fillRegionPicker(profile);
renderRegionText(profile, requestId);
setProfileLoadStatus('');
} catch (error) {
if (redirectUnauthorized(api, error)) return;
renderProfile(buildUnavailableProfileView());
setProfileLoadStatus('资料读取失败,请刷新页面后重试。');
showMessage(error.message || '个人资料加载失败');
}
}
async function submitProfileForm(form) {
var api = getApi();
if (!api || !form) return;
if (redirectUnauthorized(api)) return;
try {
setSubmitting(form, true);
setStatus(form, '保存中...');
await api.updateProfile(buildProfileUpdateBody(getFormValues(form)));
setStatus(form, '已保存');
showMessage('个人资料已保存');
await loadProfile();
} catch (error) {
if (redirectUnauthorized(api, error)) return;
setStatus(form, '保存失败');
showMessage(error.message || '个人资料保存失败');
} finally {
setSubmitting(form, false);
}
}
function bindProfileForm() {
var form = query('[data-profile-form]');
if (!form) return;
form.addEventListener('submit', function (event) {
event.preventDefault();
submitProfileForm(form);
});
}
function init() {
bindProfileForm();
loadProfile();
}
return {
getAvatarText: getAvatarText,
getFinalRegionCode: getFinalRegionCode,
buildRegionText: buildRegionText,
buildProfileView: buildProfileView,
buildUnavailableProfileView: buildUnavailableProfileView,
buildProfileUpdateBody: buildProfileUpdateBody,
shouldRedirectToLogin: shouldRedirectToLogin,
reloadProfile: loadProfile,
init: init
};
});