From 2f3852271aedcca1147aa0def78b36f9cb4d098f Mon Sep 17 00:00:00 2001 From: qiuxiang <394405259@qq.com> Date: Mon, 26 Oct 2026 15:47:50 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E7=B2=BE=E7=AE=80=E5=AE=89?= =?UTF-8?q?=E8=A3=85=E6=98=8E=E7=BB=86=E7=AD=9B=E9=80=89=E5=8C=BA=E5=B9=B6?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=94=9F=E6=95=88=E6=9D=A1=E4=BB=B6=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=20(#602)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一安装明细时间口径与日期区间,收敛常驻筛选,增加快捷区间、可删除生效条件和低频筛选折叠。 --- .../InstallationDetailMirrorRepository.php | 29 +- .../partials/date-range-picker.blade.php | 1 + .../installation-fee-summary.blade.php | 4 +- .../partials/installation-filters.blade.php | 320 ++++++++++++++ resources/views/business/pages/show.blade.php | 406 ++++++++++++------ tests/Feature/Business/ManagementCrudTest.php | 92 +++- ...InstallationDetailMirrorRepositoryTest.php | 28 +- 7 files changed, 729 insertions(+), 151 deletions(-) create mode 100644 resources/views/business/pages/partials/installation-filters.blade.php diff --git a/modules/EcommerceFinance/Repositories/InstallationDetailMirrorRepository.php b/modules/EcommerceFinance/Repositories/InstallationDetailMirrorRepository.php index a33cb19a..5356e0ac 100644 --- a/modules/EcommerceFinance/Repositories/InstallationDetailMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/InstallationDetailMirrorRepository.php @@ -212,16 +212,39 @@ class InstallationDetailMirrorRepository public static function requestFilters(): array { $rules = []; + $rules['time_scope'] = ['nullable', 'in:created,customer_review,finance_review']; + $rules['date_from'] = ['nullable', 'date_format:Y-m-d']; + $rules['date_to'] = ['nullable', 'date_format:Y-m-d']; + if (request()->filled('date_from')) $rules['date_to'][] = 'after_or_equal:date_from'; foreach (['created', 'customer_review', 'finance_review'] as $prefix) { $rules[$prefix . '_from'] = ['nullable', 'date_format:Y-m-d']; $rules[$prefix . '_to'] = ['nullable', 'date_format:Y-m-d']; if (request()->filled($prefix . '_from')) $rules[$prefix . '_to'][] = 'after_or_equal:' . $prefix . '_from'; } - foreach (['push_status','order_no','customer_review','finance_review','quote_status','arranged_status','fee_payer','completed_status','shop_name','delete_status','installation_company','installation_no','order_type','product_id'] as $key) { + foreach (['push_status','order_no','customer_review','finance_review','quote_status','arranged_status','fee_payer','completed_status','shop_name','shop_code','delete_status','installation_company','installation_no','order_type','product_id'] as $key) { $rules[$key] = ['nullable', 'string', 'max:191']; } foreach (['customer_review', 'finance_review', 'arranged_status', 'completed_status', 'delete_status'] as $key) $rules[$key][] = 'in:0,1'; - return validator(request()->only(array_keys($rules)), $rules)->validate(); + $filters = validator(request()->only(array_keys($rules)), $rules)->validate(); + if (! array_key_exists('time_scope', $filters) + && ! array_key_exists('date_from', $filters) + && ! array_key_exists('date_to', $filters)) { + return $filters; + } + + $scope = (string) ($filters['time_scope'] ?? 'customer_review'); + $prefix = match ($scope) { + 'created' => 'created', + 'finance_review' => 'finance_review', + default => 'customer_review', + }; + foreach (['created', 'customer_review', 'finance_review'] as $legacyPrefix) { + unset($filters[$legacyPrefix . '_from'], $filters[$legacyPrefix . '_to']); + } + $filters[$prefix . '_from'] = $filters['date_from'] ?? null; + $filters[$prefix . '_to'] = $filters['date_to'] ?? null; + + return $filters; } private function applyFilters($query, array $filters, string $itemTable): void @@ -230,7 +253,7 @@ class InstallationDetailMirrorRepository if (!empty($filters[$prefix.'_from'])) $query->where('v.'.$column, '>=', $filters[$prefix.'_from']); if (!empty($filters[$prefix.'_to'])) $query->where('v.'.$column, '<', \Illuminate\Support\Carbon::parse($filters[$prefix.'_to'])->addDay()->toDateString()); } - foreach (['push_status'=>'apistatus','order_no'=>'OrderNum','quote_status'=>'BidStatus','fee_payer'=>'costtaker','shop_name'=>'shop_name','installation_company'=>'installCompanyName','installation_no'=>'ordernum_id','order_type'=>'TypeDto'] as $key=>$column) { + foreach (['push_status'=>'apistatus','order_no'=>'OrderNum','quote_status'=>'BidStatus','fee_payer'=>'costtaker','shop_name'=>'shop_name','shop_code'=>'shopcode','installation_company'=>'installCompanyName','installation_no'=>'ordernum_id','order_type'=>'TypeDto'] as $key=>$column) { if (isset($filters[$key]) && $filters[$key] !== '') $query->where('v.'.$column, $filters[$key]); } foreach (['customer_review'=>'kfsh','finance_review'=>'cwsh','completed_status'=>'installComplete','delete_status'=>'Delete'] as $key=>$column) { diff --git a/resources/views/business/pages/partials/date-range-picker.blade.php b/resources/views/business/pages/partials/date-range-picker.blade.php index 0de248c4..8dd024b2 100644 --- a/resources/views/business/pages/partials/date-range-picker.blade.php +++ b/resources/views/business/pages/partials/date-range-picker.blade.php @@ -286,6 +286,7 @@ }); input.addEventListener('click', open); + input.addEventListener('input', syncClear); input.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ' || event.key === 'ArrowDown') { event.preventDefault(); diff --git a/resources/views/business/pages/partials/installation-fee-summary.blade.php b/resources/views/business/pages/partials/installation-fee-summary.blade.php index 583b3afe..65440a1a 100644 --- a/resources/views/business/pages/partials/installation-fee-summary.blade.php +++ b/resources/views/business/pages/partials/installation-fee-summary.blade.php @@ -381,8 +381,8 @@ var dialog = document.getElementById('installation-fee-dialog'); if (!form || !card || !dialog) { return; } - var fromInput = form.elements.namedItem('customer_review_from'); - var toInput = form.elements.namedItem('customer_review_to'); + var fromInput = form.elements.namedItem('date_from'); + var toInput = form.elements.namedItem('date_to'); var cardTotal = card.querySelector('[data-installation-fee-total]'); var status = document.querySelector('[data-installation-fee-status]'); var subtitle = dialog.querySelector('[data-installation-fee-subtitle]'); diff --git a/resources/views/business/pages/partials/installation-filters.blade.php b/resources/views/business/pages/partials/installation-filters.blade.php new file mode 100644 index 00000000..8263978d --- /dev/null +++ b/resources/views/business/pages/partials/installation-filters.blade.php @@ -0,0 +1,320 @@ +@php + $installationTimeScopes = [ + 'created' => '创建时间', + 'customer_review' => '客服审核时间', + 'finance_review' => '财务审核时间', + ]; + $installationTimePrefixes = [ + 'created' => 'created', + 'customer_review' => 'customer_review', + 'finance_review' => 'finance_review', + ]; + $usesUnifiedTime = request()->hasAny(['time_scope', 'date_from', 'date_to']); + $installationTimeScope = (string) request('time_scope', ''); + if (! array_key_exists($installationTimeScope, $installationTimeScopes)) { + $installationTimeScope = request()->filled('created_from') || request()->filled('created_to') + ? 'created' + : (request()->filled('finance_review_from') || request()->filled('finance_review_to') + ? 'finance_review' + : 'customer_review'); + } + $installationTimePrefix = $installationTimePrefixes[$installationTimeScope]; + $installationDateFrom = $usesUnifiedTime + ? (string) request('date_from', '') + : (string) request($installationTimePrefix . '_from', ''); + $installationDateTo = $usesUnifiedTime + ? (string) request('date_to', '') + : (string) request($installationTimePrefix . '_to', ''); + $installationStore = (string) request('shop_name', ''); + $installationCompleted = (string) request('completed_status', ''); + $installationMoreFields = [ + 'shop_code' => ['label' => '店铺 ID', 'placeholder' => '按店铺编码精确查询'], + 'order_no' => ['label' => '订单编号', 'placeholder' => '按原值精确查询'], + 'installation_no' => ['label' => '安装单号', 'placeholder' => '按原值精确查询'], + 'installation_company' => ['label' => '安装公司', 'placeholder' => '按原值精确查询'], + 'push_status' => ['label' => '推送状态', 'placeholder' => '按原值精确查询'], + 'quote_status' => ['label' => '报价状态', 'placeholder' => '按原值精确查询'], + 'fee_payer' => ['label' => '安装费承担方', 'placeholder' => '按原值精确查询'], + 'order_type' => ['label' => '订单类型', 'placeholder' => '按原值精确查询'], + 'product_id' => ['label' => '商品 ID', 'placeholder' => '按原值精确查询'], + 'customer_review' => ['label' => '客服审核', 'options' => ['0' => '未审核', '1' => '已审核']], + 'finance_review' => ['label' => '财务审核', 'options' => ['0' => '未审核', '1' => '已审核']], + 'arranged_status' => ['label' => '是否安排', 'options' => ['0' => '未安排', '1' => '已安排']], + 'delete_status' => ['label' => '删除状态', 'options' => ['0' => '未删除', '1' => '已删除']], + ]; + $installationActiveMore = collect($installationMoreFields) + ->filter(static fn (array $field, string $name): bool => request()->filled($name)) + ->map(static fn (array $field, string $name): array => $field + [ + 'name' => $name, + 'value' => (string) request($name, ''), + ]) + ->all(); + $installationMoreCount = count($installationActiveMore); + $installationQuery = request()->query(); + $installationRemoveUrl = static function (array $keys) use ($installationQuery): string { + unset($installationQuery['page']); + foreach ($keys as $key) { + unset($installationQuery[$key]); + } + + $query = \Illuminate\Support\Arr::query($installationQuery); + + return request()->url() . ($query !== '' ? '?' . $query : ''); + }; + $installationDateChip = trim($installationDateFrom . ' 至 ' . $installationDateTo, ' 至'); +@endphp + +
+ + diff --git a/resources/views/business/pages/show.blade.php b/resources/views/business/pages/show.blade.php index 2d13204f..561471eb 100644 --- a/resources/views/business/pages/show.blade.php +++ b/resources/views/business/pages/show.blade.php @@ -203,12 +203,10 @@ } .installation-filter-bar { - display: grid; - grid-template-columns: repeat(4, minmax(12rem, 1fr)); - gap: 0.65rem 1rem; - padding: 1rem 1.5rem; - border-bottom: 1px solid #f3f4f6; - background: #fcfbf8; + padding: 0.7rem 1.25rem 0.75rem; + border-bottom: 1px solid #e8e2d8; + background: #fffdf8; + color: #595959; } .installation-filter-field { @@ -238,34 +236,207 @@ font-size: 0.75rem; } - .installation-date-filter { - display: flex; - grid-column: span 2; + .installation-filter-compact { + display: block; + } + + .installation-filter-primary { + display: grid; + grid-template-columns: + minmax(28rem, 1.55fr) + minmax(10.5rem, 0.62fr) + minmax(8.5rem, 0.42fr) + auto; + gap: 0.6rem; align-items: center; - gap: 0.4rem; + } + + .installation-filter-time { + display: grid; + grid-template-columns: minmax(7rem, 0.45fr) minmax(17rem, 1.4fr); + gap: 0.55rem; min-width: 0; + } + + .installation-filter-control { + display: flex; + min-width: 0; + flex-direction: column; + gap: 0.22rem; color: #595959; font-size: 0.75rem; + } + + .installation-filter-control > span { + height: 0.95rem; + overflow: hidden; + color: #595959; + font-size: 0.6875rem; + font-weight: 500; + line-height: 0.95rem; + text-overflow: ellipsis; white-space: nowrap; } - .installation-date-filter__label { - flex: 0 0 auto; - } - - .installation-date-filter__range { - flex: 1 1 auto; + .installation-filter-control input, + .installation-filter-control select { + width: 100%; min-width: 0; - } - - .installation-date-filter .upstream-data-date input { - height: 2.1rem; + height: 2rem; + padding: 0 0.55rem; + border: 1px solid #d8d2c7; + border-radius: 0.45rem; + background: #ffffff; + color: #171717; + font: inherit; font-size: 0.75rem; } + .installation-filter-control input:focus, + .installation-filter-control select:focus { + border-color: #856632; + box-shadow: 0 0 0 3px rgba(133, 102, 50, 0.16); + outline: none; + } + + .installation-filter-range .upstream-data-date-range { + gap: 0.35rem; + } + + .installation-filter-range .upstream-data-date input { + height: 2rem; + border: 1px solid #d8d2c7; + font-size: 0.75rem; + } + + .installation-filter-range .upstream-data-date__clear, + .installation-filter-range .upstream-data-date__pick { + height: 2rem; + } + + .installation-filter-actions { + display: flex; + align-items: flex-end; + justify-content: flex-end; + gap: 0.35rem; + white-space: nowrap; + } + + .installation-filter-actions .biz-btn { + height: 2rem; + gap: 0.25rem; + padding: 0 0.62rem; + font-size: 0.75rem; + } + + .installation-filter-actions .material-icons-outlined { + font-size: 1rem; + } + + .installation-more-toggle b { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 1.05rem; + height: 1.05rem; + padding: 0 0.2rem; + border-radius: 999px; + background: #856632; + color: #ffffff; + font-size: 0.625rem; + font-weight: 600; + } + + .installation-more-toggle b[hidden] { + display: none; + } + + .installation-filter-context { + display: flex; + min-height: 1.65rem; + align-items: center; + gap: 0.55rem; + flex-wrap: wrap; + margin-top: 0.55rem; + } + + .installation-quick-ranges, + .installation-active-filters { + display: flex; + align-items: center; + gap: 0.3rem; + flex-wrap: wrap; + } + + .installation-quick-ranges > span { + color: #595959; + font-size: 0.6875rem; + } + + .installation-quick-range { + height: 1.55rem; + padding: 0 0.55rem; + border: 1px solid #ddd6ca; + border-radius: 999px; + background: #ffffff; + color: #595959; + cursor: pointer; + font: inherit; + font-size: 0.6875rem; + line-height: 1; + } + + .installation-quick-range:hover, + .installation-quick-range:focus-visible, + .installation-quick-range.is-active { + border-color: #b9954f; + background: #f7f0de; + color: #856632; + outline: none; + } + + .installation-active-filters { + min-width: 0; + margin-left: auto; + } + + .installation-filter-chip { + display: inline-flex; + max-width: 24rem; + min-height: 1.55rem; + align-items: center; + gap: 0.28rem; + padding: 0.08rem 0.35rem 0.08rem 0.55rem; + border: 1px solid #e2d6bb; + border-radius: 999px; + background: #f7f0de; + color: #6a5129; + font-size: 0.6875rem; + line-height: 1.2; + text-decoration: none; + } + + .installation-filter-chip:hover, + .installation-filter-chip:focus-visible { + border-color: #b9954f; + background: #f2e7c5; + outline: none; + } + + .installation-filter-chip strong { + overflow: hidden; + max-width: 15rem; + font-weight: 500; + text-overflow: ellipsis; + white-space: nowrap; + } + + .installation-filter-chip .material-icons-outlined { + font-size: 0.9rem; + } + .installation-date-range-error { - grid-column: 1 / -1; - margin: 0; + margin: 0.4rem 0 0; color: #b80000; font-size: 0.75rem; } @@ -274,22 +445,92 @@ display: none; } - .installation-filter-actions { - display: flex; - align-items: center; - gap: 0.5rem; + .installation-more-panel { + margin-top: 0.6rem; + padding: 0.65rem 0.75rem 0.75rem; + border: 1px solid #e7dfd1; + border-radius: 0.55rem; + background: #ffffff; } - @media (max-width: 1100px) { - .installation-filter-bar { grid-template-columns: repeat(2, minmax(14rem, 1fr)); } - .installation-date-filter { grid-column: 1 / -1; } + .installation-more-panel[hidden] { + display: none; } + + .installation-more-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + margin-bottom: 0.55rem; + } + + .installation-more-head strong { + color: #171717; + font-size: 0.75rem; + font-weight: 600; + } + + .installation-more-head span { + color: #595959; + font-size: 0.6875rem; + } + + .installation-more-grid { + display: grid; + grid-template-columns: repeat(4, minmax(9rem, 1fr)); + gap: 0.55rem 0.75rem; + } + + @media (max-width: 1280px) { + .installation-filter-primary { + grid-template-columns: minmax(27rem, 1fr) minmax(10rem, 0.55fr) minmax(8rem, 0.4fr); + } + .installation-filter-actions { + grid-column: 1 / -1; + justify-content: flex-start; + } + .installation-more-grid { + grid-template-columns: repeat(3, minmax(9rem, 1fr)); + } + } + + @media (max-width: 980px) { + .installation-filter-primary { + grid-template-columns: minmax(0, 1fr) minmax(11rem, 0.55fr); + } + .installation-filter-time { + grid-column: 1 / -1; + } + .installation-filter-actions { + grid-column: 1 / -1; + } + .installation-more-grid { + grid-template-columns: repeat(2, minmax(9rem, 1fr)); + } + } + @media (max-width: 650px) { - .installation-filter-bar { grid-template-columns: minmax(0, 1fr); padding: 1rem; } - .installation-date-filter { - flex-direction: column; - align-items: stretch; - gap: 0.25rem; + .installation-filter-bar { + padding: 0.75rem; + } + .installation-filter-primary, + .installation-filter-time { + grid-template-columns: minmax(0, 1fr); + } + .installation-filter-time, + .installation-filter-actions { + grid-column: auto; + } + .installation-filter-actions { + justify-content: flex-start; + flex-wrap: wrap; + } + .installation-active-filters { + margin-left: 0; + } + .installation-more-grid { + grid-template-columns: minmax(0, 1fr); } } @@ -773,102 +1014,7 @@{{ $directoryQueryPageDescriptions[$directoryPageKey] ?? '按当前字段查询、筛选并导出业务数据' }}
+