feat: 增加图片合并功能
This commit is contained in:
@@ -28,9 +28,11 @@ export function createAppShell(root, { onReload }) {
|
||||
content.append(tabs, cards);
|
||||
const chartNav = element('aside', 'sidebar chart-sidebar');
|
||||
layout.append(companyNav, content, chartNav);
|
||||
root.append(layout, reloadButton);
|
||||
const cartBar = element('div', 'cart-bar');
|
||||
cartBar.hidden = true;
|
||||
root.append(layout, reloadButton, cartBar);
|
||||
|
||||
return { companyNav, tabs, cards, chartNav };
|
||||
return { companyNav, tabs, cards, chartNav, cartBar };
|
||||
}
|
||||
|
||||
export function renderCompanies(container, companies, activeCompanyId, onSelect) {
|
||||
@@ -57,7 +59,7 @@ export function renderTabs(container, tabs, activeTabId, onSelect) {
|
||||
});
|
||||
}
|
||||
|
||||
export function renderCards(container, charts, activeViews, { onExport, onSelectView }) {
|
||||
export function renderCards(container, charts, activeViews, { onExport, isCarted, onToggleCart, onSelectView }) {
|
||||
container.replaceChildren();
|
||||
if (!charts.length) {
|
||||
container.append(element('p', 'empty-state', '该分类暂时没有图表。'));
|
||||
@@ -92,7 +94,13 @@ export function renderCards(container, charts, activeViews, { onExport, onSelect
|
||||
bar.append(switcher);
|
||||
}
|
||||
|
||||
bar.append(button('导出 PNG', {
|
||||
const cartButton = button(isCarted?.(chart.instanceId) ? '已加入 ✓' : '➕ 加入合并', {
|
||||
onClick: () => onToggleCart?.(chart),
|
||||
className: 'cart-button',
|
||||
});
|
||||
cartButton.dataset.cartId = chart.instanceId;
|
||||
cartButton.dataset.inCart = String(Boolean(isCarted?.(chart.instanceId)));
|
||||
bar.append(cartButton, button('导出 PNG', {
|
||||
onClick: () => onExport(chart.instanceId, chart.filename || chart.title),
|
||||
className: 'export-button',
|
||||
}));
|
||||
@@ -121,3 +129,95 @@ export function showChartError(chartId, message) {
|
||||
if (!chartEl) return;
|
||||
chartEl.replaceChildren(element('div', 'chart-error', message));
|
||||
}
|
||||
|
||||
const CART_MODES = [
|
||||
['pairs', '两两合并'],
|
||||
['single', '合并为一张'],
|
||||
];
|
||||
|
||||
export function renderCartBar(container, { items = [], mode, onModeChange, onMove, onRemove, onClear, onPreview }) {
|
||||
container.replaceChildren();
|
||||
if (!items.length) {
|
||||
container.hidden = true;
|
||||
return;
|
||||
}
|
||||
container.hidden = false;
|
||||
|
||||
container.append(element('span', 'cart-label', `合并队列 ${items.length}`));
|
||||
|
||||
const chips = element('div', 'cart-chips');
|
||||
items.forEach((item, index) => {
|
||||
const chip = element('div', 'cart-chip');
|
||||
chip.title = item.title;
|
||||
const up = button('↑', { onClick: () => onMove?.(item.instanceId, -1), className: 'cart-chip-button' });
|
||||
up.disabled = index === 0;
|
||||
const down = button('↓', { onClick: () => onMove?.(item.instanceId, 1), className: 'cart-chip-button' });
|
||||
down.disabled = index === items.length - 1;
|
||||
chip.append(
|
||||
element('span', 'cart-chip-name', item.title),
|
||||
up,
|
||||
down,
|
||||
button('✕', { onClick: () => onRemove?.(item.instanceId), className: 'cart-chip-button' }),
|
||||
);
|
||||
chips.append(chip);
|
||||
});
|
||||
container.append(chips);
|
||||
|
||||
container.append(element('span', 'cart-divider'));
|
||||
|
||||
const modes = element('div', 'cart-modes');
|
||||
CART_MODES.forEach(([value, label]) => {
|
||||
modes.append(button(label, {
|
||||
active: mode === value,
|
||||
onClick: () => onModeChange?.(value),
|
||||
className: 'cart-mode-button',
|
||||
}));
|
||||
});
|
||||
container.append(modes);
|
||||
|
||||
container.append(button('预览导出', { onClick: () => onPreview?.(), className: 'cart-export-button' }));
|
||||
container.append(button('清空', { onClick: () => onClear?.(), className: 'cart-clear-button' }));
|
||||
}
|
||||
|
||||
export function showMergePreview(entries, { notice = '', onDownload, onDownloadAll, onClose }) {
|
||||
const overlay = element('div', 'modal-overlay');
|
||||
const modal = element('div', 'modal');
|
||||
|
||||
const head = element('div', 'modal-head');
|
||||
head.append(element('h2', 'modal-title', `合并预览 · ${entries.length} 张`));
|
||||
if (notice) head.append(element('span', 'modal-notice', notice));
|
||||
head.append(button('✕', { onClick: onClose, className: 'modal-close' }));
|
||||
|
||||
const body = element('div', 'modal-body');
|
||||
entries.forEach((entry) => {
|
||||
const item = element('div', 'preview-item');
|
||||
entry.canvas.className = 'preview-canvas';
|
||||
item.append(entry.canvas);
|
||||
item.append(button(`⬇ 下载:${entry.filename}.png`, {
|
||||
onClick: () => onDownload?.(entry),
|
||||
className: 'export-button',
|
||||
}));
|
||||
body.append(item);
|
||||
});
|
||||
|
||||
const foot = element('div', 'modal-foot');
|
||||
foot.append(button('全部下载', { onClick: () => onDownloadAll?.(), className: 'primary-button' }));
|
||||
foot.append(button('取消', { onClick: onClose, className: 'export-button' }));
|
||||
|
||||
modal.append(head, body, foot);
|
||||
overlay.append(modal);
|
||||
overlay.addEventListener('click', (event) => {
|
||||
if (event.target === overlay) onClose?.();
|
||||
});
|
||||
document.addEventListener('keydown', onKeydown);
|
||||
document.body.append(overlay);
|
||||
|
||||
function onKeydown(event) {
|
||||
if (event.key === 'Escape') onClose?.();
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('keydown', onKeydown);
|
||||
overlay.remove();
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user