现在有的接口对接测试完成
This commit is contained in:
@@ -125,6 +125,21 @@
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.rich-editor-host {
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
.rich-editor-toolbar {
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.rich-editor-content {
|
||||
height: 320px;
|
||||
}
|
||||
|
||||
.module-panel h2 {
|
||||
margin-bottom: 10px;
|
||||
font-family: "SimSun", "Songti SC", serif;
|
||||
@@ -523,6 +538,18 @@
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.notification-raw {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
overflow-wrap: anywhere;
|
||||
white-space: pre-wrap;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 15px;
|
||||
background: #fffdf8;
|
||||
color: var(--ink);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.module-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
+29
-25
@@ -18,22 +18,22 @@
|
||||
var FORM_CONFIG = {
|
||||
login: {
|
||||
selector: '#login-password-form',
|
||||
captchaScene: 'PC_SMS_LOGIN',
|
||||
operationCode: 'password-login',
|
||||
successUrl: 'profile.html'
|
||||
},
|
||||
'sms-login': {
|
||||
selector: '#login-sms-form',
|
||||
captchaScene: 'PC_SMS_LOGIN',
|
||||
operationCode: 'sms-login',
|
||||
successUrl: 'profile.html'
|
||||
},
|
||||
register: {
|
||||
selector: '#register-form',
|
||||
captchaScene: 'PC_REGISTER',
|
||||
operationCode: 'register',
|
||||
successUrl: 'login.html'
|
||||
},
|
||||
'password-reset': {
|
||||
selector: '#password-reset-form',
|
||||
captchaScene: 'PC_FORGOT_PASSWORD',
|
||||
operationCode: 'forgot-password',
|
||||
successUrl: 'login.html'
|
||||
}
|
||||
};
|
||||
@@ -47,16 +47,19 @@
|
||||
}
|
||||
|
||||
function buildLoginBody(values, hashFn) {
|
||||
// 这里只组装登录 DTO 字段,grantType、tenantId、clientId 由 api-client 统一补齐。
|
||||
// 这里只组装登录 DTO 字段,grantType、tenantId 由 api-client 统一补齐;clientid 只走请求头。
|
||||
var hash = hashFn || hashPassword;
|
||||
return {
|
||||
var body = {
|
||||
phone: values.phone,
|
||||
password: hash(values.password)
|
||||
};
|
||||
|
||||
if (values.validToken) body.validToken = values.validToken;
|
||||
return body;
|
||||
}
|
||||
|
||||
function buildSmsLoginBody(values) {
|
||||
// 短信登录接口只需要页面字段,grantType、tenantId、clientId 由 api-client 统一补齐。
|
||||
// 短信登录接口只需要页面字段,grantType、tenantId 由 api-client 统一补齐;clientid 只走请求头。
|
||||
return {
|
||||
phone: values.phone,
|
||||
smsCode: values.smsCode
|
||||
@@ -88,11 +91,11 @@
|
||||
return root.GenealogyApi && root.GenealogyApi.defaultClient;
|
||||
}
|
||||
|
||||
function getCaptchaScene(formType) {
|
||||
// PC/H5 认证场景来自后台验证码配置,客户端 Key 为 web_pc。
|
||||
function getCaptchaOperation(formType) {
|
||||
// PC 验证中心按业务动作解析服务端已激活的验证场景。
|
||||
var config = FORM_CONFIG[formType] || {};
|
||||
|
||||
return config.captchaScene || '';
|
||||
return config.operationCode || '';
|
||||
}
|
||||
|
||||
function getSuccessUrl(formType) {
|
||||
@@ -101,16 +104,16 @@
|
||||
return config.successUrl || '';
|
||||
}
|
||||
|
||||
function getFormCaptchaScene(form, formType) {
|
||||
// 场景码由当前脚本统一管理,避免散落在 HTML 属性中。
|
||||
var sceneCode = getCaptchaScene(formType);
|
||||
function getFormCaptchaOperation(form, formType) {
|
||||
// 业务动作由当前脚本统一管理,避免散落在 HTML 属性中。
|
||||
var operationCode = getCaptchaOperation(formType);
|
||||
|
||||
return sceneCode;
|
||||
return operationCode;
|
||||
}
|
||||
|
||||
async function ensureCaptcha(form, sceneCode, subject) {
|
||||
// 未配置 PC/H5 验证场景时不调用验证中心,也不伪造 validToken。
|
||||
if (!sceneCode) return true;
|
||||
async function ensureCaptcha(form, operationCode, subject) {
|
||||
// 未配置 PC 认证动作时不调用验证中心,也不伪造 validToken。
|
||||
if (!operationCode) return true;
|
||||
|
||||
if (!root.CaptchaPages || !root.CaptchaPages.ensureToken) {
|
||||
showMessage('验证码组件未加载,请刷新后重试');
|
||||
@@ -118,7 +121,7 @@
|
||||
}
|
||||
|
||||
return Boolean(await root.CaptchaPages.ensureToken(form, {
|
||||
sceneCode: sceneCode,
|
||||
operationCode: operationCode,
|
||||
subject: subject
|
||||
}));
|
||||
}
|
||||
@@ -310,6 +313,8 @@
|
||||
return;
|
||||
}
|
||||
if (!validateBeforeSubmit('login', values)) return;
|
||||
if (!await ensureCaptcha(form, getFormCaptchaOperation(form, 'login'), values.phone)) return;
|
||||
values = readForm(form);
|
||||
setBusy(button, true);
|
||||
try {
|
||||
await api.login(buildLoginBody(values));
|
||||
@@ -364,26 +369,25 @@
|
||||
}
|
||||
|
||||
async function sendCode(button) {
|
||||
// 短信验证码场景由表单配置决定,不在按钮属性中重复声明。
|
||||
// 短信验证码认证动作由表单配置决定,不在按钮属性中重复声明。
|
||||
var api = getApi();
|
||||
var form = button.closest('form');
|
||||
var values = readForm(form);
|
||||
var formType = getFormType(form);
|
||||
var sceneCode = getFormCaptchaScene(form, formType);
|
||||
var operationCode = getFormCaptchaOperation(form, formType);
|
||||
|
||||
if (!api) {
|
||||
showMessage('接口初始化失败,请刷新后重试');
|
||||
return;
|
||||
}
|
||||
if (!validateBeforeSendCode(values)) return;
|
||||
if (!await ensureCaptcha(form, sceneCode, values.phone)) return;
|
||||
if (!await ensureCaptcha(form, operationCode, values.phone)) return;
|
||||
|
||||
values = readForm(form);
|
||||
|
||||
setBusy(button, true);
|
||||
try {
|
||||
await api.sendSmsCode({
|
||||
sceneCode: sceneCode,
|
||||
await api.sendSmsCode(operationCode, {
|
||||
phone: values.phone,
|
||||
validToken: values.validToken || ''
|
||||
});
|
||||
@@ -473,8 +477,8 @@
|
||||
buildPasswordResetBody: buildPasswordResetBody,
|
||||
clearCaptchaToken: clearCaptchaToken,
|
||||
startSmsCooldown: startSmsCooldown,
|
||||
getCaptchaScene: getCaptchaScene,
|
||||
getFormCaptchaScene: getFormCaptchaScene,
|
||||
getCaptchaOperation: getCaptchaOperation,
|
||||
getFormCaptchaOperation: getFormCaptchaOperation,
|
||||
getSuccessUrl: getSuccessUrl,
|
||||
validateAuthValues: validateAuthValues,
|
||||
init: init
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
||||
'use strict';
|
||||
|
||||
var DEFAULT_SCENE_CODE = 'PC_SMS_LOGIN';
|
||||
var CAPTCHA_LAYER_AREA = ['318px', '318px'];
|
||||
var captchaLayerSeed = 0;
|
||||
|
||||
@@ -31,13 +30,11 @@
|
||||
}
|
||||
|
||||
function buildChallengeBody(options, api) {
|
||||
// 请求体字段严格对应 PC YAML 的 VerificationChallengeBody。
|
||||
// operationCode 属于路径,PC 验证请求体只提交租户和验证主体。
|
||||
var settings = options || {};
|
||||
|
||||
return {
|
||||
tenantId: settings.tenantId || api.tenantId,
|
||||
clientId: settings.clientId || api.clientId,
|
||||
sceneCode: settings.sceneCode || DEFAULT_SCENE_CODE,
|
||||
subject: settings.subject || ''
|
||||
};
|
||||
}
|
||||
@@ -74,8 +71,6 @@
|
||||
|
||||
return {
|
||||
tenantId: settings.tenantId || api.tenantId,
|
||||
clientId: settings.clientId || api.clientId,
|
||||
sceneCode: settings.sceneCode || DEFAULT_SCENE_CODE,
|
||||
subject: settings.subject || '',
|
||||
challengeId: settings.challengeId || source.challengeId || source.id,
|
||||
providerCode: settings.providerCode || 'tianai',
|
||||
@@ -87,7 +82,7 @@
|
||||
}
|
||||
|
||||
function normalizeVerifyTrack(data) {
|
||||
// 后端 /captcha/verify 要求轨迹统一放在 payload.track,并显式携带 left/top。
|
||||
// PC 验证接口要求轨迹统一放在 payload.track,并显式携带 left/top。
|
||||
var track = Object.assign({}, data || {});
|
||||
var trackList = Array.isArray(track.trackList) ? track.trackList : [];
|
||||
var first = trackList[0] || {};
|
||||
@@ -184,7 +179,7 @@
|
||||
}
|
||||
|
||||
function shouldRequireCaptcha(result) {
|
||||
// /captcha/require 返回 required=false 时允许直接继续业务请求。
|
||||
// PC require 返回 required=false 时允许直接继续业务请求。
|
||||
if (!result) return true;
|
||||
return result.required !== false;
|
||||
}
|
||||
@@ -213,8 +208,8 @@
|
||||
|
||||
config = new root.CaptchaConfig({
|
||||
bindEl: box,
|
||||
requestCaptchaDataUrl: api.buildApiUrl('/captcha/challenge'),
|
||||
validCaptchaUrl: api.buildApiUrl('/captcha/verify'),
|
||||
requestCaptchaDataUrl: api.captchaChallengeUrl(settings.operationCode),
|
||||
validCaptchaUrl: api.captchaVerifyUrl(settings.operationCode),
|
||||
requestHeaders: {
|
||||
clientid: api.clientId
|
||||
},
|
||||
@@ -301,10 +296,10 @@
|
||||
|
||||
if (!api) return '';
|
||||
if (field && field.value) return field.value;
|
||||
if (!settings.sceneCode) return '';
|
||||
if (!settings.operationCode) return '';
|
||||
|
||||
try {
|
||||
requirement = await api.captchaRequirement(buildChallengeBody(settings, api));
|
||||
requirement = await api.captchaRequirement(settings.operationCode, buildChallengeBody(settings, api));
|
||||
if (!shouldRequireCaptcha(requirement)) return 'CAPTCHA_NOT_REQUIRED';
|
||||
} catch (error) {
|
||||
// 查询策略失败时仍尝试直接拉起验证,避免认证接口缺少 validToken。
|
||||
|
||||
@@ -377,7 +377,7 @@
|
||||
genealogyId = requireGenealogyId();
|
||||
if (!genealogyId) return;
|
||||
// 富文本编辑器在提交前同步回 textarea,保证发送的是用户当前输入。
|
||||
if (root.KindEditor && root.KindEditor.sync) root.KindEditor.sync('#feedContent');
|
||||
if (root.AppRichEditor && root.AppRichEditor.syncAll) root.AppRichEditor.syncAll();
|
||||
body = buildFeedBody(getFormValues(form));
|
||||
if (!body.feedContent) {
|
||||
showMessage('请填写动态内容');
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
(function (root, factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(root);
|
||||
return;
|
||||
}
|
||||
|
||||
root.GenealogyEntryPages = factory(root);
|
||||
if (root.document) {
|
||||
root.document.addEventListener('DOMContentLoaded', function () {
|
||||
root.GenealogyEntryPages.init();
|
||||
});
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
||||
'use strict';
|
||||
|
||||
var TARGET_LABELS = {
|
||||
'profile-family-home.html': '家谱主页',
|
||||
'profile-feed.html': '家族圈动态',
|
||||
'profile-growth.html': '成长记录',
|
||||
'profile-relative.html': '亲友往来',
|
||||
'profile-memo.html': '备忘录',
|
||||
'profile-tree.html': '世系图',
|
||||
'profile-generation.html': '字辈谱'
|
||||
};
|
||||
|
||||
function getTargetPage(search) {
|
||||
var params = new URLSearchParams(String(search === undefined && root.location ? root.location.search : search || '').replace(/^\?/, ''));
|
||||
var target = params.get('next') || '';
|
||||
|
||||
return /^profile-[a-z-]+\.html$/.test(target) ? target : 'profile-family-home.html';
|
||||
}
|
||||
|
||||
function buildStatus(quota, target) {
|
||||
var source = quota || {};
|
||||
var details = [];
|
||||
var label = TARGET_LABELS[target] || '该家谱业务';
|
||||
|
||||
if (Number(source.createRemaining) >= 0) details.push('还可创建 ' + source.createRemaining + ' 部');
|
||||
if (Number(source.joinRemaining) >= 0) details.push('还可加入 ' + source.joinRemaining + ' 部');
|
||||
return '当前 PC 接口尚未提供“我的家谱”列表或家谱详情,不能伪造家谱编号进入“' + label + '”。' +
|
||||
(details.length ? ' 已读取额度:' + details.join(',') + '。' : '');
|
||||
}
|
||||
|
||||
async function init() {
|
||||
var status = root.document && root.document.querySelector('[data-genealogy-entry-status]');
|
||||
var api = root.GenealogyApi && root.GenealogyApi.defaultClient;
|
||||
var target = getTargetPage();
|
||||
|
||||
if (!status) return;
|
||||
if (!api || !api.genealogyQuota) {
|
||||
status.textContent = '家谱入口接口初始化失败,请刷新后重试。';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
status.textContent = buildStatus(await api.genealogyQuota(), target);
|
||||
} catch (error) {
|
||||
status.textContent = '无法读取家谱额度,请稍后重试。当前 PC 接口仍未提供家谱列表或详情,不能伪造家谱编号。';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getTargetPage: getTargetPage,
|
||||
buildStatus: buildStatus,
|
||||
init: init
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,301 @@
|
||||
(function (root, factory) {
|
||||
// 成长记录页面只消费 ApiClient 中已在 Apifox PC 目录核验的方法。
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(root);
|
||||
return;
|
||||
}
|
||||
|
||||
root.GrowthPages = factory(root);
|
||||
if (root.document) {
|
||||
root.document.addEventListener('DOMContentLoaded', function () {
|
||||
root.GrowthPages.init();
|
||||
});
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
||||
'use strict';
|
||||
|
||||
var documentRef = root.document;
|
||||
var writePending = false;
|
||||
|
||||
function getApi() {
|
||||
return root.GenealogyApi && root.GenealogyApi.defaultClient;
|
||||
}
|
||||
|
||||
function query(selector, rootNode) {
|
||||
return documentRef ? (rootNode || documentRef).querySelector(selector) : null;
|
||||
}
|
||||
|
||||
function queryAll(selector, rootNode) {
|
||||
return documentRef ? Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)) : [];
|
||||
}
|
||||
|
||||
function trimOrUndefined(value) {
|
||||
var text = String(value === undefined || value === null ? '' : value).trim();
|
||||
|
||||
return text || undefined;
|
||||
}
|
||||
|
||||
function toSafeIntegerOrUndefined(value) {
|
||||
var text = trimOrUndefined(value);
|
||||
var number;
|
||||
|
||||
if (!text) return undefined;
|
||||
number = Number(text);
|
||||
return Number.isInteger(number) && Number.isSafeInteger(number) ? number : undefined;
|
||||
}
|
||||
|
||||
function getQueryParam(search, name) {
|
||||
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
|
||||
|
||||
return params.get(name) || '';
|
||||
}
|
||||
|
||||
function getCurrentGenealogyId(search) {
|
||||
var page = query('[data-growth-page], [data-growth-edit-page]');
|
||||
var source = search === undefined && root.location ? root.location.search : search;
|
||||
var profileGenealogyId;
|
||||
|
||||
if (search === undefined && root.ProfileUI && root.ProfileUI.getGenealogyId) {
|
||||
profileGenealogyId = root.ProfileUI.getGenealogyId();
|
||||
if (profileGenealogyId) return profileGenealogyId;
|
||||
}
|
||||
return getQueryParam(source, 'genealogyId') || (page && page.getAttribute('data-genealogy-id')) || '';
|
||||
}
|
||||
|
||||
function buildGrowthRecordBody(values) {
|
||||
var source = values || {};
|
||||
var body = {
|
||||
recordTitle: String(source.recordTitle || '').trim()
|
||||
};
|
||||
var lineagePersonId = trimOrUndefined(source.lineagePersonId);
|
||||
var recordType = trimOrUndefined(source.recordType);
|
||||
var recordContent = trimOrUndefined(source.recordContent);
|
||||
var recordDate = trimOrUndefined(source.recordDate);
|
||||
var remindTime = trimOrUndefined(source.remindTime);
|
||||
var mediaOssIds = trimOrUndefined(source.mediaOssIds);
|
||||
var status = trimOrUndefined(source.status);
|
||||
var sortOrderText = trimOrUndefined(source.sortOrder);
|
||||
|
||||
if (lineagePersonId !== undefined) body.lineagePersonId = lineagePersonId;
|
||||
if (recordType !== undefined) body.recordType = recordType;
|
||||
if (recordContent !== undefined) body.recordContent = recordContent;
|
||||
if (recordDate !== undefined) body.recordDate = recordDate;
|
||||
if (remindTime !== undefined) body.remindTime = remindTime;
|
||||
if (mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds;
|
||||
if (sortOrderText !== undefined) body.sortOrder = toSafeIntegerOrUndefined(sortOrderText);
|
||||
if (status !== undefined) body.status = status;
|
||||
return body;
|
||||
}
|
||||
|
||||
function validateGrowthRecordBody(body) {
|
||||
if (!body || !body.recordTitle) return '请填写记录标题';
|
||||
if (body.lineagePersonId !== undefined && !/^\d+$/.test(body.lineagePersonId)) return '世系人物 ID 必须是整数';
|
||||
if (body.mediaOssIds !== undefined && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(body.mediaOssIds)) {
|
||||
return '附件 OSS ID 请使用英文逗号分隔的正整数';
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(body, 'sortOrder') && body.sortOrder === undefined) return '排序值必须是安全整数';
|
||||
return '';
|
||||
}
|
||||
|
||||
function normalizeGrowthList(data) {
|
||||
return Array.isArray(data) ? data : [];
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value === undefined || value === null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function stringifyRecord(record) {
|
||||
try {
|
||||
return typeof record === 'string' ? record : JSON.stringify(record);
|
||||
} catch (error) {
|
||||
return String(record);
|
||||
}
|
||||
}
|
||||
|
||||
function renderGrowthRecords(data) {
|
||||
var container = query('[data-growth-list]');
|
||||
var records = normalizeGrowthList(data);
|
||||
|
||||
if (!container) return;
|
||||
if (!Array.isArray(data)) {
|
||||
container.innerHTML = '<div class="api-empty">成长记录响应未按 Apifox ListResult 返回数组,无法安全展示或操作记录。</div>';
|
||||
return;
|
||||
}
|
||||
if (!records.length) {
|
||||
container.innerHTML = '<div class="api-empty">暂无成长记录</div>';
|
||||
return;
|
||||
}
|
||||
// ListResult 的元素 DTO 尚未展开,不能假设 recordId、标题或权限字段。
|
||||
container.innerHTML = records.map(function (record, index) {
|
||||
return '<article class="module-row"><div><h3>成长记录 ' + (index + 1) + '</h3><p>' +
|
||||
escapeHtml(stringifyRecord(record)) + '</p></div></article>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function showMessage(message) {
|
||||
if (root.layui && root.layui.layer) {
|
||||
root.layui.layer.msg(message);
|
||||
return;
|
||||
}
|
||||
if (root.alert) root.alert(message);
|
||||
}
|
||||
|
||||
function shouldRedirectToLogin(api, error) {
|
||||
var status = error && (error.status || error.code);
|
||||
|
||||
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
|
||||
}
|
||||
|
||||
function redirectUnauthorized(api, error) {
|
||||
if (!shouldRedirectToLogin(api, error)) return false;
|
||||
if (api && api.clearToken) api.clearToken();
|
||||
if (!root.location) return true;
|
||||
if (typeof root.location.replace === 'function') {
|
||||
root.location.replace('login.html');
|
||||
return true;
|
||||
}
|
||||
root.location.href = 'login.html';
|
||||
return true;
|
||||
}
|
||||
|
||||
function getFormValues(form) {
|
||||
var values = {};
|
||||
|
||||
queryAll('[name]', form).forEach(function (field) {
|
||||
values[field.name] = field.value;
|
||||
});
|
||||
return values;
|
||||
}
|
||||
|
||||
function setFormStatus(message) {
|
||||
var status = query('[data-growth-form-status]');
|
||||
|
||||
if (status) status.textContent = message || '';
|
||||
}
|
||||
|
||||
function setWritePending(pending) {
|
||||
writePending = Boolean(pending);
|
||||
queryAll('[data-growth-form] button, [data-growth-form] input, [data-growth-form] textarea').forEach(function (control) {
|
||||
control.disabled = writePending;
|
||||
});
|
||||
}
|
||||
|
||||
function syncGenealogyLinks(genealogyId) {
|
||||
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) {
|
||||
root.ProfileUI.syncGenealogyContextLinks();
|
||||
return;
|
||||
}
|
||||
queryAll('[data-genealogy-context-link]').forEach(function (link) {
|
||||
var href = link.getAttribute('href');
|
||||
var base;
|
||||
|
||||
if (!href || href === '#') return;
|
||||
base = href.split('?')[0];
|
||||
link.href = base + '?genealogyId=' + encodeURIComponent(genealogyId);
|
||||
});
|
||||
}
|
||||
|
||||
function renderNoContext() {
|
||||
var list = query('[data-growth-list]');
|
||||
|
||||
if (list) list.innerHTML = '<div class="api-empty">等待家谱入口接口;请从具体家谱进入成长记录。</div>';
|
||||
setFormStatus('等待家谱入口接口;请从具体家谱进入成长记录。');
|
||||
}
|
||||
|
||||
function requireGenealogyId() {
|
||||
var genealogyId = getCurrentGenealogyId();
|
||||
|
||||
if (!genealogyId) {
|
||||
renderNoContext();
|
||||
showMessage('等待家谱入口接口;请从具体家谱进入成长记录。');
|
||||
}
|
||||
return genealogyId;
|
||||
}
|
||||
|
||||
function buildListUrl(genealogyId) {
|
||||
return 'profile-growth.html?genealogyId=' + encodeURIComponent(genealogyId);
|
||||
}
|
||||
|
||||
async function loadGrowthRecords() {
|
||||
var api = getApi();
|
||||
var genealogyId;
|
||||
|
||||
if (redirectUnauthorized(api)) return;
|
||||
genealogyId = requireGenealogyId();
|
||||
if (!genealogyId) return;
|
||||
syncGenealogyLinks(genealogyId);
|
||||
try {
|
||||
renderGrowthRecords(await api.growthRecords(genealogyId));
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
renderGrowthRecords(null);
|
||||
showMessage(error.message || '成长记录加载失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitGrowthRecord(form) {
|
||||
var api = getApi();
|
||||
var genealogyId;
|
||||
var body;
|
||||
var validation;
|
||||
|
||||
if (writePending || redirectUnauthorized(api)) return;
|
||||
genealogyId = requireGenealogyId();
|
||||
if (!genealogyId) return;
|
||||
body = buildGrowthRecordBody(getFormValues(form));
|
||||
validation = validateGrowthRecordBody(body);
|
||||
if (validation) {
|
||||
setFormStatus(validation);
|
||||
return;
|
||||
}
|
||||
setWritePending(true);
|
||||
setFormStatus('正在保存成长记录...');
|
||||
try {
|
||||
await api.createGrowthRecord(genealogyId, body);
|
||||
if (root.location) root.location.href = buildListUrl(genealogyId);
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
setFormStatus(error.message || '成长记录保存失败');
|
||||
} finally {
|
||||
setWritePending(false);
|
||||
}
|
||||
}
|
||||
|
||||
function bindActions() {
|
||||
if (!documentRef) return;
|
||||
documentRef.addEventListener('submit', function (event) {
|
||||
var form = event.target.closest('[data-growth-form]');
|
||||
|
||||
if (!form) return;
|
||||
event.preventDefault();
|
||||
submitGrowthRecord(form);
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
var genealogyId;
|
||||
|
||||
if (!documentRef) return;
|
||||
bindActions();
|
||||
if (query('[data-growth-page]')) loadGrowthRecords();
|
||||
if (query('[data-growth-edit-page]')) {
|
||||
genealogyId = requireGenealogyId();
|
||||
if (genealogyId) syncGenealogyLinks(genealogyId);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getCurrentGenealogyId: getCurrentGenealogyId,
|
||||
buildGrowthRecordBody: buildGrowthRecordBody,
|
||||
validateGrowthRecordBody: validateGrowthRecordBody,
|
||||
normalizeGrowthList: normalizeGrowthList,
|
||||
shouldRedirectToLogin: shouldRedirectToLogin,
|
||||
init: init
|
||||
};
|
||||
});
|
||||
Vendored
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 371 B |
Binary file not shown.
|
Before Width: | Height: | Size: 43 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,948 +0,0 @@
|
||||
/* common */
|
||||
.ke-inline-block {
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.ke-clearfix {
|
||||
zoom: 1;
|
||||
}
|
||||
.ke-clearfix:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
clear: both;
|
||||
font-size: 0;
|
||||
height: 0;
|
||||
line-height: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
.ke-shadow {
|
||||
box-shadow: 1px 1px 3px #A0A0A0;
|
||||
-moz-box-shadow: 1px 1px 3px #A0A0A0;
|
||||
-webkit-box-shadow: 1px 1px 3px #A0A0A0;
|
||||
filter: progid:DXImageTransform.Microsoft.Shadow(color='#A0A0A0', Direction=135, Strength=3);
|
||||
background-color: #F0F0EE;
|
||||
}
|
||||
.ke-menu a,
|
||||
.ke-menu a:hover,
|
||||
.ke-dialog a,
|
||||
.ke-dialog a:hover {
|
||||
color: #337FE5;
|
||||
text-decoration: none;
|
||||
}
|
||||
/* icons */
|
||||
.ke-icon-source {
|
||||
background-position: 0px 0px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-preview {
|
||||
background-position: 0px -16px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-print {
|
||||
background-position: 0px -32px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-undo {
|
||||
background-position: 0px -48px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-redo {
|
||||
background-position: 0px -64px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-cut {
|
||||
background-position: 0px -80px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-copy {
|
||||
background-position: 0px -96px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-paste {
|
||||
background-position: 0px -112px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-selectall {
|
||||
background-position: 0px -128px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-justifyleft {
|
||||
background-position: 0px -144px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-justifycenter {
|
||||
background-position: 0px -160px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-justifyright {
|
||||
background-position: 0px -176px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-justifyfull {
|
||||
background-position: 0px -192px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-insertorderedlist {
|
||||
background-position: 0px -208px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-insertunorderedlist {
|
||||
background-position: 0px -224px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-indent {
|
||||
background-position: 0px -240px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-outdent {
|
||||
background-position: 0px -256px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-subscript {
|
||||
background-position: 0px -272px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-superscript {
|
||||
background-position: 0px -288px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-date {
|
||||
background-position: 0px -304px;
|
||||
width: 25px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-time {
|
||||
background-position: 0px -320px;
|
||||
width: 25px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-formatblock {
|
||||
background-position: 0px -336px;
|
||||
width: 25px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-fontname {
|
||||
background-position: 0px -352px;
|
||||
width: 21px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-fontsize {
|
||||
background-position: 0px -368px;
|
||||
width: 23px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-forecolor {
|
||||
background-position: 0px -384px;
|
||||
width: 20px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-hilitecolor {
|
||||
background-position: 0px -400px;
|
||||
width: 23px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-bold {
|
||||
background-position: 0px -416px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-italic {
|
||||
background-position: 0px -432px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-underline {
|
||||
background-position: 0px -448px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-strikethrough {
|
||||
background-position: 0px -464px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-removeformat {
|
||||
background-position: 0px -480px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-image {
|
||||
background-position: 0px -496px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-flash {
|
||||
background-position: 0px -512px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-media {
|
||||
background-position: 0px -528px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-div {
|
||||
background-position: 0px -544px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-formula {
|
||||
background-position: 0px -576px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-hr {
|
||||
background-position: 0px -592px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-emoticons {
|
||||
background-position: 0px -608px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-link {
|
||||
background-position: 0px -624px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-unlink {
|
||||
background-position: 0px -640px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-fullscreen {
|
||||
background-position: 0px -656px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-about {
|
||||
background-position: 0px -672px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-plainpaste {
|
||||
background-position: 0px -704px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-wordpaste {
|
||||
background-position: 0px -720px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-table {
|
||||
background-position: 0px -784px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablemenu {
|
||||
background-position: 0px -768px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tableinsert {
|
||||
background-position: 0px -784px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tabledelete {
|
||||
background-position: 0px -800px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablecolinsertleft {
|
||||
background-position: 0px -816px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablecolinsertright {
|
||||
background-position: 0px -832px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablerowinsertabove {
|
||||
background-position: 0px -848px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablerowinsertbelow {
|
||||
background-position: 0px -864px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablecoldelete {
|
||||
background-position: 0px -880px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablerowdelete {
|
||||
background-position: 0px -896px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablecellprop {
|
||||
background-position: 0px -912px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tableprop {
|
||||
background-position: 0px -928px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-checked {
|
||||
background-position: 0px -944px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-code {
|
||||
background-position: 0px -960px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-map {
|
||||
background-position: 0px -976px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-baidumap {
|
||||
background-position: 0px -976px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-lineheight {
|
||||
background-position: 0px -992px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-clearhtml {
|
||||
background-position: 0px -1008px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-pagebreak {
|
||||
background-position: 0px -1024px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-insertfile {
|
||||
background-position: 0px -1040px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-quickformat {
|
||||
background-position: 0px -1056px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-template {
|
||||
background-position: 0px -1072px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablecellsplit {
|
||||
background-position: 0px -1088px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablerowmerge {
|
||||
background-position: 0px -1104px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablerowsplit {
|
||||
background-position: 0px -1120px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablecolmerge {
|
||||
background-position: 0px -1136px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-tablecolsplit {
|
||||
background-position: 0px -1152px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-anchor {
|
||||
background-position: 0px -1168px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-search {
|
||||
background-position: 0px -1184px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-new {
|
||||
background-position: 0px -1200px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-specialchar {
|
||||
background-position: 0px -1216px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-multiimage {
|
||||
background-position: 0px -1232px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-zijianju {
|
||||
background-position: 0px -1248px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ke-icon-duansuo {
|
||||
background-position: 0px -1264px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
/* container */
|
||||
.ke-container {
|
||||
display: block;
|
||||
/*border: 1px solid #CCCCCC;*/
|
||||
background-color: #FFF;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
/* toolbar */
|
||||
.ke-toolbar {
|
||||
border-bottom: 1px solid #CCC;
|
||||
background-color: #F0F0EE;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
zoom: 1;
|
||||
}
|
||||
.ke-toolbar-icon {
|
||||
background-repeat: no-repeat;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
.ke-toolbar-icon-url {
|
||||
background-image: url(default.png);
|
||||
}
|
||||
.ke-toolbar .ke-outline {
|
||||
border: 1px solid #F0F0EE;
|
||||
/*margin: 1px;*/
|
||||
margin: 8px 9px;
|
||||
padding: 1px 2px;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.ke-toolbar .ke-on {
|
||||
border: 1px solid #5690D2;
|
||||
}
|
||||
.ke-toolbar .ke-selected {
|
||||
border: 1px solid #5690D2;
|
||||
background-color: #E9EFF6;
|
||||
}
|
||||
.ke-toolbar .ke-disabled {
|
||||
cursor: default;
|
||||
}
|
||||
.ke-toolbar .ke-separator {
|
||||
height: 16px;
|
||||
margin: 2px 3px;
|
||||
border-left: 1px solid #A0A0A0;
|
||||
border-right: 1px solid #FFFFFF;
|
||||
border-top:0;
|
||||
border-bottom:0;
|
||||
width: 0;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.ke-toolbar .ke-hr {
|
||||
overflow: hidden;
|
||||
height: 1px;
|
||||
clear: both;
|
||||
}
|
||||
/* edit */
|
||||
.ke-edit {
|
||||
padding: 0;
|
||||
}
|
||||
.ke-edit-iframe,
|
||||
.ke-edit-textarea {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
.ke-edit-textarea {
|
||||
font: 12px/1.5 "Consolas", "Monaco", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
|
||||
color: #000;
|
||||
overflow: auto;
|
||||
resize: none;
|
||||
}
|
||||
.ke-edit-textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
/* statusbar */
|
||||
.ke-statusbar {
|
||||
position: relative;
|
||||
background-color: #F0F0EE;
|
||||
border-top: 1px solid #CCCCCC;
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
*height: 12px;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
cursor: s-resize;
|
||||
}
|
||||
.ke-statusbar-center-icon {
|
||||
background-position: -0px -754px;
|
||||
width: 15px;
|
||||
height: 11px;
|
||||
background-image: url(default.png);
|
||||
}
|
||||
.ke-statusbar-right-icon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
cursor: se-resize;
|
||||
background-position: -5px -741px;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
background-image: url(default.png);
|
||||
}
|
||||
/* menu */
|
||||
.ke-menu {
|
||||
border: 1px solid #A0A0A0;
|
||||
background-color: #F1F1F1;
|
||||
color: #222222;
|
||||
padding: 2px;
|
||||
font-family: "sans serif",tahoma,verdana,helvetica;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ke-menu-item {
|
||||
border: 1px solid #F1F1F1;
|
||||
background-color: #F1F1F1;
|
||||
color: #222222;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ke-menu-item-on {
|
||||
border: 1px solid #5690D2;
|
||||
background-color: #E9EFF6;
|
||||
}
|
||||
.ke-menu-item-left {
|
||||
width: 27px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ke-menu-item-center {
|
||||
width: 0;
|
||||
height: 24px;
|
||||
border-left: 1px solid #E3E3E3;
|
||||
border-right: 1px solid #FFFFFF;
|
||||
border-top: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.ke-menu-item-center-on {
|
||||
border-left: 1px solid #E9EFF6;
|
||||
border-right: 1px solid #E9EFF6;
|
||||
}
|
||||
.ke-menu-item-right {
|
||||
border: 0;
|
||||
padding: 0 0 0 5px;
|
||||
line-height: 24px;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ke-menu-separator {
|
||||
margin: 2px 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
border-top: 1px solid #CCCCCC;
|
||||
border-bottom: 1px solid #FFFFFF;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
/* colorpicker */
|
||||
.ke-colorpicker {
|
||||
border: 1px solid #A0A0A0;
|
||||
background-color: #F1F1F1;
|
||||
color: #222222;
|
||||
padding: 2px;
|
||||
}
|
||||
.ke-colorpicker-table {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
border-collapse: separate;
|
||||
}
|
||||
.ke-colorpicker-cell {
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
border: 1px solid #F0F0EE;
|
||||
cursor: pointer;
|
||||
margin:3px;
|
||||
padding:0;
|
||||
}
|
||||
.ke-colorpicker-cell-top {
|
||||
font-family: "sans serif",tahoma,verdana,helvetica;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
border: 1px solid #F0F0EE;
|
||||
cursor: pointer;
|
||||
margin:0;
|
||||
padding:0;
|
||||
text-align: center;
|
||||
}
|
||||
.ke-colorpicker-cell-on {
|
||||
border: 1px solid #5690D2;
|
||||
}
|
||||
.ke-colorpicker-cell-selected {
|
||||
border: 1px solid #2446AB;
|
||||
}
|
||||
.ke-colorpicker-cell-color {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin: 3px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
/* dialog */
|
||||
.ke-dialog {
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ke-dialog .ke-header {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ke-dialog .ke-header .ke-left {
|
||||
float: left;
|
||||
}
|
||||
.ke-dialog .ke-header .ke-right {
|
||||
float: right;
|
||||
}
|
||||
.ke-dialog .ke-header label {
|
||||
margin-right: 0;
|
||||
cursor: pointer;
|
||||
font-weight: normal;
|
||||
display: inline;
|
||||
vertical-align: top;
|
||||
}
|
||||
.ke-dialog-content {
|
||||
background-color: #FFF;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #333;
|
||||
border: 1px solid #A0A0A0;
|
||||
}
|
||||
.ke-dialog-shadow {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: 3px 3px 7px #999;
|
||||
-moz-box-shadow: 3px 3px 7px #999;
|
||||
-webkit-box-shadow: 3px 3px 7px #999;
|
||||
filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius='3', MakeShadow='true', ShadowOpacity='0.4');
|
||||
background-color: #F0F0EE;
|
||||
}
|
||||
.ke-dialog-header {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding: 0 10px;
|
||||
background: url(background.png) repeat scroll 0 0 #F0F0EE;
|
||||
border-bottom: 1px solid #CFCFCF;
|
||||
height: 24px;
|
||||
font: 12px/24px "sans serif",tahoma,verdana,helvetica;
|
||||
text-align: left;
|
||||
color: #222;
|
||||
cursor: move;
|
||||
}
|
||||
.ke-dialog-icon-close {
|
||||
display: block;
|
||||
background: url(default.png) no-repeat scroll 0px -688px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ke-dialog-body {
|
||||
font: 12px/1.5 "sans serif",tahoma,verdana,helvetica;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
.ke-dialog-body textarea {
|
||||
display: block;
|
||||
overflow: auto;
|
||||
padding: 0;
|
||||
resize: none;
|
||||
}
|
||||
.ke-dialog-body textarea:focus,
|
||||
.ke-dialog-body input:focus,
|
||||
.ke-dialog-body select:focus {
|
||||
outline: none;
|
||||
}
|
||||
.ke-dialog-body label {
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.ke-dialog-body img {
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.ke-dialog-body select {
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
width: auto;
|
||||
}
|
||||
.ke-dialog-body .ke-textarea {
|
||||
display: block;
|
||||
width: 408px;
|
||||
height: 260px;
|
||||
font-family: "sans serif",tahoma,verdana,helvetica;
|
||||
font-size: 12px;
|
||||
border-color: #848484 #E0E0E0 #E0E0E0 #848484;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
.ke-dialog-body .ke-form {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ke-dialog-loading {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 1px;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
}
|
||||
.ke-dialog-loading-content {
|
||||
background: url("../common/loading.gif") no-repeat;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
height: 31px;
|
||||
line-height: 31px;
|
||||
padding-left: 36px;
|
||||
}
|
||||
.ke-dialog-row {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.ke-dialog-footer {
|
||||
font: 12px/1 "sans serif",tahoma,verdana,helvetica;
|
||||
text-align: right;
|
||||
padding:0 0 5px 0;
|
||||
background-color: #FFF;
|
||||
width: 100%;
|
||||
}
|
||||
.ke-dialog-preview,
|
||||
.ke-dialog-yes {
|
||||
margin: 5px;
|
||||
}
|
||||
.ke-dialog-no {
|
||||
margin: 5px 10px 5px 5px;
|
||||
}
|
||||
.ke-dialog-mask {
|
||||
background-color:#FFF;
|
||||
filter:alpha(opacity=50);
|
||||
opacity:0.5;
|
||||
}
|
||||
.ke-button-common {
|
||||
background: url(background.png) no-repeat;
|
||||
cursor: pointer;
|
||||
height: 23px;
|
||||
line-height: 23px;
|
||||
overflow: visible;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ke-button-outer {
|
||||
background-position: 0 -25px;
|
||||
padding: 0;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.ke-button {
|
||||
background-position: right -25px;
|
||||
padding: 0 14px 0 12px;
|
||||
margin: 0 0 0 2px;
|
||||
font-family: "sans serif",tahoma,verdana,helvetica;
|
||||
border: 0 none;
|
||||
color: #333;
|
||||
font-size: 12px;
|
||||
text-decoration: none;
|
||||
}
|
||||
/* inputbox */
|
||||
.ke-input-text {
|
||||
background-color:#FFFFFF;
|
||||
font-family: "sans serif",tahoma,verdana,helvetica;
|
||||
font-size: 12px;
|
||||
line-height: 17px;
|
||||
height: 17px;
|
||||
padding: 2px 4px;
|
||||
border-color: #848484 #E0E0E0 #E0E0E0 #848484;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.ke-input-number {
|
||||
width: 50px;
|
||||
}
|
||||
.ke-input-color {
|
||||
border: 1px solid #A0A0A0;
|
||||
background-color: #FFFFFF;
|
||||
font-size: 12px;
|
||||
width: 60px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
padding-left: 5px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.ke-upload-button {
|
||||
position: relative;
|
||||
}
|
||||
.ke-upload-area {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
*height: 25px;
|
||||
}
|
||||
.ke-upload-area .ke-upload-file {
|
||||
position: absolute;
|
||||
font-size: 60px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
z-index: 811212;
|
||||
border: 0 none;
|
||||
opacity: 0;
|
||||
filter: alpha(opacity=0);
|
||||
}
|
||||
/* tabs */
|
||||
.ke-tabs {
|
||||
font: 12px/1 "sans serif",tahoma,verdana,helvetica;
|
||||
border-bottom:1px solid #A0A0A0;
|
||||
padding-left:5px;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
.ke-tabs-ul {
|
||||
list-style-image:none;
|
||||
list-style-position:outside;
|
||||
list-style-type:none;
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
.ke-tabs-li {
|
||||
position: relative;
|
||||
border: 1px solid #A0A0A0;
|
||||
background-color: #F0F0EE;
|
||||
margin: 0 2px -1px 0;
|
||||
padding: 0 20px;
|
||||
float: left;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
color: #555555;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ke-tabs-li-selected {
|
||||
background-color: #FFF;
|
||||
border-bottom: 1px solid #FFF;
|
||||
color: #000;
|
||||
cursor: default;
|
||||
}
|
||||
.ke-tabs-li-on {
|
||||
background-color: #FFF;
|
||||
color: #000;
|
||||
}
|
||||
/* progressbar */
|
||||
.ke-progressbar {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ke-progressbar-bar {
|
||||
border: 1px solid #6FA5DB;
|
||||
width: 80px;
|
||||
height: 5px;
|
||||
margin: 10px 10px 0 10px;
|
||||
padding: 0;
|
||||
}
|
||||
.ke-progressbar-bar-inner {
|
||||
width: 0;
|
||||
height: 5px;
|
||||
background-color: #6FA5DB;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.ke-progressbar-percent {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 40%;
|
||||
display: none;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,289 @@
|
||||
(function (root, factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(root);
|
||||
return;
|
||||
}
|
||||
|
||||
root.MemoPages = factory(root);
|
||||
if (root.document) {
|
||||
root.document.addEventListener('DOMContentLoaded', function () {
|
||||
root.MemoPages.init();
|
||||
});
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
||||
'use strict';
|
||||
|
||||
var documentRef = root.document;
|
||||
var writePending = false;
|
||||
|
||||
function getApi() {
|
||||
return root.GenealogyApi && root.GenealogyApi.defaultClient;
|
||||
}
|
||||
|
||||
function query(selector, rootNode) {
|
||||
return documentRef ? (rootNode || documentRef).querySelector(selector) : null;
|
||||
}
|
||||
|
||||
function queryAll(selector, rootNode) {
|
||||
return documentRef ? Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)) : [];
|
||||
}
|
||||
|
||||
function trimOrUndefined(value) {
|
||||
var text = String(value === undefined || value === null ? '' : value).trim();
|
||||
|
||||
return text || undefined;
|
||||
}
|
||||
|
||||
function toSafeIntegerOrUndefined(value) {
|
||||
var text = trimOrUndefined(value);
|
||||
var number;
|
||||
|
||||
if (!text) return undefined;
|
||||
number = Number(text);
|
||||
return Number.isInteger(number) && Number.isSafeInteger(number) ? number : undefined;
|
||||
}
|
||||
|
||||
function getQueryParam(search, name) {
|
||||
var params = new URLSearchParams(String(search || '').replace(/^\?/, ''));
|
||||
|
||||
return params.get(name) || '';
|
||||
}
|
||||
|
||||
function getCurrentGenealogyId(search) {
|
||||
var page = query('[data-memo-page], [data-memo-edit-page]');
|
||||
var source = search === undefined && root.location ? root.location.search : search;
|
||||
var profileGenealogyId;
|
||||
|
||||
if (search === undefined && root.ProfileUI && root.ProfileUI.getGenealogyId) {
|
||||
profileGenealogyId = root.ProfileUI.getGenealogyId();
|
||||
if (profileGenealogyId) return profileGenealogyId;
|
||||
}
|
||||
return getQueryParam(source, 'genealogyId') || (page && page.getAttribute('data-genealogy-id')) || '';
|
||||
}
|
||||
|
||||
function buildMemoBody(values) {
|
||||
var source = values || {};
|
||||
var body = { memoTitle: String(source.memoTitle || '').trim() };
|
||||
var fields = ['memoContent', 'remindTime', 'completed', 'mediaOssIds', 'status'];
|
||||
var sortOrderText = trimOrUndefined(source.sortOrder);
|
||||
|
||||
fields.forEach(function (field) {
|
||||
var value = trimOrUndefined(source[field]);
|
||||
|
||||
if (value !== undefined) body[field] = value;
|
||||
});
|
||||
if (sortOrderText !== undefined) body.sortOrder = toSafeIntegerOrUndefined(sortOrderText);
|
||||
return body;
|
||||
}
|
||||
|
||||
function validateMemoBody(body) {
|
||||
if (!body || !body.memoTitle) return '请填写备忘标题';
|
||||
if (body.mediaOssIds !== undefined && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(body.mediaOssIds)) {
|
||||
return '附件 OSS ID 请使用英文逗号分隔的正整数';
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(body, 'sortOrder') && body.sortOrder === undefined) return '排序值必须是安全整数';
|
||||
return '';
|
||||
}
|
||||
|
||||
function normalizeMemoList(data) {
|
||||
return Array.isArray(data) ? data : [];
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value === undefined || value === null ? '' : value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function stringifyMemo(memo) {
|
||||
try {
|
||||
return typeof memo === 'string' ? memo : JSON.stringify(memo);
|
||||
} catch (error) {
|
||||
return String(memo);
|
||||
}
|
||||
}
|
||||
|
||||
function renderMemos(data) {
|
||||
var container = query('[data-memo-list]');
|
||||
var memos = normalizeMemoList(data);
|
||||
|
||||
if (!container) return;
|
||||
if (!Array.isArray(data)) {
|
||||
container.innerHTML = '<div class="api-empty">备忘录响应未按 Apifox ListResult 返回数组,无法安全展示或操作记录。</div>';
|
||||
return;
|
||||
}
|
||||
if (!memos.length) {
|
||||
container.innerHTML = '<div class="api-empty">暂无备忘录</div>';
|
||||
return;
|
||||
}
|
||||
// ListResult 的元素 DTO 尚未展开,不能假设 memoId、标题或权限字段。
|
||||
container.innerHTML = memos.map(function (memo, index) {
|
||||
return '<article class="module-row"><div><h3>备忘录 ' + (index + 1) + '</h3><p>' +
|
||||
escapeHtml(stringifyMemo(memo)) + '</p></div></article>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function showMessage(message) {
|
||||
if (root.layui && root.layui.layer) {
|
||||
root.layui.layer.msg(message);
|
||||
return;
|
||||
}
|
||||
if (root.alert) root.alert(message);
|
||||
}
|
||||
|
||||
function shouldRedirectToLogin(api, error) {
|
||||
var status = error && (error.status || error.code);
|
||||
|
||||
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
|
||||
}
|
||||
|
||||
function redirectUnauthorized(api, error) {
|
||||
if (!shouldRedirectToLogin(api, error)) return false;
|
||||
if (api && api.clearToken) api.clearToken();
|
||||
if (!root.location) return true;
|
||||
if (typeof root.location.replace === 'function') {
|
||||
root.location.replace('login.html');
|
||||
return true;
|
||||
}
|
||||
root.location.href = 'login.html';
|
||||
return true;
|
||||
}
|
||||
|
||||
function getFormValues(form) {
|
||||
var values = {};
|
||||
|
||||
queryAll('[name]', form).forEach(function (field) {
|
||||
values[field.name] = field.value;
|
||||
});
|
||||
return values;
|
||||
}
|
||||
|
||||
function setFormStatus(message) {
|
||||
var status = query('[data-memo-form-status]');
|
||||
|
||||
if (status) status.textContent = message || '';
|
||||
}
|
||||
|
||||
function setWritePending(pending) {
|
||||
writePending = Boolean(pending);
|
||||
queryAll('[data-memo-form] button, [data-memo-form] input, [data-memo-form] textarea, [data-memo-form] select').forEach(function (control) {
|
||||
control.disabled = writePending;
|
||||
});
|
||||
}
|
||||
|
||||
function syncGenealogyLinks(genealogyId) {
|
||||
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) {
|
||||
root.ProfileUI.syncGenealogyContextLinks();
|
||||
return;
|
||||
}
|
||||
queryAll('[data-genealogy-context-link]').forEach(function (link) {
|
||||
var href = link.getAttribute('href');
|
||||
var base;
|
||||
|
||||
if (!href || href === '#') return;
|
||||
base = href.split('?')[0];
|
||||
link.href = base + '?genealogyId=' + encodeURIComponent(genealogyId);
|
||||
});
|
||||
}
|
||||
|
||||
function renderNoContext() {
|
||||
var list = query('[data-memo-list]');
|
||||
|
||||
if (list) list.innerHTML = '<div class="api-empty">等待家谱入口接口;请从具体家谱进入备忘录。</div>';
|
||||
setFormStatus('等待家谱入口接口;请从具体家谱进入备忘录。');
|
||||
}
|
||||
|
||||
function requireGenealogyId() {
|
||||
var genealogyId = getCurrentGenealogyId();
|
||||
|
||||
if (!genealogyId) {
|
||||
renderNoContext();
|
||||
showMessage('等待家谱入口接口;请从具体家谱进入备忘录。');
|
||||
}
|
||||
return genealogyId;
|
||||
}
|
||||
|
||||
function buildListUrl(genealogyId) {
|
||||
return 'profile-memo.html?genealogyId=' + encodeURIComponent(genealogyId);
|
||||
}
|
||||
|
||||
async function loadMemos() {
|
||||
var api = getApi();
|
||||
var genealogyId;
|
||||
|
||||
if (redirectUnauthorized(api)) return;
|
||||
genealogyId = requireGenealogyId();
|
||||
if (!genealogyId) return;
|
||||
syncGenealogyLinks(genealogyId);
|
||||
try {
|
||||
renderMemos(await api.memos(genealogyId));
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
renderMemos(null);
|
||||
showMessage(error.message || '备忘录加载失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function submitMemo(form) {
|
||||
var api = getApi();
|
||||
var genealogyId;
|
||||
var body;
|
||||
var validation;
|
||||
|
||||
if (writePending || redirectUnauthorized(api)) return;
|
||||
genealogyId = requireGenealogyId();
|
||||
if (!genealogyId) return;
|
||||
body = buildMemoBody(getFormValues(form));
|
||||
validation = validateMemoBody(body);
|
||||
if (validation) {
|
||||
setFormStatus(validation);
|
||||
return;
|
||||
}
|
||||
setWritePending(true);
|
||||
setFormStatus('正在保存备忘录...');
|
||||
try {
|
||||
await api.createMemo(genealogyId, body);
|
||||
if (root.location) root.location.href = buildListUrl(genealogyId);
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
setFormStatus(error.message || '备忘录保存失败');
|
||||
} finally {
|
||||
setWritePending(false);
|
||||
}
|
||||
}
|
||||
|
||||
function bindActions() {
|
||||
if (!documentRef) return;
|
||||
documentRef.addEventListener('submit', function (event) {
|
||||
var form = event.target.closest('[data-memo-form]');
|
||||
|
||||
if (!form) return;
|
||||
event.preventDefault();
|
||||
submitMemo(form);
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
var genealogyId;
|
||||
|
||||
if (!documentRef) return;
|
||||
bindActions();
|
||||
if (query('[data-memo-page]')) loadMemos();
|
||||
if (query('[data-memo-edit-page]')) {
|
||||
genealogyId = requireGenealogyId();
|
||||
if (genealogyId) syncGenealogyLinks(genealogyId);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
getCurrentGenealogyId: getCurrentGenealogyId,
|
||||
buildMemoBody: buildMemoBody,
|
||||
validateMemoBody: validateMemoBody,
|
||||
normalizeMemoList: normalizeMemoList,
|
||||
shouldRedirectToLogin: shouldRedirectToLogin,
|
||||
init: init
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,132 @@
|
||||
(function (root, factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(root);
|
||||
return;
|
||||
}
|
||||
|
||||
root.NotificationPages = factory(root);
|
||||
if (root.document) {
|
||||
root.document.addEventListener('DOMContentLoaded', function () {
|
||||
root.NotificationPages.init();
|
||||
});
|
||||
}
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
||||
'use strict';
|
||||
|
||||
var documentRef = root.document;
|
||||
|
||||
function getApi() {
|
||||
return root.GenealogyApi && root.GenealogyApi.defaultClient;
|
||||
}
|
||||
|
||||
function normalizeNotifications(data) {
|
||||
return Array.isArray(data) ? data : [];
|
||||
}
|
||||
|
||||
function formatNotification(data) {
|
||||
return JSON.stringify(data || {}, null, 2);
|
||||
}
|
||||
|
||||
function getUnreadCount(data) {
|
||||
var count = Number(data);
|
||||
|
||||
return Number.isFinite(count) && count >= 0 ? count : 0;
|
||||
}
|
||||
|
||||
function query(selector, rootNode) {
|
||||
return (rootNode || documentRef).querySelector(selector);
|
||||
}
|
||||
|
||||
function showMessage(message) {
|
||||
if (root.layui && root.layui.layer) {
|
||||
root.layui.layer.msg(message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (root.alert) root.alert(message);
|
||||
}
|
||||
|
||||
function renderNotifications(container, notifications) {
|
||||
if (!container) return;
|
||||
container.innerHTML = '';
|
||||
|
||||
if (!notifications.length) {
|
||||
container.textContent = '暂无通知';
|
||||
return;
|
||||
}
|
||||
|
||||
notifications.forEach(function (notification) {
|
||||
var row = documentRef.createElement('pre');
|
||||
|
||||
row.className = 'notification-raw';
|
||||
row.textContent = formatNotification(notification);
|
||||
container.appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function updateStatus(message) {
|
||||
var status = query('[data-notification-status]');
|
||||
|
||||
if (status) status.textContent = message;
|
||||
}
|
||||
|
||||
async function loadNotifications() {
|
||||
var api = getApi();
|
||||
var container = query('[data-notification-list]');
|
||||
var countTarget = query('[data-notification-unread-count]');
|
||||
var result;
|
||||
|
||||
if (!api || !api.notifications || !api.unreadNotificationCount) {
|
||||
updateStatus('消息接口初始化失败,请刷新后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
updateStatus('正在同步消息…');
|
||||
try {
|
||||
result = await Promise.all([api.notifications(), api.unreadNotificationCount()]);
|
||||
renderNotifications(container, normalizeNotifications(result[0]));
|
||||
if (countTarget) countTarget.textContent = String(getUnreadCount(result[1]));
|
||||
updateStatus('已同步消息。列表元素 DTO 尚未在 PC 契约展开,以下保留服务端原始记录。');
|
||||
} catch (error) {
|
||||
updateStatus(error.message || '消息同步失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function markAllRead(button) {
|
||||
var api = getApi();
|
||||
|
||||
if (!api || !api.markAllNotificationsRead) {
|
||||
updateStatus('消息接口初始化失败,请刷新后重试');
|
||||
return;
|
||||
}
|
||||
|
||||
button.disabled = true;
|
||||
try {
|
||||
await api.markAllNotificationsRead();
|
||||
showMessage('已全部标记为已读');
|
||||
await loadNotifications();
|
||||
} catch (error) {
|
||||
updateStatus(error.message || '全部标记已读失败');
|
||||
} finally {
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
var readAll = query('[data-notification-read-all]');
|
||||
var refresh = query('[data-notification-refresh]');
|
||||
|
||||
if (!documentRef || !query('[data-notification-list]')) return;
|
||||
if (readAll) readAll.addEventListener('click', function () { markAllRead(readAll); });
|
||||
if (refresh) refresh.addEventListener('click', loadNotifications);
|
||||
loadNotifications();
|
||||
}
|
||||
|
||||
return {
|
||||
normalizeNotifications: normalizeNotifications,
|
||||
formatNotification: formatNotification,
|
||||
getUnreadCount: getUnreadCount,
|
||||
loadNotifications: loadNotifications,
|
||||
init: init
|
||||
};
|
||||
});
|
||||
@@ -147,14 +147,21 @@
|
||||
return parts[0] + '?' + params.toString() + hash;
|
||||
}
|
||||
|
||||
function getGenealogyEntryUrl(href) {
|
||||
var value = String(href || '');
|
||||
|
||||
if (!value || value.charAt(0) === '#' || /^(?:https?:|mailto:|tel:)/i.test(value)) return value;
|
||||
if (value.indexOf('profile-families.html') === 0) return value;
|
||||
return 'profile-families.html?next=' + encodeURIComponent(value.split('?')[0]);
|
||||
}
|
||||
|
||||
function syncGenealogyContextLinks() {
|
||||
var genealogyId = getGenealogyId();
|
||||
|
||||
if (!genealogyId) return;
|
||||
$('[data-genealogy-context-link]').each(function () {
|
||||
var $link = $(this);
|
||||
|
||||
$link.attr('href', withGenealogyId($link.attr('href'), genealogyId));
|
||||
$link.attr('href', genealogyId ? withGenealogyId($link.attr('href'), genealogyId) : getGenealogyEntryUrl($link.attr('href')));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -272,6 +279,7 @@
|
||||
initLayui: initLayui,
|
||||
getGenealogyId: getGenealogyId,
|
||||
withGenealogyId: withGenealogyId,
|
||||
getGenealogyEntryUrl: getGenealogyEntryUrl,
|
||||
syncGenealogyContextLinks: syncGenealogyContextLinks
|
||||
};
|
||||
})(window, window.jQuery || (window.layui && window.layui.$));
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
(function (root, factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(root);
|
||||
return;
|
||||
}
|
||||
root.RelativePages = factory(root);
|
||||
if (root.document) root.document.addEventListener('DOMContentLoaded', function () { root.RelativePages.init(); });
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
||||
'use strict';
|
||||
|
||||
var documentRef = root.document;
|
||||
var writePending = false;
|
||||
|
||||
function getApi() { return root.GenealogyApi && root.GenealogyApi.defaultClient; }
|
||||
function query(selector, node) { return documentRef ? (node || documentRef).querySelector(selector) : null; }
|
||||
function queryAll(selector, node) { return documentRef ? Array.prototype.slice.call((node || documentRef).querySelectorAll(selector)) : []; }
|
||||
function trim(value) { var text = String(value === undefined || value === null ? '' : value).trim(); return text || undefined; }
|
||||
function queryParam(search, name) { return new URLSearchParams(String(search || '').replace(/^\?/, '')).get(name) || ''; }
|
||||
|
||||
function getCurrentGenealogyId(search) {
|
||||
var page = query('[data-relative-page], [data-relative-edit-page]');
|
||||
var source = search === undefined && root.location ? root.location.search : search;
|
||||
var profileGenealogyId;
|
||||
|
||||
if (search === undefined && root.ProfileUI && root.ProfileUI.getGenealogyId) {
|
||||
profileGenealogyId = root.ProfileUI.getGenealogyId();
|
||||
if (profileGenealogyId) return profileGenealogyId;
|
||||
}
|
||||
return queryParam(source, 'genealogyId') || (page && page.getAttribute('data-genealogy-id')) || '';
|
||||
}
|
||||
|
||||
function toSafeNumber(value) {
|
||||
var text = trim(value);
|
||||
var number;
|
||||
|
||||
if (!text) return undefined;
|
||||
number = Number(text);
|
||||
return Number.isFinite(number) ? number : undefined;
|
||||
}
|
||||
|
||||
function toSafeInteger(value) {
|
||||
var number = toSafeNumber(value);
|
||||
|
||||
return Number.isInteger(number) && Number.isSafeInteger(number) ? number : undefined;
|
||||
}
|
||||
|
||||
function buildRelativeRecordBody(values) {
|
||||
var source = values || {};
|
||||
var body = { relativeName: String(source.relativeName || '').trim() };
|
||||
var fields = ['relationName', 'eventName', 'eventTime', 'recordContent', 'mediaOssIds', 'status'];
|
||||
var amountText = trim(source.giftAmount);
|
||||
var sortOrderText = trim(source.sortOrder);
|
||||
|
||||
fields.forEach(function (field) {
|
||||
var value = trim(source[field]);
|
||||
|
||||
if (value !== undefined) body[field] = value;
|
||||
});
|
||||
if (amountText !== undefined) body.giftAmount = toSafeNumber(amountText);
|
||||
if (sortOrderText !== undefined) body.sortOrder = toSafeInteger(sortOrderText);
|
||||
return body;
|
||||
}
|
||||
|
||||
function validateRelativeRecordBody(body) {
|
||||
if (!body || !body.relativeName) return '请填写亲友姓名';
|
||||
if (body.mediaOssIds !== undefined && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(body.mediaOssIds)) {
|
||||
return '附件 OSS ID 请使用英文逗号分隔的正整数';
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(body, 'giftAmount') && body.giftAmount === undefined) return '礼金金额必须是数字';
|
||||
if (Object.prototype.hasOwnProperty.call(body, 'sortOrder') && body.sortOrder === undefined) return '排序值必须是安全整数';
|
||||
return '';
|
||||
}
|
||||
|
||||
function normalizeRelativeList(data) { return Array.isArray(data) ? data : []; }
|
||||
function escapeHtml(value) { return String(value === undefined || value === null ? '' : value).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, '''); }
|
||||
function stringify(value) { try { return typeof value === 'string' ? value : JSON.stringify(value); } catch (error) { return String(value); } }
|
||||
|
||||
function renderRelativeRecords(data) {
|
||||
var container = query('[data-relative-list]');
|
||||
var records = normalizeRelativeList(data);
|
||||
|
||||
if (!container) return;
|
||||
if (!Array.isArray(data)) {
|
||||
container.innerHTML = '<div class="api-empty">亲友往来响应未按 Apifox ListResult 返回数组,无法安全展示或操作记录。</div>';
|
||||
return;
|
||||
}
|
||||
if (!records.length) {
|
||||
container.innerHTML = '<div class="api-empty">暂无亲友往来记录</div>';
|
||||
return;
|
||||
}
|
||||
container.innerHTML = records.map(function (record, index) {
|
||||
return '<article class="module-row"><div><h3>亲友往来 ' + (index + 1) + '</h3><p>' + escapeHtml(stringify(record)) + '</p></div></article>';
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function showMessage(message) { if (root.layui && root.layui.layer) root.layui.layer.msg(message); else if (root.alert) root.alert(message); }
|
||||
function shouldRedirectToLogin(api, error) { var status = error && (error.status || error.code); return !api || !api.getToken || !api.getToken() || Number(status) === 401; }
|
||||
function redirectUnauthorized(api, error) {
|
||||
if (!shouldRedirectToLogin(api, error)) return false;
|
||||
if (api && api.clearToken) api.clearToken();
|
||||
if (root.location && typeof root.location.replace === 'function') root.location.replace('login.html');
|
||||
else if (root.location) root.location.href = 'login.html';
|
||||
return true;
|
||||
}
|
||||
function setFormStatus(message) { var target = query('[data-relative-form-status]'); if (target) target.textContent = message || ''; }
|
||||
function setWritePending(value) { writePending = Boolean(value); queryAll('[data-relative-form] button, [data-relative-form] input, [data-relative-form] textarea').forEach(function (control) { control.disabled = writePending; }); }
|
||||
function syncLinks(genealogyId) {
|
||||
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) { root.ProfileUI.syncGenealogyContextLinks(); return; }
|
||||
queryAll('[data-genealogy-context-link]').forEach(function (link) { var href = link.getAttribute('href'); if (href) link.href = href.split('?')[0] + '?genealogyId=' + encodeURIComponent(genealogyId); });
|
||||
}
|
||||
function requireGenealogyId() {
|
||||
var genealogyId = getCurrentGenealogyId();
|
||||
var list = query('[data-relative-list]');
|
||||
|
||||
if (genealogyId) return genealogyId;
|
||||
if (list) list.innerHTML = '<div class="api-empty">等待家谱入口接口;请从具体家谱进入亲友往来。</div>';
|
||||
setFormStatus('等待家谱入口接口;请从具体家谱进入亲友往来。');
|
||||
showMessage('等待家谱入口接口;请从具体家谱进入亲友往来。');
|
||||
return '';
|
||||
}
|
||||
|
||||
async function loadRelativeRecords() {
|
||||
var api = getApi();
|
||||
var genealogyId;
|
||||
|
||||
if (redirectUnauthorized(api)) return;
|
||||
genealogyId = requireGenealogyId();
|
||||
if (!genealogyId) return;
|
||||
syncLinks(genealogyId);
|
||||
try { renderRelativeRecords(await api.relativeRecords(genealogyId)); }
|
||||
catch (error) { if (!redirectUnauthorized(api, error)) { renderRelativeRecords(null); showMessage(error.message || '亲友往来加载失败'); } }
|
||||
}
|
||||
|
||||
async function submitRelativeRecord(form) {
|
||||
var api = getApi();
|
||||
var genealogyId;
|
||||
var values = {};
|
||||
var body;
|
||||
var validation;
|
||||
|
||||
if (writePending || redirectUnauthorized(api)) return;
|
||||
genealogyId = requireGenealogyId();
|
||||
if (!genealogyId) return;
|
||||
queryAll('[name]', form).forEach(function (field) { values[field.name] = field.value; });
|
||||
body = buildRelativeRecordBody(values);
|
||||
validation = validateRelativeRecordBody(body);
|
||||
if (validation) { setFormStatus(validation); return; }
|
||||
setWritePending(true);
|
||||
setFormStatus('正在保存亲友往来...');
|
||||
try {
|
||||
await api.createRelativeRecord(genealogyId, body);
|
||||
if (root.location) root.location.href = 'profile-relative.html?genealogyId=' + encodeURIComponent(genealogyId);
|
||||
} catch (error) {
|
||||
if (!redirectUnauthorized(api, error)) setFormStatus(error.message || '亲友往来保存失败');
|
||||
} finally { setWritePending(false); }
|
||||
}
|
||||
|
||||
function init() {
|
||||
var genealogyId;
|
||||
|
||||
if (!documentRef) return;
|
||||
documentRef.addEventListener('submit', function (event) { var form = event.target.closest('[data-relative-form]'); if (form) { event.preventDefault(); submitRelativeRecord(form); } });
|
||||
if (query('[data-relative-page]')) loadRelativeRecords();
|
||||
if (query('[data-relative-edit-page]')) { genealogyId = requireGenealogyId(); if (genealogyId) syncLinks(genealogyId); }
|
||||
}
|
||||
|
||||
return { getCurrentGenealogyId: getCurrentGenealogyId, buildRelativeRecordBody: buildRelativeRecordBody, validateRelativeRecordBody: validateRelativeRecordBody, normalizeRelativeList: normalizeRelativeList, shouldRedirectToLogin: shouldRedirectToLogin, init: init };
|
||||
});
|
||||
+122
-72
@@ -1,93 +1,143 @@
|
||||
(function (window, $) {
|
||||
(function (root) {
|
||||
'use strict';
|
||||
|
||||
var defaultItems = [
|
||||
'source', '|',
|
||||
'undo', 'redo', '|',
|
||||
'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',
|
||||
'plainpaste', 'wordpaste', '|',
|
||||
'justifyleft', 'justifycenter', 'justifyright', 'justifyfull',
|
||||
'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', '|',
|
||||
'formatblock', 'fontname', 'fontsize', '|',
|
||||
'forecolor', 'hilitecolor', 'bold', 'italic', 'underline',
|
||||
'strikethrough', 'lineheight', 'removeformat', '|',
|
||||
'image', 'multiimage', 'table', 'hr', 'emoticons', 'baidumap',
|
||||
'pagebreak', 'anchor', 'link', 'unlink', '|',
|
||||
'about'
|
||||
var documentRef = root.document;
|
||||
var editors = {};
|
||||
var submitBound = false;
|
||||
var toolbarKeys = [
|
||||
'headerSelect', 'fontSize', 'color', 'bgColor', '|',
|
||||
'bold', 'italic', 'underline', 'through', '|',
|
||||
'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyJustify', '|',
|
||||
'insertTable', 'clearStyle', 'insertLink', 'undo', 'redo'
|
||||
];
|
||||
|
||||
function getJquery() {
|
||||
return $ || window.jQuery || (window.layui && window.layui.$);
|
||||
function findTextarea(target) {
|
||||
if (!target || !documentRef) return null;
|
||||
if (typeof target === 'string') return documentRef.querySelector(target);
|
||||
if (target.nodeType === 1) return target;
|
||||
return target[0] || null;
|
||||
}
|
||||
|
||||
function normalizeSelector(target) {
|
||||
var jq = getJquery();
|
||||
|
||||
if (!target) return '';
|
||||
if (typeof target === 'string') return target;
|
||||
if (jq && target instanceof jq) target = target.get(0);
|
||||
if (target && target.id) return '#' + target.id;
|
||||
return target;
|
||||
function ensureId(textarea) {
|
||||
if (!textarea.id) textarea.id = 'rich-editor-' + Math.floor(Math.random() * 1000000);
|
||||
return textarea.id;
|
||||
}
|
||||
|
||||
function initRichEditor(target, options) {
|
||||
var jq = getJquery();
|
||||
var selector = normalizeSelector(target);
|
||||
var $textarea;
|
||||
function fieldValue(editor) {
|
||||
return String(editor.getText() || '').trim() ? editor.getHtml() : '';
|
||||
}
|
||||
|
||||
if (!jq) return null;
|
||||
$textarea = jq(selector).first();
|
||||
if (!$textarea.length || !window.KindEditor) return null;
|
||||
function syncEditor(instance) {
|
||||
instance.textarea.value = fieldValue(instance.editor);
|
||||
}
|
||||
|
||||
if (!$textarea.attr('id')) {
|
||||
$textarea.attr('id', 'rich-editor-' + Math.floor(Math.random() * 1000000));
|
||||
selector = '#' + $textarea.attr('id');
|
||||
function createRichEditor(target, options) {
|
||||
var textarea = findTextarea(target);
|
||||
var settings = options || {};
|
||||
var id;
|
||||
var host;
|
||||
var toolbar;
|
||||
var content;
|
||||
var editor;
|
||||
var wangEditor = root.wangEditor;
|
||||
var instance;
|
||||
|
||||
if (!textarea || !wangEditor || !wangEditor.createEditor || !wangEditor.createToolbar) return null;
|
||||
if (textarea.dataset.wangEditorInitialized === 'true') return editors[textarea.id] || null;
|
||||
|
||||
id = ensureId(textarea);
|
||||
host = documentRef.createElement('div');
|
||||
host.className = 'rich-editor-host';
|
||||
toolbar = documentRef.createElement('div');
|
||||
toolbar.className = 'rich-editor-toolbar';
|
||||
toolbar.id = id + '-toolbar';
|
||||
content = documentRef.createElement('div');
|
||||
content.className = 'rich-editor-content';
|
||||
content.id = id + '-content';
|
||||
host.appendChild(toolbar);
|
||||
host.appendChild(content);
|
||||
textarea.insertAdjacentElement('afterend', host);
|
||||
|
||||
try {
|
||||
editor = wangEditor.createEditor({
|
||||
selector: '#' + content.id,
|
||||
html: textarea.value || '',
|
||||
config: Object.assign({
|
||||
placeholder: textarea.getAttribute('placeholder') || '请输入内容',
|
||||
onChange: function (currentEditor) {
|
||||
textarea.value = fieldValue(currentEditor);
|
||||
},
|
||||
onBlur: function (currentEditor) {
|
||||
textarea.value = fieldValue(currentEditor);
|
||||
}
|
||||
}, settings.editorConfig || {}),
|
||||
mode: settings.mode || 'default'
|
||||
});
|
||||
wangEditor.createToolbar({
|
||||
editor: editor,
|
||||
selector: '#' + toolbar.id,
|
||||
config: Object.assign({ toolbarKeys: toolbarKeys }, settings.toolbarConfig || {}),
|
||||
mode: settings.mode || 'default'
|
||||
});
|
||||
} catch (error) {
|
||||
host.remove();
|
||||
return null;
|
||||
}
|
||||
|
||||
return window.KindEditor.create(selector, jq.extend({
|
||||
width: '100%',
|
||||
minWidth: '100%',
|
||||
height: '460px',
|
||||
resizeType: 1,
|
||||
allowPreviewEmoticons: true,
|
||||
allowImageUpload: true,
|
||||
filterMode: false,
|
||||
newlineTag: 'p',
|
||||
items: defaultItems,
|
||||
afterBlur: function () {
|
||||
this.sync();
|
||||
}
|
||||
}, options || {}));
|
||||
textarea.hidden = true;
|
||||
textarea.setAttribute('aria-hidden', 'true');
|
||||
textarea.dataset.wangEditorInitialized = 'true';
|
||||
instance = {
|
||||
textarea: textarea,
|
||||
editor: editor,
|
||||
sync: function () { syncEditor(instance); },
|
||||
getHtml: function () { return fieldValue(editor); },
|
||||
getText: function () { return editor.getText(); }
|
||||
};
|
||||
editors[id] = instance;
|
||||
instance.sync();
|
||||
return instance;
|
||||
}
|
||||
|
||||
function initRichEditors(root, options) {
|
||||
var jq = getJquery();
|
||||
var editors = {};
|
||||
var $root;
|
||||
function initRichEditors(rootNode, options) {
|
||||
var scope = rootNode || documentRef;
|
||||
var instances = {};
|
||||
|
||||
if (!jq) return editors;
|
||||
$root = root ? jq(root) : jq(document);
|
||||
$root.find('.js-rich-editor').each(function (index) {
|
||||
var $textarea = jq(this);
|
||||
var id = $textarea.attr('id') || ('rich-editor-' + (index + 1));
|
||||
if (!scope || !scope.querySelectorAll) return instances;
|
||||
Array.prototype.forEach.call(scope.querySelectorAll('.js-rich-editor'), function (textarea) {
|
||||
var instance = createRichEditor(textarea, options);
|
||||
|
||||
$textarea.attr('id', id);
|
||||
editors[id] = initRichEditor('#' + id, options);
|
||||
if (instance) instances[textarea.id] = instance;
|
||||
});
|
||||
|
||||
return editors;
|
||||
return instances;
|
||||
}
|
||||
|
||||
window.AppRichEditor = {
|
||||
init: initRichEditor,
|
||||
initAll: initRichEditors
|
||||
function bindFormSync() {
|
||||
if (submitBound || !documentRef) return;
|
||||
submitBound = true;
|
||||
documentRef.addEventListener('submit', function (event) {
|
||||
Object.keys(editors).forEach(function (id) {
|
||||
var instance = editors[id];
|
||||
|
||||
if (instance.textarea.form === event.target) instance.sync();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initAll() {
|
||||
bindFormSync();
|
||||
return initRichEditors(documentRef);
|
||||
}
|
||||
|
||||
root.AppRichEditor = {
|
||||
init: createRichEditor,
|
||||
initAll: initRichEditors,
|
||||
syncAll: function () {
|
||||
Object.keys(editors).forEach(function (id) { editors[id].sync(); });
|
||||
}
|
||||
};
|
||||
|
||||
if (getJquery()) {
|
||||
getJquery()(function () {
|
||||
if (getJquery()('.js-rich-editor').length) {
|
||||
initRichEditors(document);
|
||||
}
|
||||
});
|
||||
}
|
||||
})(window, window.jQuery || (window.layui && window.layui.$));
|
||||
if (!documentRef) return;
|
||||
if (documentRef.readyState === 'loading') documentRef.addEventListener('DOMContentLoaded', initAll);
|
||||
else initAll();
|
||||
})(window);
|
||||
|
||||
+13
-15
@@ -15,15 +15,15 @@
|
||||
'use strict';
|
||||
|
||||
var documentRef = root.document;
|
||||
var CAPTCHA_SCENES = {
|
||||
phone: 'PC_PHONE_CHANGE',
|
||||
deactivate: 'PC_ACCOUNT_DEACTIVATE'
|
||||
var CAPTCHA_OPERATIONS = {
|
||||
phone: 'phone-change',
|
||||
deactivate: 'account-deactivate'
|
||||
};
|
||||
var SMS_CODE_COOLDOWN_SECONDS = 60;
|
||||
var boundPhone = '';
|
||||
|
||||
function getCaptchaScene(formType) {
|
||||
return CAPTCHA_SCENES[formType] || '';
|
||||
function getCaptchaOperation(formType) {
|
||||
return CAPTCHA_OPERATIONS[formType] || '';
|
||||
}
|
||||
|
||||
function hashPassword(value) {
|
||||
@@ -100,18 +100,18 @@
|
||||
}
|
||||
|
||||
async function ensureCaptcha(form, formType, subject) {
|
||||
// 发送敏感业务短信前,先取得与当前场景绑定的验证码票据。
|
||||
// 发送敏感业务短信前,先取得与当前认证动作绑定的验证码票据。
|
||||
var captcha = root.CaptchaPages;
|
||||
var sceneCode = getCaptchaScene(formType);
|
||||
var operationCode = getCaptchaOperation(formType);
|
||||
|
||||
if (!sceneCode) return true;
|
||||
if (!operationCode) return true;
|
||||
if (!captcha || !captcha.ensureToken) {
|
||||
showMessage('验证码组件未加载,请刷新后重试');
|
||||
return false;
|
||||
}
|
||||
|
||||
return Boolean(await captcha.ensureToken(form, {
|
||||
sceneCode: sceneCode,
|
||||
operationCode: operationCode,
|
||||
subject: subject || ''
|
||||
}));
|
||||
}
|
||||
@@ -295,7 +295,7 @@
|
||||
}
|
||||
|
||||
async function sendPhoneCode(button) {
|
||||
// 换绑手机号使用独立短信场景,避免复用登录或找回密码票据。
|
||||
// 换绑手机号使用独立认证动作,避免复用登录或找回密码票据。
|
||||
var api = getApi();
|
||||
var form = query('[data-security-form="phone"]');
|
||||
var values;
|
||||
@@ -311,9 +311,8 @@
|
||||
button.disabled = true;
|
||||
|
||||
try {
|
||||
await api.sendSmsCode({
|
||||
await api.sendSmsCode(getCaptchaOperation('phone'), {
|
||||
phone: values.phone,
|
||||
sceneCode: getCaptchaScene('phone'),
|
||||
validToken: values.validToken || ''
|
||||
});
|
||||
clearCaptchaToken(form);
|
||||
@@ -342,9 +341,8 @@
|
||||
button.disabled = true;
|
||||
|
||||
try {
|
||||
await api.sendSmsCode({
|
||||
await api.sendSmsCode(getCaptchaOperation('deactivate'), {
|
||||
phone: boundPhone,
|
||||
sceneCode: getCaptchaScene('deactivate'),
|
||||
validToken: values.validToken || ''
|
||||
});
|
||||
clearCaptchaToken(form);
|
||||
@@ -460,7 +458,7 @@
|
||||
buildPasswordChangeBody: buildPasswordChangeBody,
|
||||
buildPhoneChangeBody: buildPhoneChangeBody,
|
||||
buildDeactivateBody: buildDeactivateBody,
|
||||
getCaptchaScene: getCaptchaScene,
|
||||
getCaptchaOperation: getCaptchaOperation,
|
||||
isDangerConfirmed: isDangerConfirmed,
|
||||
isPhone: isPhone,
|
||||
isSmsCode: isSmsCode,
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
}
|
||||
|
||||
function getUploadMode() {
|
||||
// 本期只保留头像单文件上传,不再根据文件大小切换分片流程。
|
||||
return 'single';
|
||||
// 当前 PC 目录只定义统一分片上传流程。
|
||||
return 'resumable';
|
||||
}
|
||||
|
||||
function getApi() {
|
||||
@@ -72,9 +72,9 @@
|
||||
}
|
||||
|
||||
function uploadFileForPage(api, file) {
|
||||
// 上传结果仅用于回填资料表单中的 avatarOssId。
|
||||
// 当前初始化响应未展开 uploadId、instant 与 OSS 字段,不能猜测分片闭环。
|
||||
if (!api || !file) return Promise.reject(new Error('请选择文件'));
|
||||
return api.uploadFile(file);
|
||||
return Promise.reject(new Error('当前 PC 文件上传响应未定义头像回填字段,暂不能上传头像'));
|
||||
}
|
||||
|
||||
async function uploadFromInput(input) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user