商品推广费用当前页导出 #70

Merged
renshen merged 1 commits from feature/renshen-order-promotion-export into main 2026-08-19 12:53:05 +08:00
2 changed files with 86 additions and 7 deletions

View File

@ -44,6 +44,20 @@
<span class="material-icons-outlined" aria-hidden="true">refresh</span>
<span>重置</span>
</a>
<button
type="button"
id="order-data-promotion-export-button"
data-export-contract="order-data.promotion.current-page-export"
data-export-table="order-data-promotion-table"
data-export-filename="商品推广费用明细_{{ $filters['date'] ?? '日期' }}_第{{ $rows?->currentPage() ?? 1 }}页.csv"
@class(['upstream-data-button', 'is-disabled' => !$rows || $rows->isEmpty()])
@if (!$rows || $rows->isEmpty()) disabled="disabled" aria-disabled="true" @endif
aria-label="导出当前页"
title="导出当前页"
>
<span class="material-icons-outlined" aria-hidden="true">download</span>
<span>导出当前页</span>
</button>
</div>
</form>
@ -56,7 +70,7 @@
<div class="upstream-data-table-panel">
<div class="upstream-data-table-scroll">
<table class="upstream-data-table order-data-table" aria-label="商品推广费用明细">
<table id="order-data-promotion-table" class="upstream-data-table order-data-table" aria-label="商品推广费用明细">
<thead>
<tr>
@foreach ($columns as $field => $header)
@ -69,7 +83,11 @@
<tr>
@foreach ($columns as $field => $header)
@php $value = data_get($row, $field); @endphp
<td @class(['is-number' => $field === 'promotion_fee'])>
<td
data-export-value="{{ $value ?? '' }}"
data-export-number="{{ $field === 'promotion_fee' ? 'true' : 'false' }}"
@class(['is-number' => $field === 'promotion_fee'])
>
@if ($field === 'promotion_fee')
{{ $value === null || $value === '' ? '-' : number_format((float) $value, 2) }}
@else
@ -109,3 +127,51 @@
</div>
</div>
</div>
<script>
(() => {
const button = document.getElementById('order-data-promotion-export-button');
if (!button || button.dataset.exportBound === 'true') {
return;
}
button.dataset.exportBound = 'true';
button.addEventListener('click', () => {
const table = document.getElementById(button.dataset.exportTable || '');
const bodyRows = table ? Array.from(table.querySelectorAll('tbody tr:not(.is-empty)')) : [];
if (!table || bodyRows.length === 0) {
return;
}
const protectSpreadsheetFormula = (value) => /^[=+\-@\t\r]/.test(value) ? `'${value}` : value;
const csvCell = (value, isNumber = false) => {
const raw = String(value ?? '').replace(/\r\n?/g, '\n');
const normalized = isNumber ? raw : protectSpreadsheetFormula(raw);
return `"${normalized.replace(/"/g, '""')}"`;
};
const header = Array.from(table.querySelectorAll('thead th'))
.map((cell) => csvCell(cell.textContent.trim()));
const rows = bodyRows.map((row) => Array.from(row.querySelectorAll('td'))
.map((cell) => csvCell(
cell.dataset.exportValue ?? cell.textContent.trim(),
cell.dataset.exportNumber === 'true',
)));
const csv = [header, ...rows].map((row) => row.join(',')).join('\r\n');
const blob = new Blob(['\uFEFF', csv], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = button.dataset.exportFilename || '商品推广费用明细.csv';
document.body.appendChild(link);
link.click();
window.setTimeout(() => {
link.remove();
URL.revokeObjectURL(url);
}, 1000);
});
})();
</script>

View File

@ -33,7 +33,7 @@ class OrderDataPagesUpstreamTest extends FeatureTestCase
'date' => '2026-08-09',
'rows' => [[
'shop_name' => '推广测试店铺',
'product_code' => 'SKU-1',
'product_code' => '=SKU-1',
'promotion_fee' => 9.05,
]],
];
@ -73,16 +73,27 @@ class OrderDataPagesUpstreamTest extends FeatureTestCase
->assertSee('URL.revokeObjectURL(url);', false)
->assertSee('订单售后费用明细_2026-08-01_2026-08-09_第1页.csv', false);
$this->get(route('business.pages.show', [
$promotionResponse = $this->get(route('business.pages.show', [
'section' => 'order-data',
'page' => 'raw-data',
'tab' => 'promotion-expenses',
]))
]));
$promotionResponse
->assertOk()
->assertSeeText('推广测试店铺')
->assertSeeText('SKU-1')
->assertSeeText('9.05')
->assertSee('value="2026-08-09"', false);
->assertSee('value="2026-08-09"', false)
->assertSee('id="order-data-promotion-export-button"', false)
->assertSee('data-export-contract="order-data.promotion.current-page-export"', false)
->assertSee('data-export-table="order-data-promotion-table"', false)
->assertSee('data-export-value="=SKU-1"', false)
->assertSee('data-export-value="9.05"', false)
->assertSee('data-export-number="true"', false)
->assertSee('protectSpreadsheetFormula', false)
->assertSee('URL.revokeObjectURL(url);', false)
->assertSee('商品推广费用明细_2026-08-09_第1页.csv', false);
}
public function test_order_data_page_keeps_rendering_when_the_upstream_is_unavailable(): void
@ -104,7 +115,9 @@ class OrderDataPagesUpstreamTest extends FeatureTestCase
]))
->assertOk()
->assertSeeText('首份共享快照正在准备。')
->assertSeeText('暂无数据');
->assertSeeText('暂无数据')
->assertSee('id="order-data-promotion-export-button"', false)
->assertSee('disabled="disabled"', false);
}
public function test_after_sales_export_is_disabled_when_the_current_page_has_no_rows(): void