财务台账:支持自定义账户总表列宽 #433
@ -102,8 +102,19 @@
|
||||
.ledger-accounts-table thead th,
|
||||
.ledger-accounts-table thead th.num { text-align:center; }
|
||||
.ledger-accounts-table { min-width:max-content; }
|
||||
.ledger-accounts-table tbody td { overflow:hidden; text-overflow:ellipsis; }
|
||||
.ledger-accounts-table .is-column-hidden { display:none !important; }
|
||||
.ledger-keep-column { width:6.5rem; min-width:6.5rem; text-align:center; }
|
||||
.ledger-column-resizer { position:absolute; top:0; right:0; z-index:4; width:.45rem; height:100%; cursor:col-resize; touch-action:none; }
|
||||
.ledger-column-resizer::after { content:""; position:absolute; top:20%; bottom:20%; left:50%; width:1px; background:transparent; transition:background .15s ease; }
|
||||
.ledger-column-resizer:hover::after,
|
||||
.ledger-column-resizer.is-active::after { background:var(--vip-content-accent-dark, #856632); }
|
||||
.ledger-column-resizer:focus { outline:none; }
|
||||
.ledger-column-resizer:focus-visible::after { width:2px; background:var(--vip-content-accent-dark, #856632); }
|
||||
body.is-resizing-ledger-column { cursor:col-resize; user-select:none; }
|
||||
.ledger-table .ledger-plan-column,
|
||||
.ledger-table .ledger-keep-column { padding-right:.2rem; padding-left:.2rem; text-align:center; }
|
||||
.ledger-plan-column { width:5.6rem; min-width:5.6rem; }
|
||||
.ledger-keep-column { width:5.4rem; min-width:5.4rem; }
|
||||
.ledger-table td.wrap { white-space:normal; min-width:14rem; max-width:22rem; line-height:1.5; }
|
||||
.ledger-table .muted { color:var(--vip-content-muted, #595959); }
|
||||
.ledger-table .danger { color:var(--vip-danger, #B80000); }
|
||||
@ -132,11 +143,11 @@
|
||||
.ledger-map-input.is-saving { border-color:var(--vip-content-accent, #B9954F); background:#fffdf6; }
|
||||
.ledger-map-input.is-error { border-color:var(--vip-danger, #B80000); background:#fff5f5; }
|
||||
.ledger-table .ledger-status-column { width:4.6rem; min-width:4.6rem; padding-right:.2rem; padding-left:.2rem; text-align:center; }
|
||||
.ledger-inline-select { display:block; height:1.75rem; margin:0 auto; padding:0 .1rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.4rem; background:#fff; color:var(--vip-content-ink, #171717); font:inherit; font-size:.68rem; font-weight:600; text-align:center; text-align-last:center; cursor:pointer; }
|
||||
.ledger-status-select { width:4.15rem; min-width:4.15rem; }
|
||||
.ledger-type-select { width:5.2rem; min-width:5.2rem; }
|
||||
.ledger-plan-select { width:6.5rem; min-width:6.5rem; }
|
||||
.ledger-keep-select { width:4.8rem; min-width:4.8rem; }
|
||||
.ledger-inline-select { display:block; width:100%; max-width:100%; min-width:0; height:1.75rem; margin:0 auto; padding:0 .1rem; border:1px solid var(--vip-border, #D8D2C7); border-radius:.4rem; background:#fff; color:var(--vip-content-ink, #171717); font:inherit; font-size:.68rem; font-weight:600; text-align:center; text-align-last:center; cursor:pointer; }
|
||||
.ledger-status-select { max-width:4.15rem; }
|
||||
.ledger-type-select { max-width:5.2rem; }
|
||||
.ledger-plan-select { max-width:5rem; }
|
||||
.ledger-keep-select { max-width:3.9rem; }
|
||||
.ledger-inline-select:focus { border-color:var(--vip-content-accent-dark, #856632); outline:none; box-shadow:0 0 0 3px rgba(133,102,50,.16); }
|
||||
.ledger-inline-select[data-status="normal"] { border-color:rgba(83,121,62,.38); background:#f3f7ee; color:var(--vip-success, #53793E); }
|
||||
.ledger-inline-select[data-status="online_banking_abnormal"] { border-color:rgba(184,0,0,.34); background:#fff5f5; color:var(--vip-danger, #B80000); }
|
||||
@ -203,7 +214,7 @@
|
||||
<div class="ledger-column-picker" data-ledger-column-picker data-column-storage-key="caiwu.financial-ledger.accounts.preferences.v1.{{ auth()->id() }}.{{ company_id() }}">
|
||||
<button class="ledger-btn" type="button" data-ledger-column-toggle aria-expanded="false"><span class="material-icons-outlined" aria-hidden="true">view_column</span>列设置</button>
|
||||
<div class="ledger-column-panel" data-ledger-column-panel role="group" aria-label="选择账户总表展示列" hidden>
|
||||
<p class="ledger-column-help">拖动字段可调整列顺序;按住 Alt 并使用 ↑ / ↓ 也可移动。</p>
|
||||
<p class="ledger-column-help">拖动字段可调整列顺序;拖动表头右侧边界可调整列宽,双击边界恢复默认。</p>
|
||||
<div class="ledger-column-options" data-ledger-column-options></div>
|
||||
<div class="ledger-column-actions"><span data-ledger-column-count></span><div class="ledger-column-action-group"><button type="button" data-ledger-column-all>显示全部</button><button type="button" data-ledger-column-reset>恢复默认</button></div></div>
|
||||
</div>
|
||||
@ -219,7 +230,7 @@
|
||||
</div>
|
||||
<div class="ledger-table-wrap ledger-accounts-table-wrap">
|
||||
<table class="ledger-table ledger-accounts-table">
|
||||
<thead><tr><th>公司名称</th><th>账户简称</th><th>账户类型</th><th class="ledger-status-column">账户状态</th><th>地区</th><th>账号</th><th>开户行</th><th>开户城市/网点</th><th>公司规划</th><th class="ledger-keep-column">账户是否保留</th><th>后4位</th><th>单笔限额</th><th>日限额</th><th>月限额</th><th>年限额</th><th class="num">可用金额</th><th class="num">不可用金额</th><th class="num">每日可出</th><th class="num">预计出清天数</th><th>提额目标</th><th>提额状态</th><th>提额动作</th><th>已做动作</th><th>下一步</th><th>备注</th><th>责任人</th></tr></thead>
|
||||
<thead><tr><th>公司名称</th><th>账户简称</th><th>账户类型</th><th class="ledger-status-column">账户状态</th><th>地区</th><th>账号</th><th>开户行</th><th>开户城市/网点</th><th class="ledger-plan-column">公司规划</th><th class="ledger-keep-column">账户是否保留</th><th>后4位</th><th>单笔限额</th><th>日限额</th><th>月限额</th><th>年限额</th><th class="num">可用金额</th><th class="num">不可用金额</th><th class="num">每日可出</th><th class="num">预计出清天数</th><th>提额目标</th><th>提额状态</th><th>提额动作</th><th>已做动作</th><th>下一步</th><th>备注</th><th>责任人</th></tr></thead>
|
||||
<tbody>
|
||||
@forelse ($ledgerAccounts as $row)
|
||||
<tr>
|
||||
@ -251,7 +262,7 @@
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $row->region }}</td><td>{{ $row->account_no }}</td><td>{{ $row->bank_name }}</td><td>{{ $row->bank_city_branch }}</td>
|
||||
<td>
|
||||
<td class="ledger-plan-column">
|
||||
<span class="ledger-account-readonly" data-account-edit-display data-account-plan-display>{{ $row->company_plan ?: '未填写' }}</span>
|
||||
@if ($canEditAccountStatus)
|
||||
<select class="ledger-inline-select ledger-plan-select ledger-account-editor" data-account-editor data-account-plan data-account-short="{{ $row->account_short }}" data-url="{{ route('business.financial-ledger.accounts.plan') }}" aria-label="账户 {{ $row->account_short }} 公司规划" disabled>
|
||||
@ -490,6 +501,57 @@
|
||||
});
|
||||
let visibleAccountColumns = new Set(initialAccountColumnKeys);
|
||||
let accountColumnOrder = [...initialAccountColumnKeys];
|
||||
const initialAccountColumnWidths = new Map(
|
||||
initialAccountColumnHeaders.map((header, index) => [
|
||||
initialAccountColumnKeys[index],
|
||||
Math.max(48, Math.round(header.getBoundingClientRect().width * 10) / 10),
|
||||
]),
|
||||
);
|
||||
let accountColumnWidths = new Map(initialAccountColumnWidths);
|
||||
const clampAccountColumnWidth = (value) => Math.min(720, Math.max(48, Math.round(Number(value) || 0)));
|
||||
|
||||
const accountColumnGroup = () => {
|
||||
const table = accountPanel?.querySelector('.ledger-accounts-table');
|
||||
if (!table) return null;
|
||||
let group = table.querySelector('colgroup[data-ledger-column-group]');
|
||||
if (!group) {
|
||||
group = document.createElement('colgroup');
|
||||
group.dataset.ledgerColumnGroup = 'true';
|
||||
table.insertBefore(group, table.firstChild);
|
||||
}
|
||||
initialAccountColumnKeys.forEach((key) => {
|
||||
if (group.querySelector('col[data-ledger-column-key="' + key + '"]')) return;
|
||||
const column = document.createElement('col');
|
||||
column.dataset.ledgerColumnKey = key;
|
||||
group.appendChild(column);
|
||||
});
|
||||
|
||||
return group;
|
||||
};
|
||||
|
||||
const applyAccountColumnWidths = () => {
|
||||
const table = accountPanel?.querySelector('.ledger-accounts-table');
|
||||
const group = accountColumnGroup();
|
||||
if (!table || !group) return;
|
||||
const visible = new Set(visibleAccountColumns);
|
||||
let totalWidth = 0;
|
||||
accountColumnOrder.forEach((key) => {
|
||||
const column = group.querySelector('col[data-ledger-column-key="' + key + '"]');
|
||||
if (!column) return;
|
||||
const width = visible.has(key)
|
||||
? clampAccountColumnWidth(accountColumnWidths.get(key) ?? initialAccountColumnWidths.get(key))
|
||||
: 0;
|
||||
column.style.width = width + 'px';
|
||||
totalWidth += width;
|
||||
const handle = table.querySelector(
|
||||
'thead th[data-ledger-column-key="' + key + '"] [data-ledger-column-resizer]',
|
||||
);
|
||||
handle?.setAttribute('aria-valuenow', String(width));
|
||||
});
|
||||
table.style.tableLayout = 'fixed';
|
||||
table.style.width = Math.max(1, totalWidth) + 'px';
|
||||
table.style.minWidth = Math.max(1, totalWidth) + 'px';
|
||||
};
|
||||
|
||||
const normaliseColumnKeys = (values, fallback) => {
|
||||
const valid = new Set(initialAccountColumnKeys);
|
||||
@ -508,6 +570,16 @@
|
||||
if (!table) return;
|
||||
const headerRow = table.tHead?.rows?.[0];
|
||||
if (!headerRow) return;
|
||||
const group = accountColumnGroup();
|
||||
if (group) {
|
||||
const columnsByKey = new Map(
|
||||
[...group.children].map((column) => [column.dataset.ledgerColumnKey, column]),
|
||||
);
|
||||
accountColumnOrder.forEach((key) => {
|
||||
const column = columnsByKey.get(key);
|
||||
if (column) group.appendChild(column);
|
||||
});
|
||||
}
|
||||
const headersByKey = new Map(
|
||||
[...headerRow.children].map((header) => [header.dataset.ledgerColumnKey, header]),
|
||||
);
|
||||
@ -540,6 +612,7 @@
|
||||
if (accountColumnCount) {
|
||||
accountColumnCount.textContent = '显示 ' + visible.size + ' / ' + initialAccountColumnKeys.length + ' 列';
|
||||
}
|
||||
applyAccountColumnWidths();
|
||||
};
|
||||
|
||||
const saveAccountColumnPreferences = () => {
|
||||
@ -547,6 +620,7 @@
|
||||
localStorage.setItem(accountColumnKey, JSON.stringify({
|
||||
visible: [...visibleAccountColumns],
|
||||
order: accountColumnOrder,
|
||||
widths: Object.fromEntries(accountColumnWidths),
|
||||
}));
|
||||
} catch (_) {}
|
||||
};
|
||||
@ -560,11 +634,81 @@
|
||||
const savedVisible = Array.isArray(saved?.visible) ? saved.visible : initialAccountColumnKeys;
|
||||
visibleAccountColumns = new Set(normaliseColumnKeys(savedVisible, initialAccountColumnKeys));
|
||||
accountColumnOrder = normaliseColumnKeys(saved?.order, initialAccountColumnKeys);
|
||||
accountColumnWidths = new Map(initialAccountColumnWidths);
|
||||
initialAccountColumnKeys.forEach((key) => {
|
||||
if (saved?.widths && saved.widths[key] !== undefined) {
|
||||
accountColumnWidths.set(key, clampAccountColumnWidth(saved.widths[key]));
|
||||
}
|
||||
});
|
||||
} catch (_) {
|
||||
visibleAccountColumns = new Set(initialAccountColumnKeys);
|
||||
accountColumnOrder = [...initialAccountColumnKeys];
|
||||
accountColumnWidths = new Map(initialAccountColumnWidths);
|
||||
}
|
||||
|
||||
initialAccountColumnHeaders.forEach((header, index) => {
|
||||
const key = initialAccountColumnKeys[index];
|
||||
if (header.querySelector('[data-ledger-column-resizer]')) return;
|
||||
const label = header.textContent.trim() || '未命名字段';
|
||||
const resizer = document.createElement('span');
|
||||
resizer.className = 'ledger-column-resizer';
|
||||
resizer.dataset.ledgerColumnResizer = 'true';
|
||||
resizer.tabIndex = 0;
|
||||
resizer.setAttribute('role', 'separator');
|
||||
resizer.setAttribute('aria-orientation', 'vertical');
|
||||
resizer.setAttribute('aria-label', '调整“' + label + '”列宽');
|
||||
resizer.setAttribute('aria-valuemin', '48');
|
||||
resizer.setAttribute('aria-valuemax', '720');
|
||||
resizer.title = '拖动调整列宽,双击恢复默认';
|
||||
|
||||
const setWidth = (value, persist = false) => {
|
||||
accountColumnWidths.set(key, clampAccountColumnWidth(value));
|
||||
applyAccountColumnWidths();
|
||||
if (persist) saveAccountColumnPreferences();
|
||||
};
|
||||
|
||||
resizer.addEventListener('pointerdown', (event) => {
|
||||
if (event.button !== 0) return;
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const startX = event.clientX;
|
||||
const startWidth = Number(accountColumnWidths.get(key))
|
||||
|| header.getBoundingClientRect().width;
|
||||
resizer.classList.add('is-active');
|
||||
document.body.classList.add('is-resizing-ledger-column');
|
||||
|
||||
const move = (moveEvent) => {
|
||||
setWidth(startWidth + moveEvent.clientX - startX);
|
||||
};
|
||||
const finish = () => {
|
||||
document.removeEventListener('pointermove', move);
|
||||
document.removeEventListener('pointerup', finish);
|
||||
document.removeEventListener('pointercancel', finish);
|
||||
resizer.classList.remove('is-active');
|
||||
document.body.classList.remove('is-resizing-ledger-column');
|
||||
saveAccountColumnPreferences();
|
||||
};
|
||||
document.addEventListener('pointermove', move);
|
||||
document.addEventListener('pointerup', finish);
|
||||
document.addEventListener('pointercancel', finish);
|
||||
});
|
||||
resizer.addEventListener('keydown', (event) => {
|
||||
if (!['ArrowLeft', 'ArrowRight'].includes(event.key)) return;
|
||||
event.preventDefault();
|
||||
const currentWidth = Number(accountColumnWidths.get(key))
|
||||
|| header.getBoundingClientRect().width;
|
||||
setWidth(currentWidth + (event.key === 'ArrowLeft' ? -1 : 1) * (event.shiftKey ? 24 : 8), true);
|
||||
});
|
||||
resizer.addEventListener('dblclick', (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
setWidth(initialAccountColumnWidths.get(key), true);
|
||||
notify(label + '列宽已恢复默认');
|
||||
});
|
||||
resizer.addEventListener('click', (event) => event.stopPropagation());
|
||||
header.appendChild(resizer);
|
||||
});
|
||||
|
||||
accountColumnOrder.forEach((key) => {
|
||||
const header = headersByKey.get(key);
|
||||
if (!header) return;
|
||||
@ -619,6 +763,7 @@
|
||||
accountColumnPicker.querySelector('[data-ledger-column-reset]')?.addEventListener('click', () => {
|
||||
visibleAccountColumns = new Set(initialAccountColumnKeys);
|
||||
accountColumnOrder = [...initialAccountColumnKeys];
|
||||
accountColumnWidths = new Map(initialAccountColumnWidths);
|
||||
accountColumnOptions.querySelectorAll('[data-ledger-column-check]').forEach((checkbox) => {
|
||||
checkbox.checked = true;
|
||||
});
|
||||
|
||||
@ -88,7 +88,7 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
|
||||
->assertSee('data-account-template-download', false)
|
||||
->assertSee('data-account-template-download-status', false)
|
||||
->assertSee('<th>账户简称</th><th>账户类型</th><th class="ledger-status-column">账户状态</th>', false)
|
||||
->assertSee('<th>公司规划</th><th class="ledger-keep-column">账户是否保留</th><th>后4位</th>', false)
|
||||
->assertSee('<th class="ledger-plan-column">公司规划</th><th class="ledger-keep-column">账户是否保留</th><th>后4位</th>', false)
|
||||
->assertSee('data-account-short="测试账户" data-account-should-keep="是"', false)
|
||||
->assertSee('网商银行')
|
||||
->assertSee('ledger-accounts-table-wrap', false)
|
||||
@ -103,6 +103,7 @@ class FinanceLedgerAccountStatusTest extends FeatureTestCase
|
||||
->assertSee('data-account-edit-display', false)
|
||||
->assertSee('列设置')
|
||||
->assertSee('拖动字段可调整列顺序')
|
||||
->assertSee('拖动表头右侧边界可调整列宽')
|
||||
->assertSee('恢复默认')
|
||||
->assertSee('可用金额合计')
|
||||
->assertSee('不可用金额合计')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user