137 lines
5.4 KiB
JavaScript
137 lines
5.4 KiB
JavaScript
(function () {
|
||
// 页面级骨架屏:加载期整页覆盖一层模拟页面结构的占位层,数据就绪后整层淡出。
|
||
// 样式在 common/theme.css 的 .hy-page-skeleton 系列类,流光块复用 .hy-skeleton,主题色自动跟随 App。
|
||
// 布局通过 section 描述数组配置,默认布局对齐各中心页的“资料卡 + 卡片栅格 + 操作宫格”结构;
|
||
// 结构差异大的页面调用 show({ sections: [...] }) 自定义。
|
||
var SKELETON_ID = 'hy-page-skeleton';
|
||
var LEAVE_MS = 200;
|
||
var leaveTimer = 0;
|
||
|
||
var DEFAULT_SECTIONS = [
|
||
// 顶栏:返回按钮 + 标题 + 右侧按钮
|
||
{ type: 'header' },
|
||
// 资料卡:圆头像 + 两行文本
|
||
{ type: 'profile' },
|
||
// KPI 卡片 2x2:圆图标 + 两条文本
|
||
{ type: 'grid', count: 4, columns: 2, lines: 2, height: 128 },
|
||
// 资产行 2x1
|
||
{ type: 'grid', count: 2, columns: 2, lines: 1, height: 78 },
|
||
// 操作宫格 3x3:居中圆图标 + 短文本
|
||
{ type: 'grid', count: 9, columns: 3, lines: 1, height: 112, center: true },
|
||
];
|
||
|
||
function block(circle) {
|
||
var node = document.createElement('div');
|
||
node.className = circle ? 'hy-skeleton hy-skeleton--circle' : 'hy-skeleton';
|
||
return node;
|
||
}
|
||
|
||
function buildSection(section) {
|
||
if (section.type === 'header') {
|
||
var header = document.createElement('div');
|
||
header.className = 'hy-page-skeleton__header';
|
||
header.appendChild(block(true));
|
||
header.appendChild(block(false));
|
||
header.appendChild(block(true));
|
||
return header;
|
||
}
|
||
if (section.type === 'profile') {
|
||
var card = document.createElement('div');
|
||
card.className = 'hy-page-skeleton__card hy-page-skeleton__profile';
|
||
var avatar = block(true);
|
||
avatar.style.width = '76px';
|
||
avatar.style.height = '76px';
|
||
card.appendChild(avatar);
|
||
var lines = document.createElement('div');
|
||
lines.className = 'hy-page-skeleton__lines';
|
||
lines.appendChild(block(false));
|
||
lines.appendChild(block(false));
|
||
card.appendChild(lines);
|
||
return card;
|
||
}
|
||
// grid:等宽单元格,每格圆图标 + N 条文本
|
||
var grid = document.createElement('div');
|
||
grid.className = 'hy-page-skeleton__grid';
|
||
grid.style.gridTemplateColumns =
|
||
'repeat(' + (section.columns || 2) + ', minmax(0, 1fr))';
|
||
for (var i = 0; i < (section.count || 2); i += 1) {
|
||
var cell = document.createElement('div');
|
||
cell.className =
|
||
'hy-page-skeleton__cell' +
|
||
(section.center ? ' hy-page-skeleton__cell--center' : '');
|
||
if (section.height) cell.style.minHeight = section.height + 'px';
|
||
cell.appendChild(block(true));
|
||
for (var line = 0; line < (section.lines || 1); line += 1) {
|
||
var bar = block(false);
|
||
bar.style.width = line === 0 ? '72%' : '48%';
|
||
cell.appendChild(bar);
|
||
}
|
||
grid.appendChild(cell);
|
||
}
|
||
return grid;
|
||
}
|
||
|
||
function ensureNode(sections) {
|
||
var node = document.getElementById(SKELETON_ID);
|
||
if (node) return node;
|
||
node = document.createElement('div');
|
||
node.id = SKELETON_ID;
|
||
node.className = 'hy-page-skeleton';
|
||
node.setAttribute('aria-hidden', 'true');
|
||
for (var i = 0; i < sections.length; i += 1) {
|
||
node.appendChild(buildSection(sections[i]));
|
||
}
|
||
// 骨架结构之上居中叠加拉鲁猫 loading 图 + 转圈(视觉与 .hy-loading 全屏遮罩同源)。
|
||
var mascotWrap = document.createElement('div');
|
||
mascotWrap.className = 'hy-page-skeleton__mascot-wrap';
|
||
var mascot = document.createElement('div');
|
||
mascot.className = 'hy-loading__mascot';
|
||
var spinner = document.createElement('div');
|
||
spinner.className = 'hy-loading__spinner';
|
||
mascotWrap.appendChild(mascot);
|
||
mascotWrap.appendChild(spinner);
|
||
node.appendChild(mascotWrap);
|
||
document.body.appendChild(node);
|
||
return node;
|
||
}
|
||
|
||
function show(options) {
|
||
if (!document.body) {
|
||
document.addEventListener(
|
||
'DOMContentLoaded',
|
||
function () {
|
||
show(options);
|
||
},
|
||
{ once: true }
|
||
);
|
||
return;
|
||
}
|
||
window.clearTimeout(leaveTimer);
|
||
var node = ensureNode((options && options.sections) || DEFAULT_SECTIONS);
|
||
node.classList.remove('is-leaving');
|
||
node.classList.add('is-active');
|
||
}
|
||
|
||
function hide() {
|
||
var node = document.getElementById(SKELETON_ID);
|
||
if (!node || !node.classList.contains('is-active')) return;
|
||
// 淡出后再摘除激活态,避免数据渲染瞬间硬切;重复 hide 幂等。
|
||
node.classList.add('is-leaving');
|
||
window.clearTimeout(leaveTimer);
|
||
leaveTimer = window.setTimeout(function () {
|
||
node.classList.remove('is-active', 'is-leaving');
|
||
}, LEAVE_MS);
|
||
}
|
||
|
||
function isVisible() {
|
||
var node = document.getElementById(SKELETON_ID);
|
||
return !!node && node.classList.contains('is-active');
|
||
}
|
||
|
||
window.HyAppSkeleton = {
|
||
show: show,
|
||
hide: hide,
|
||
isVisible: isVisible,
|
||
};
|
||
})();
|