diff --git a/resources/views/business/pages/refunds/small-payment-work-orders.blade.php b/resources/views/business/pages/refunds/small-payment-work-orders.blade.php index 14efb903..39aab483 100644 --- a/resources/views/business/pages/refunds/small-payment-work-orders.blade.php +++ b/resources/views/business/pages/refunds/small-payment-work-orders.blade.php @@ -36,10 +36,9 @@ default => '业务镜像尚未建立', }; $lastSucceededAt = $smallPaymentSyncStatus['last_succeeded_at'] ?? null; - $datetimeLocalValue = static function (string $value): string { - $value = str_replace(' ', 'T', trim($value)); - - return substr(str_contains($value, 'T') ? $value : $value . 'T00:00', 0, 16); + // 日期控件只承载日期:时区无关,服务端会把纯日期补成整天半开区间(from 00:00:00 / to 次日 00:00:00) + $dateValue = static function (string $value): string { + return substr(str_replace(' ', 'T', trim($value)), 0, 10); }; @endphp @@ -96,6 +95,256 @@ background: rgba(147, 95, 7, 0.06); color: var(--vip-warning, #935f07); } + + /* + * 筛选下拉:可滚动的选项面板。 + * 原生 的面板由浏览器绘制,高度/滚动条无法控制(选项多时看不全也滑不动), + * 因此改为自绘面板:固定最大高度 + 内部滚动,风格对齐 .finance-table-field-picker__panel。 + */ + .upstream-data-combobox { + position: relative; + display: flex; + align-items: center; + width: 100%; + } + .upstream-data-combobox input { padding-right: 2.25rem; } + .upstream-data-combobox input::-webkit-search-cancel-button { + -webkit-appearance: none; + appearance: none; + } + .upstream-data-combobox__toggle { + position: absolute; + right: 0.25rem; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.75rem; + height: 1.75rem; + padding: 0; + border: 0; + border-radius: 0.35rem; + background: transparent; + color: var(--vip-content-muted, #595959); + cursor: pointer; + } + .upstream-data-combobox__toggle:hover { + background: rgba(133, 102, 50, 0.12); + color: var(--vip-content-accent-dark, #856632); + } + .upstream-data-combobox__toggle .material-icons-outlined { + font-size: 1.125rem; + transition: transform 0.15s ease; + } + .upstream-data-combobox.is-open .upstream-data-combobox__toggle .material-icons-outlined { + transform: rotate(180deg); + } + .upstream-data-combobox__menu { + position: absolute; + top: calc(100% + 0.35rem); + left: 0; + right: 0; + z-index: 30; + max-height: min(18rem, 50vh); + overflow: auto; + padding: 0.25rem; + border: 1px solid var(--vip-border, #d8d2c7); + border-radius: 0.5rem; + background: var(--vip-surface, #ffffff); + box-shadow: 0 4px 8px rgba(23, 23, 23, 0.1); + overscroll-behavior: contain; + scrollbar-color: var(--vip-content-accent, #b9954f) #efede8; + scrollbar-width: thin; + } + .upstream-data-combobox__menu[hidden] { display: none; } + .upstream-data-combobox__option { + display: block; + width: 100%; + min-height: 1.75rem; + padding: 0.25rem 0.5rem; + border: 0; + border-radius: 0.35rem; + background: transparent; + color: var(--vip-content-ink, #171717); + cursor: pointer; + font: inherit; + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + .upstream-data-combobox__option:hover, + .upstream-data-combobox__option.is-active { + background: rgba(133, 102, 50, 0.12); + color: var(--vip-content-accent-dark, #856632); + } + .upstream-data-combobox__option[aria-selected="true"] { font-weight: 600; } + .upstream-data-combobox__empty { + margin: 0; + padding: 0.5rem; + color: var(--vip-content-muted, #595959); + font-size: 0.75rem; + text-align: center; + } + .upstream-data-combobox__empty[hidden] { display: none; } + + /* + * 日期选择器:对齐「淘系微信账户」账单页的日期控件 + * (文本输入框 + 右侧日历按钮 + 中文月历弹层,选中/今天用品牌色)。 + * 原来是 datetime-local,会显示成 "2026-08-29T00:00",与账单页不一致。 + */ + .upstream-data-field__label { + color: var(--vip-content-muted, #595959); + font-size: 0.75rem; + font-weight: 500; + line-height: 1.333; + } + .upstream-data-field--dates { flex: 1 1 20rem; } + .upstream-data-date-range { + display: flex; + align-items: center; + gap: 0.5rem; + } + .upstream-data-date { + position: relative; + display: flex; + flex: 1 1 8.5rem; + min-width: 0; + } + .upstream-data-date input { + flex: 1 1 auto; + width: auto; + min-width: 0; + padding-right: 1.75rem; + border-radius: 0.5rem 0 0 0.5rem; + cursor: pointer; + } + .upstream-data-date__clear { + position: absolute; + top: 0; + right: 2.25rem; + bottom: 0; + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + margin: auto 0; + padding: 0; + border: 0; + border-radius: 0.25rem; + background: transparent; + color: var(--vip-content-muted, #595959); + cursor: pointer; + } + .upstream-data-date__clear:hover { + background: rgba(133, 102, 50, 0.12); + color: var(--vip-content-accent-dark, #856632); + } + .upstream-data-date__clear[hidden] { display: none; } + .upstream-data-date__clear .material-icons-outlined { font-size: 0.9375rem; } + .upstream-data-date__pick { + display: inline-flex; + flex: 0 0 2.125rem; + align-items: center; + justify-content: center; + width: 2.125rem; + height: 2.125rem; + padding: 0; + border: 1px solid var(--vip-border, #d8d2c7); + border-left: 0; + border-radius: 0 0.5rem 0.5rem 0; + background: var(--vip-surface, #ffffff); + color: var(--vip-content-accent-dark, #856632); + cursor: pointer; + } + .upstream-data-date__pick:hover, + .upstream-data-date__pick:focus-visible { + background: #f7f0de; + color: #6a5129; + outline: none; + } + .upstream-data-date__pick .material-icons-outlined { font-size: 1.125rem; } + .upstream-data-date-range__sep { + color: var(--vip-content-muted, #595959); + font-weight: 400; + } + .upstream-data-calendar { + position: absolute; + top: calc(100% + 0.35rem); + left: 0; + z-index: 30; + width: 16.5rem; + padding: 0.5rem; + border: 1px solid var(--vip-border, #d8d2c7); + border-radius: 0.5rem; + background: var(--vip-surface, #ffffff); + box-shadow: 0 4px 8px rgba(23, 23, 23, 0.1); + } + .upstream-data-calendar[hidden] { display: none; } + .upstream-data-calendar__head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.25rem; + margin-bottom: 0.25rem; + } + .upstream-data-calendar__title { + color: var(--vip-content-ink, #171717); + font-size: 0.8125rem; + font-weight: 600; + } + .upstream-data-calendar__nav { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.5rem; + height: 1.5rem; + padding: 0; + border: 0; + border-radius: 0.25rem; + background: transparent; + color: var(--vip-content-accent-dark, #856632); + cursor: pointer; + } + .upstream-data-calendar__nav:hover { background: #f7f0de; } + .upstream-data-calendar__nav .material-icons-outlined { font-size: 1.125rem; } + .upstream-data-calendar__week, + .upstream-data-calendar__grid { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: 0.125rem; + } + .upstream-data-calendar__week span { + padding: 0.25rem 0; + color: var(--vip-content-muted, #595959); + font-size: 0.75rem; + text-align: center; + } + .upstream-data-calendar__day { + height: 1.75rem; + padding: 0; + border: 1px solid transparent; + border-radius: 0.25rem; + background: transparent; + color: var(--vip-content-ink, #171717); + cursor: pointer; + font: inherit; + font-size: 0.75rem; + font-variant-numeric: tabular-nums; + } + .upstream-data-calendar__day:hover { background: #f7f0de; } + .upstream-data-calendar__day.is-outside { color: #b9b3a8; } + .upstream-data-calendar__day.is-today { border-color: var(--vip-content-accent-dark, #856632); } + .upstream-data-calendar__day.is-selected { + border-color: var(--vip-content-accent-dark, #856632); + background: var(--vip-content-accent-dark, #856632); + color: #ffffff; + } + /* 结束日期的日历向右展开会顶出工具栏,改成向左 */ + .upstream-data-date:last-of-type .upstream-data-calendar { + right: 0; + left: auto; + }
@@ -124,13 +373,49 @@ action="{{ route('business.pages.show', ['section' => 'financial-operations', 'page' => 'small-payment-work-orders']) }}" data-filter-contract="small-payment-work-orders.filters" > -
- - -
-
- - +
+ 完成日期 +
+ + + + + + + 至 + + + + + + +
@@ -138,12 +423,49 @@
- - - @foreach ($smallPaymentShops as $shopName) - +
+ + + +
@@ -164,12 +486,49 @@
- - - @foreach ($smallPaymentReasons as $reasonOption) - +
+ + + +
+ + diff --git a/tests/Feature/Business/SmallPaymentWorkOrderPageTest.php b/tests/Feature/Business/SmallPaymentWorkOrderPageTest.php index 51645dfe..93c15bda 100644 --- a/tests/Feature/Business/SmallPaymentWorkOrderPageTest.php +++ b/tests/Feature/Business/SmallPaymentWorkOrderPageTest.php @@ -84,10 +84,11 @@ class SmallPaymentWorkOrderPageTest extends FeatureTestCase ->assertSee('data-filter-contract="small-payment-work-orders.filters"', false) ->assertSee('data-sync-state="syncing"', false) ->assertSeeText('业务镜像同步中,当前显示最近成功数据') - ->assertSeeText('完成开始时间') - ->assertSeeText('完成结束时间') - ->assertSee('value="2026-09-01T08:30"', false) - ->assertSee('value="2026-09-03T17:45"', false) + ->assertSeeText('完成日期') + ->assertSee('value="2026-09-01"', false) + ->assertSee('value="2026-09-03"', false) + ->assertSee('data-date-picker', false) + ->assertSee('data-date-toggle', false) ->assertSee('aria-label="聚水潭小额打款工单"', false) ->assertSeeText('GD-001') ->assertSeeText('支付宝已打款') @@ -138,6 +139,55 @@ class SmallPaymentWorkOrderPageTest extends FeatureTestCase $this->assertStringContainsString('50.00', $content); } + /** + * 筛选下拉必须是自绘的可滚动面板:原生 的面板由浏览器绘制, + * 高度与滚动条无法控制,店铺有 200+ 个选项时看不全也滑不动。 + */ + public function test_filter_dropdowns_are_scrollable_option_panels(): void + { + $this->grantAdminPermission(); + setting()->set('default.list_limit', 25); + $states = [ + SmallPaymentWorkOrderMirrorRepository::WORK_ORDER_DATASET => [ + 'status' => 'succeeded', 'initial_completed_at' => '2026-09-01 00:00:00', + ], + SmallPaymentWorkOrderMirrorRepository::PAYMENT_MAP_DATASET => [ + 'status' => 'succeeded', 'initial_completed_at' => '2026-09-01 00:00:00', + ], + ]; + $repository = \Mockery::mock(SmallPaymentWorkOrderMirrorRepository::class); + $repository->shouldReceive('available')->once()->andReturnTrue(); + $repository->shouldReceive('states')->once()->andReturn($states); + $repository->shouldReceive('syncStatus')->once()->with($states)->andReturn([ + 'code' => 'healthy', 'last_succeeded_at' => '2026-09-03 12:00:00', + ]); + $repository->shouldReceive('ready')->once()->andReturnTrue(); + $repository->shouldReceive('queryReady')->once()->andReturnTrue(); + $repository->shouldReceive('paginate')->once()->andReturn([ + 'data' => [], 'total' => 0, + 'page_payment_amount' => '0.00', 'filtered_payment_amount' => '0.00', + ]); + $repository->shouldReceive('filterOptions')->once()->andReturn([ + 'shops' => ['京东-HX旗舰店101', '京东-HX旗舰店102'], + 'payment_reasons' => ['买家原因/买家-不想要'], + ]); + $this->app->instance(SmallPaymentWorkOrderMirrorRepository::class, $repository); + + $this->loginAs()->get(route('business.pages.show', [ + 'section' => 'financial-operations', 'page' => 'small-payment-work-orders', + ])) + ->assertOk() + ->assertSee('data-filter-combobox', false) + ->assertSee('class="upstream-data-combobox__menu"', false) + // 唯一能锁住"可滚动"的常量:面板有最大高度且内部滚动 + ->assertSee('max-height: min(18rem, 50vh)', false) + ->assertSee('data-combobox-option', false) + ->assertSee('data-value="京东-HX旗舰店101"', false) + ->assertSee('data-combobox-empty', false) + ->assertDontSee('list="small-payment', false) + ->assertDontSee('