WIP: 种菜应收对账平台标签 #93
@ -1,5 +1,6 @@
|
||||
<!-- 种菜应收对账 - 嵌入业务页面的21列表格 -->
|
||||
<style>
|
||||
[x-cloak] { display: none !important; }
|
||||
.zhongcai-table-wrapper { overflow-x: auto; border-radius: 0.5rem; border: 1px solid #e5e7eb; }
|
||||
.zhongcai-table { min-width: 100%; white-space: nowrap; font-size: 0.8125rem; }
|
||||
.zhongcai-table thead th { position: sticky; top: 0; background: #f9fafb; text-align: left; color: #6b7280; padding: 0.5rem 0.6rem; border-bottom: 1px solid #e5e7eb; font-weight: 600; }
|
||||
@ -8,9 +9,71 @@
|
||||
.amount-cell { text-align: right; font-variant-numeric: tabular-nums; }
|
||||
</style>
|
||||
|
||||
@php $pageIds = $records->pluck('id')->map(fn($id) => (string) $id)->values(); @endphp
|
||||
@php
|
||||
$pageIds = $records->pluck('id')->map(fn($id) => (string) $id)->values();
|
||||
$platformTabs = [
|
||||
'jd' => '京东',
|
||||
'douyin' => '抖音',
|
||||
'tmall' => '天猫',
|
||||
'pdd' => '拼多多',
|
||||
'taobao' => '淘宝',
|
||||
'jingxi' => '京喜',
|
||||
];
|
||||
$douyinHeaders = [
|
||||
'日期', '订单编号', '财务售价', '财务运费', '财务佣金',
|
||||
'财务空包费', '财务打款金额', '笔数', '备注', '正确店铺', '负责人',
|
||||
'账户', '类型', '创建人', '标签', '所属利润中心', '负责平台', 'ID',
|
||||
'实收', '差异', '退款',
|
||||
];
|
||||
$jdHeaders = [
|
||||
'日期', '订单编号', '财务售价', '财务运费', '财务佣金', '财务空包费',
|
||||
'财务打款金额', '笔数', '备注', '正确店铺', '负责人', '账户', '类型',
|
||||
'创建人', '标签', '所属利润中心', '负责平台', 'ID', '实收', '运费',
|
||||
'差异', '是否退款', '备注', '问题件',
|
||||
];
|
||||
$tmallHeaders = [
|
||||
'日期', '订单编号', '财务售价', '财务运费', '财务佣金', '财务空包费',
|
||||
'财务打款金额', '笔数', '备注', '正确店铺', '负责人', '账户', '类型',
|
||||
'创建人', '标签', '所属利润中心', '负责平台', 'ID', '实收', '差异',
|
||||
'是否退款', '备注', '问题件',
|
||||
];
|
||||
$pddHeaders = [
|
||||
'日期', '订单编号', '财务售价', '财务运费', '财务佣金', '财务空包费',
|
||||
'财务打款金额', '笔数', '备注', '正确店铺', '负责人', '账户', '类型',
|
||||
'创建人', '标签', '所属利润中心', '负责平台', 'ID', '实收', '差异',
|
||||
'退款', '小额打款',
|
||||
];
|
||||
$taobaoHeaders = [
|
||||
'日期', '订单编号', '财务售价', '财务运费', '财务佣金', '财务空包费',
|
||||
'财务打款金额', '笔数', '备注', '正确店铺', '负责人', '账户', '类型',
|
||||
'创建人', '标签', '所属利润中心', '负责平台', 'ID', '实收', '差异',
|
||||
'是否退款', '备注', '问题件',
|
||||
];
|
||||
$platformIds = [
|
||||
'all' => $pageIds->all(),
|
||||
] + array_fill_keys(array_keys($platformTabs), []);
|
||||
|
||||
<div class="w-full" x-data="{ selected: [] }">
|
||||
foreach ($records as $record) {
|
||||
$platform = (string) $record->responsible_platform;
|
||||
$platformKey = str_contains($platform, '京东')
|
||||
? 'jd'
|
||||
: (str_contains($platform, '抖音')
|
||||
? 'douyin'
|
||||
: (str_contains($platform, '天猫')
|
||||
? 'tmall'
|
||||
: (str_contains($platform, '拼多多')
|
||||
? 'pdd'
|
||||
: (str_contains($platform, '淘宝')
|
||||
? 'taobao'
|
||||
: (str_contains($platform, '京喜') ? 'jingxi' : null)))));
|
||||
|
||||
if ($platformKey !== null) {
|
||||
$platformIds[$platformKey][] = (string) $record->id;
|
||||
}
|
||||
}
|
||||
@endphp
|
||||
|
||||
<div class="w-full" x-data="{ selected: [], activePlatform: 'all', platformIds: @js($platformIds) }">
|
||||
<form id="zhongcai-export-form" method="GET" action="{{ route('zhongcai-reconciliations.export') }}"></form>
|
||||
|
||||
<div class="mb-4 flex flex-wrap items-end justify-between gap-3 border-b border-gray-200 pb-4">
|
||||
@ -63,16 +126,35 @@
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mb-4 flex flex-wrap items-center gap-2" role="tablist" aria-label="平台筛选">
|
||||
<span class="mr-2 text-sm font-medium text-gray-700">平台筛选</span>
|
||||
@foreach ($platformTabs as $key => $label)
|
||||
<button type="button"
|
||||
role="tab"
|
||||
aria-controls="zhongcai-reconciliation-table"
|
||||
x-on:click="activePlatform = '{{ $key }}'; selected = []"
|
||||
x-bind:aria-selected="activePlatform === '{{ $key }}'"
|
||||
x-bind:class="activePlatform === '{{ $key }}'
|
||||
? 'border-[var(--vip-content-accent-dark)] bg-[var(--vip-content-accent-dark)] text-white'
|
||||
: 'border-[var(--vip-border)] bg-white text-gray-700 hover:border-[var(--vip-content-accent)] hover:text-[var(--vip-content-accent-dark)]'"
|
||||
class="inline-flex h-9 items-center gap-1 rounded-md border px-3 text-sm font-medium transition-colors">
|
||||
<span>{{ $label }}</span>
|
||||
<span class="text-xs opacity-75">({{ count($platformIds[$key]) }})</span>
|
||||
</button>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
{{-- 21列数据表格 --}}
|
||||
<div class="zhongcai-table-wrapper">
|
||||
<table class="zhongcai-table divide-y divide-gray-200">
|
||||
<table id="zhongcai-reconciliation-table" class="zhongcai-table divide-y divide-gray-200">
|
||||
<thead>
|
||||
<tr>
|
||||
<tr x-show="!['jd', 'douyin', 'tmall', 'pdd', 'taobao'].includes(activePlatform)">
|
||||
<th class="w-12 text-center">
|
||||
<input type="checkbox" aria-label="{{ trans('store_zhongcai_reconciliations.select_all') }}"
|
||||
@if ($pageIds->isEmpty()) disabled @endif
|
||||
x-on:change="selected = $event.target.checked ? @js($pageIds) : []"
|
||||
x-bind:checked="selected.length === {{ $pageIds->count() }} && selected.length > 0"
|
||||
x-bind:disabled="platformIds[activePlatform].length === 0"
|
||||
x-on:change="selected = $event.target.checked ? platformIds[activePlatform] : []"
|
||||
x-bind:checked="selected.length === platformIds[activePlatform].length && selected.length > 0"
|
||||
class="h-4 w-4 rounded border-gray-300 text-green" />
|
||||
</th>
|
||||
<th>{{ trans('store_zhongcai_reconciliations.bill_date') }}</th>
|
||||
@ -98,17 +180,98 @@
|
||||
<th>{{ trans('store_zhongcai_reconciliations.difference_data') }}</th>
|
||||
<th class="text-right">{{ trans('general.actions') }}</th>
|
||||
</tr>
|
||||
<tr data-platform-header="jd" x-show="activePlatform === 'jd'" x-cloak>
|
||||
<th class="w-12 text-center">
|
||||
<input type="checkbox" aria-label="{{ trans('store_zhongcai_reconciliations.select_all') }}"
|
||||
@if ($pageIds->isEmpty()) disabled @endif
|
||||
x-bind:disabled="platformIds[activePlatform].length === 0"
|
||||
x-on:change="selected = $event.target.checked ? platformIds[activePlatform] : []"
|
||||
x-bind:checked="selected.length === platformIds[activePlatform].length && selected.length > 0"
|
||||
class="h-4 w-4 rounded border-gray-300 text-green" />
|
||||
</th>
|
||||
@foreach ($jdHeaders as $header)
|
||||
<th>{{ $header }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
<tr data-platform-header="douyin" x-show="activePlatform === 'douyin'" x-cloak>
|
||||
<th class="w-12 text-center">
|
||||
<input type="checkbox" aria-label="{{ trans('store_zhongcai_reconciliations.select_all') }}"
|
||||
@if ($pageIds->isEmpty()) disabled @endif
|
||||
x-bind:disabled="platformIds[activePlatform].length === 0"
|
||||
x-on:change="selected = $event.target.checked ? platformIds[activePlatform] : []"
|
||||
x-bind:checked="selected.length === platformIds[activePlatform].length && selected.length > 0"
|
||||
class="h-4 w-4 rounded border-gray-300 text-green" />
|
||||
</th>
|
||||
@foreach ($douyinHeaders as $header)
|
||||
<th>{{ $header }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
<tr data-platform-header="tmall" x-show="activePlatform === 'tmall'" x-cloak>
|
||||
<th class="w-12 text-center">
|
||||
<input type="checkbox" aria-label="{{ trans('store_zhongcai_reconciliations.select_all') }}"
|
||||
@if ($pageIds->isEmpty()) disabled @endif
|
||||
x-bind:disabled="platformIds[activePlatform].length === 0"
|
||||
x-on:change="selected = $event.target.checked ? platformIds[activePlatform] : []"
|
||||
x-bind:checked="selected.length === platformIds[activePlatform].length && selected.length > 0"
|
||||
class="h-4 w-4 rounded border-gray-300 text-green" />
|
||||
</th>
|
||||
@foreach ($tmallHeaders as $header)
|
||||
<th>{{ $header }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
<tr data-platform-header="pdd" x-show="activePlatform === 'pdd'" x-cloak>
|
||||
<th class="w-12 text-center">
|
||||
<input type="checkbox" aria-label="{{ trans('store_zhongcai_reconciliations.select_all') }}"
|
||||
@if ($pageIds->isEmpty()) disabled @endif
|
||||
x-bind:disabled="platformIds[activePlatform].length === 0"
|
||||
x-on:change="selected = $event.target.checked ? platformIds[activePlatform] : []"
|
||||
x-bind:checked="selected.length === platformIds[activePlatform].length && selected.length > 0"
|
||||
class="h-4 w-4 rounded border-gray-300 text-green" />
|
||||
</th>
|
||||
@foreach ($pddHeaders as $header)
|
||||
<th>{{ $header }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
<tr data-platform-header="taobao" x-show="activePlatform === 'taobao'" x-cloak>
|
||||
<th class="w-12 text-center">
|
||||
<input type="checkbox" aria-label="{{ trans('store_zhongcai_reconciliations.select_all') }}"
|
||||
@if ($pageIds->isEmpty()) disabled @endif
|
||||
x-bind:disabled="platformIds[activePlatform].length === 0"
|
||||
x-on:change="selected = $event.target.checked ? platformIds[activePlatform] : []"
|
||||
x-bind:checked="selected.length === platformIds[activePlatform].length && selected.length > 0"
|
||||
class="h-4 w-4 rounded border-gray-300 text-green" />
|
||||
</th>
|
||||
@foreach ($taobaoHeaders as $header)
|
||||
<th>{{ $header }}</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-gray-100 bg-white">
|
||||
@if ($records->isEmpty())
|
||||
<tr>
|
||||
<tr x-show="activePlatform === 'all'" x-cloak>
|
||||
<td colspan="23" class="px-5 py-10 text-center text-gray-400">
|
||||
{{ trans('store_zhongcai_reconciliations.no_records') }}
|
||||
</td>
|
||||
</tr>
|
||||
@else
|
||||
@foreach ($records as $r)
|
||||
<tr>
|
||||
@php
|
||||
$platform = (string) $r->responsible_platform;
|
||||
$platformKey = str_contains($platform, '京东')
|
||||
? 'jd'
|
||||
: (str_contains($platform, '抖音')
|
||||
? 'douyin'
|
||||
: (str_contains($platform, '天猫')
|
||||
? 'tmall'
|
||||
: (str_contains($platform, '拼多多')
|
||||
? 'pdd'
|
||||
: (str_contains($platform, '淘宝')
|
||||
? 'taobao'
|
||||
: (str_contains($platform, '京喜') ? 'jingxi' : 'other')))));
|
||||
@endphp
|
||||
<tr x-show="activePlatform === 'all' || activePlatform === '{{ $platformKey }}'"
|
||||
x-cloak
|
||||
data-platform="{{ $platformKey }}">
|
||||
<td class="text-center">
|
||||
<input type="checkbox" name="ids[]" value="{{ $r->id }}" form="zhongcai-export-form"
|
||||
x-model="selected" aria-label="{{ trans('store_zhongcai_reconciliations.select_record') }}"
|
||||
@ -152,6 +315,15 @@
|
||||
</tr>
|
||||
@endforeach
|
||||
@endif
|
||||
@foreach ($platformTabs as $key => $label)
|
||||
@if (count($platformIds[$key]) === 0)
|
||||
<tr x-show="activePlatform === '{{ $key }}'" x-cloak>
|
||||
<td colspan="23" class="px-5 py-10 text-center text-gray-400">
|
||||
当前页暂无{{ $label }}平台数据
|
||||
</td>
|
||||
</tr>
|
||||
@endif
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
121
tests/Feature/Business/ZhongcaiReconciliationPlatformTest.php
Normal file
121
tests/Feature/Business/ZhongcaiReconciliationPlatformTest.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature\Business;
|
||||
|
||||
use App\Models\Auth\Permission;
|
||||
use App\Models\StoreReceivables\StoreZhongcaiReconciliation;
|
||||
use Tests\Feature\FeatureTestCase;
|
||||
|
||||
class ZhongcaiReconciliationPlatformTest extends FeatureTestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->user->attachPermission(
|
||||
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
||||
);
|
||||
}
|
||||
|
||||
public function testReconciliationPageProvidesPlatformTabsForCurrentPageRows(): void
|
||||
{
|
||||
foreach ([
|
||||
['platform' => '京东商城', 'order' => 'JD-TEST-001'],
|
||||
['platform' => '抖音小店', 'order' => 'DY-TEST-001'],
|
||||
['platform' => '天猫旗舰店', 'order' => 'TM-TEST-001'],
|
||||
['platform' => '拼多多店铺', 'order' => 'PDD-TEST-001'],
|
||||
['platform' => '淘宝店铺', 'order' => 'TB-TEST-001'],
|
||||
['platform' => '京喜店铺', 'order' => 'JX-TEST-001'],
|
||||
] as $row) {
|
||||
StoreZhongcaiReconciliation::create([
|
||||
'company_id' => $this->company->id,
|
||||
'bill_date' => '2026-08-01',
|
||||
'planting_order_no' => $row['order'],
|
||||
'responsible_platform' => $row['platform'],
|
||||
]);
|
||||
}
|
||||
|
||||
$response = $this->loginAs()->get(route('business.pages.show', [
|
||||
'section' => 'store-receivables',
|
||||
'page' => 'zhongcai-reconciliation',
|
||||
]));
|
||||
|
||||
$response
|
||||
->assertOk()
|
||||
->assertSee('平台筛选', false)
|
||||
->assertSee('>京东<', false)
|
||||
->assertSee('>抖音<', false)
|
||||
->assertSee('>天猫<', false)
|
||||
->assertSee('>拼多多<', false)
|
||||
->assertSee('>淘宝<', false)
|
||||
->assertSee('>京喜<', false)
|
||||
->assertDontSee('>全部<', false)
|
||||
->assertSee('data-platform="jd"', false)
|
||||
->assertSee('data-platform="douyin"', false)
|
||||
->assertSee('data-platform="tmall"', false)
|
||||
->assertSee('data-platform="pdd"', false)
|
||||
->assertSee('data-platform="taobao"', false)
|
||||
->assertSee('data-platform="jingxi"', false)
|
||||
->assertSee('data-platform-header="jd"', false)
|
||||
->assertSeeInOrder([
|
||||
'data-platform-header="jd"', '日期', '订单编号', '财务售价', '财务运费',
|
||||
'财务佣金', '财务空包费', '财务打款金额', '笔数', '备注', '正确店铺',
|
||||
'负责人', '账户', '类型', '创建人', '标签', '所属利润中心', '负责平台',
|
||||
'ID', '实收', '运费', '差异', '是否退款', '备注', '问题件',
|
||||
], false)
|
||||
->assertSee('data-platform-header="douyin"', false)
|
||||
->assertSeeInOrder([
|
||||
'日期', '订单编号', '财务售价', '财务运费', '财务佣金',
|
||||
'财务空包费', '财务打款金额', '笔数', '备注', '正确店铺', '负责人',
|
||||
'账户', '类型', '创建人', '标签', '所属利润中心', '负责平台', 'ID',
|
||||
'实收', '差异', '退款',
|
||||
], false)
|
||||
->assertSee('data-platform-header="tmall"', false)
|
||||
->assertSeeInOrder([
|
||||
'data-platform-header="tmall"', '日期', '订单编号', '财务售价', '财务运费',
|
||||
'财务佣金', '财务空包费', '财务打款金额', '笔数', '备注',
|
||||
'正确店铺', '负责人', '账户', '类型', '创建人', '标签', '所属利润中心',
|
||||
'负责平台', 'ID', '实收', '差异', '是否退款', '备注', '问题件',
|
||||
], false)
|
||||
->assertSee('data-platform-header="pdd"', false)
|
||||
->assertSeeInOrder([
|
||||
'data-platform-header="pdd"', '日期', '订单编号', '财务售价', '财务运费',
|
||||
'财务佣金', '财务空包费', '财务打款金额', '笔数', '备注',
|
||||
'正确店铺', '负责人', '账户', '类型', '创建人', '标签', '所属利润中心',
|
||||
'负责平台', 'ID', '实收', '差异', '退款', '小额打款',
|
||||
], false)
|
||||
->assertSee('data-platform-header="taobao"', false)
|
||||
->assertSeeInOrder([
|
||||
'data-platform-header="taobao"', '日期', '订单编号', '财务售价', '财务运费',
|
||||
'财务佣金', '财务空包费', '财务打款金额', '笔数', '备注',
|
||||
'正确店铺', '负责人', '账户', '类型', '创建人', '标签', '所属利润中心',
|
||||
'负责平台', 'ID', '实收', '差异', '是否退款', '备注', '问题件',
|
||||
], false)
|
||||
->assertSee('JD-TEST-001')
|
||||
->assertSee('DY-TEST-001')
|
||||
->assertSee('TM-TEST-001')
|
||||
->assertSee('PDD-TEST-001')
|
||||
->assertSee('TB-TEST-001')
|
||||
->assertSee('JX-TEST-001');
|
||||
}
|
||||
|
||||
public function testReconciliationPageShowsPlatformSpecificEmptyState(): void
|
||||
{
|
||||
StoreZhongcaiReconciliation::create([
|
||||
'company_id' => $this->company->id,
|
||||
'bill_date' => '2026-08-01',
|
||||
'planting_order_no' => 'JD-TEST-002',
|
||||
'responsible_platform' => '京东',
|
||||
]);
|
||||
|
||||
$response = $this->loginAs()->get(route('business.pages.show', [
|
||||
'section' => 'store-receivables',
|
||||
'page' => 'zhongcai-reconciliation',
|
||||
]));
|
||||
|
||||
$response
|
||||
->assertOk()
|
||||
->assertSee('当前页暂无抖音平台数据', false)
|
||||
->assertSee('当前页暂无天猫平台数据', false);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user