fix: 安装费明细支持横向滚动 #643

Merged
qiuxiang merged 1 commits from fix/qiuxiang-installation-summary-horizontal-scroll into main 2026-10-26 20:01:33 +08:00
2 changed files with 116 additions and 4 deletions

View File

@ -210,6 +210,7 @@
width: min(68rem, calc(100vw - 2rem));
max-width: none;
max-height: calc(100vh - 2rem);
box-sizing: border-box;
padding: 0;
border: 1px solid #d8d2c7;
border-radius: 0.75rem;
@ -351,13 +352,49 @@
}
.installation-fee-dialog__scroll {
min-height: 12rem;
flex: 1 1 auto;
min-height: 0;
box-sizing: border-box;
overflow: auto;
padding: 0 1.1rem;
padding: 0 1.1rem 0.65rem;
overscroll-behavior: contain;
scrollbar-color: #b9954f #f1ece3;
scrollbar-gutter: stable;
scrollbar-width: thin;
cursor: grab;
touch-action: pan-x pan-y;
}
.installation-fee-dialog__scroll.is-dragging {
cursor: grabbing;
user-select: none;
}
.installation-fee-dialog__scroll::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.installation-fee-dialog__scroll::-webkit-scrollbar-track {
background: #f1ece3;
}
.installation-fee-dialog__scroll::-webkit-scrollbar-thumb {
border-radius: 999px;
background: #b9954f;
}
.installation-fee-dialog__scroll::-webkit-scrollbar-thumb:hover {
background: #856632;
}
.installation-fee-panel {
min-width: 100%;
}
.installation-fee-table {
width: 100%;
min-width: 72rem;
border-collapse: collapse;
font-size: 0.75rem;
font-variant-numeric: tabular-nums;
@ -370,6 +407,7 @@
line-height: 1.5;
text-align: left;
vertical-align: middle;
white-space: nowrap;
}
.installation-fee-table thead th {
@ -400,6 +438,7 @@
.installation-fee-platform-summary table {
width: 100%;
min-width: 32rem;
border-collapse: collapse;
font-size: 0.75rem;
font-variant-numeric: tabular-nums;
@ -412,6 +451,7 @@
line-height: 1.5;
text-align: left;
vertical-align: middle;
white-space: nowrap;
}
.installation-fee-platform-summary th {
@ -519,7 +559,11 @@
<button type="button" class="installation-fee-tab" id="installationFeePlatformTab" role="tab" aria-selected="true" aria-controls="installationFeePlatformPanel" data-installation-fee-tab="platform" tabindex="0">平台汇总</button>
<button type="button" class="installation-fee-tab" id="installationFeeDetailTab" role="tab" aria-selected="false" aria-controls="installationFeeDetailPanel" data-installation-fee-tab="detail" tabindex="-1">店铺明细</button>
</div>
<div class="installation-fee-dialog__scroll">
<div
class="installation-fee-dialog__scroll"
data-installation-fee-scroll
aria-label="安装费明细表格,内容超出时可左右拖动或使用底部滚动条"
>
<section class="installation-fee-platform-summary installation-fee-panel is-active" id="installationFeePlatformPanel" role="tabpanel" aria-labelledby="installationFeePlatformTab" data-installation-fee-panel="platform">
<table>
<thead>
@ -580,7 +624,7 @@
var emptyEl = dialog.querySelector('[data-installation-fee-empty]');
var platformEmptyEl = dialog.querySelector('[data-installation-fee-platform-empty]');
var countEl = dialog.querySelector('[data-installation-fee-count]');
var scrollEl = dialog.querySelector('.installation-fee-dialog__scroll');
var scrollEl = dialog.querySelector('[data-installation-fee-scroll]');
var tabsEl = dialog.querySelector('.installation-fee-tabs');
var tabButtons = Array.from(dialog.querySelectorAll('[data-installation-fee-tab]'));
var tabPanels = Array.from(dialog.querySelectorAll('[data-installation-fee-panel]'));
@ -617,6 +661,7 @@
});
if (scrollEl) {
scrollEl.scrollTop = 0;
scrollEl.scrollLeft = 0;
}
updateCount();
}
@ -920,6 +965,66 @@
timer = window.setTimeout(function () { refresh(true); }, 250);
}
var dragState = null;
function finishHorizontalDrag(event) {
if (!dragState) {
return;
}
var state = dragState;
dragState = null;
scrollEl.classList.remove('is-dragging');
if (event && state.pointerId !== null && scrollEl.hasPointerCapture(state.pointerId)) {
scrollEl.releasePointerCapture(state.pointerId);
}
}
if (scrollEl) {
scrollEl.addEventListener('pointerdown', function (event) {
if (event.button !== 0 || event.target.closest('button, a, input, select, textarea')) {
return;
}
dragState = {
active: false,
pointerId: event.pointerId,
startX: event.clientX,
startY: event.clientY,
scrollLeft: scrollEl.scrollLeft
};
});
scrollEl.addEventListener('pointermove', function (event) {
if (!dragState || dragState.pointerId !== event.pointerId) {
return;
}
var dx = event.clientX - dragState.startX;
var dy = event.clientY - dragState.startY;
if (!dragState.active) {
if (Math.abs(dx) < 6 || Math.abs(dx) <= Math.abs(dy)) {
return;
}
dragState.active = true;
scrollEl.classList.add('is-dragging');
scrollEl.setPointerCapture(event.pointerId);
var selection = window.getSelection();
if (selection) {
selection.removeAllRanges();
}
}
event.preventDefault();
scrollEl.scrollLeft = dragState.scrollLeft - dx;
});
['pointerup', 'pointercancel', 'lostpointercapture'].forEach(function (eventName) {
scrollEl.addEventListener(eventName, finishHorizontalDrag);
});
scrollEl.addEventListener('wheel', function (event) {
if (!event.shiftKey || Math.abs(event.deltaY) <= Math.abs(event.deltaX)) {
return;
}
scrollEl.scrollLeft += event.deltaY;
event.preventDefault();
}, { passive: false });
}
openButton.addEventListener('click', function () {
openDialog();
refresh(false);

View File

@ -297,6 +297,13 @@ class ManagementCrudTest extends FeatureTestCase
->assertSee('installation-fee-panel is-active', false)
->assertSeeText('店铺明细')
->assertSee('overflow-x: auto', false)
->assertSee('data-installation-fee-scroll', false)
->assertSee('scrollbar-color: #b9954f #f1ece3', false)
->assertSee('scrollbar-gutter: stable', false)
->assertSee('cursor: grab', false)
->assertSee('min-width: 72rem', false)
->assertSee("scrollEl.addEventListener('pointerdown'", false)
->assertSee('scrollEl.scrollLeft = dragState.scrollLeft - dx', false)
->assertSeeText('共 0 条平台汇总')
->assertSee('--installation-fee-accent: #856632', false)
->assertSee('border-left: 3px solid var(--installation-fee-accent)', false)