diff --git a/assets/styling/admin.css b/assets/styling/admin.css index c276f01..b2a6d1d 100644 --- a/assets/styling/admin.css +++ b/assets/styling/admin.css @@ -252,6 +252,56 @@ gap: 12px; } +.orders-date-trigger { + width: 100%; + display: inline-flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 0.55rem 0.9rem; + border-radius: calc(var(--radius) - 1px); + border: 1px solid hsl(var(--border)); + background-color: hsl(var(--background)); + color: hsl(var(--foreground)); + box-shadow: inset 0 1px 0 hsl(var(--background)); + transition: + border-color 0.2s ease, + box-shadow 0.2s ease, + background-color 0.2s ease; + font-size: 0.95rem; +} + +.orders-date-trigger:hover { + border-color: hsl(var(--ring)); + background-color: hsl(var(--muted)); +} + +.orders-date-trigger-text { + flex: 1; + text-align: left; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.orders-date-trigger-icon { + font-size: 0.95rem; + opacity: 0.7; +} + +.orders-date-popover { + padding: 12px; + min-width: 320px; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.orders-date-actions { + display: flex; + justify-content: flex-end; +} + .orders-empty { padding: 60px 20px; text-align: center; diff --git a/assets/styling/shadcn.css b/assets/styling/shadcn.css index 3cba27f..3a353bd 100644 --- a/assets/styling/shadcn.css +++ b/assets/styling/shadcn.css @@ -1376,7 +1376,8 @@ .ui-date-range-calendars { display: grid; gap: 1rem; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + min-width: 460px; + grid-template-columns: repeat(2, minmax(220px, 1fr)); } .ui-date-range-calendars > .ui-date-range-calendar { diff --git a/src/components/ui/popover.rs b/src/components/ui/popover.rs index 56b89d8..a68b7fe 100644 --- a/src/components/ui/popover.rs +++ b/src/components/ui/popover.rs @@ -13,20 +13,43 @@ pub fn Popover( class: "ui-popover-trigger", style: "position: relative; display: inline-flex; align-items: center;", onmousedown: move |_| open.set(!open()), - tabindex: 0, - onfocusout: move |_| open.set(false), {trigger} if open() { + div { + style: "position: fixed; inset: 0; z-index: 40; background: transparent; pointer-events: auto;", + onmousedown: { + let mut open_signal = open.clone(); + move |event| { + event.stop_propagation(); + open_signal.set(false); + } + }, + onwheel: { + let mut open_signal = open.clone(); + move |event| { + event.stop_propagation(); + open_signal.set(false); + } + }, + ontouchmove: { + let mut open_signal = open.clone(); + move |event| { + event.stop_propagation(); + open_signal.set(false); + } + }, + } div { class: "ui-popover", "data-placement": placement.clone(), style: match placement.as_str() { - "top" => "position: absolute; left: 50%; bottom: 100%; transform: translate(-50%, -0.75rem);", - "bottom" => "position: absolute; left: 50%; top: 100%; transform: translate(-50%, 0.75rem);", - "left" => "position: absolute; right: 100%; top: 50%; transform: translate(-0.75rem, -50%);", - "right" => "position: absolute; left: 100%; top: 50%; transform: translate(0.75rem, -50%);", - _ => "position: absolute; left: 50%; top: 100%; transform: translate(-50%, 0.75rem);" + "top" => "position: absolute; left: 50%; bottom: 100%; transform: translate(-50%, -0.75rem); z-index: 50;", + "bottom" => "position: absolute; left: 50%; top: 100%; transform: translate(-50%, 0.75rem); z-index: 50;", + "left" => "position: absolute; right: 100%; top: 50%; transform: translate(-0.75rem, -50%); z-index: 50;", + "right" => "position: absolute; left: 100%; top: 50%; transform: translate(0.75rem, -50%); z-index: 50;", + _ => "position: absolute; left: 50%; top: 100%; transform: translate(-50%, 0.75rem); z-index: 50;" }, + onmousedown: move |event| event.stop_propagation(), onclick: move |event| event.stop_propagation(), {content} } diff --git a/src/components/ui/select.rs b/src/components/ui/select.rs index 54a5a5b..2a9dbc7 100644 --- a/src/components/ui/select.rs +++ b/src/components/ui/select.rs @@ -44,10 +44,25 @@ pub fn Select( div { class: "ui-select", "data-disabled": disabled, - onfocusout: { - let mut signal = open.clone(); - move |_| signal.set(false) - }, + if open() { + div { + style: "position: fixed; inset: 0; z-index: 20; background: transparent;", + onmousedown: { + let mut open_signal = open.clone(); + move |event| { + event.stop_propagation(); + open_signal.set(false); + } + }, + ontouchstart: { + let mut open_signal = open.clone(); + move |event| { + event.stop_propagation(); + open_signal.set(false); + } + }, + } + } button { class: "ui-select-trigger", "data-open": if open() { "true" } else { "false" }, diff --git a/src/views/orders.rs b/src/views/orders.rs index 8a94bad..3cd542d 100644 --- a/src/views/orders.rs +++ b/src/views/orders.rs @@ -1,8 +1,8 @@ use crate::components::ui::{ Avatar, Badge, BadgeVariant, Button, ButtonSize, ButtonVariant, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, DateRange, DateRangePicker, - Input, Label, Pagination, Select, SelectOption, Slider, Switch, Table, TableBody, TableCell, - TableHead, TableHeader, TableRow, ToggleGroup, ToggleGroupItem, ToggleGroupMode, + Input, Label, Pagination, Popover, Select, SelectOption, Slider, Switch, Table, TableBody, + TableCell, TableHead, TableHeader, TableRow, ToggleGroup, ToggleGroupItem, ToggleGroupMode, }; use chrono::NaiveDate; use dioxus::prelude::*; @@ -551,6 +551,23 @@ pub fn Orders() -> Element { let min_total_value = min_total(); let flagged_only_value = flagged_only(); let date_range_selected = date_range(); + let date_range_label = date_range_selected + .map(|range| { + let start = range.start.format("%Y-%m-%d"); + let end = range.end.format("%Y-%m-%d"); + if range.start == range.end { + format!("{}", start) + } else { + format!("{} → {}", start, end) + } + }) + .unwrap_or_else(|| "选择日期范围".to_string()); + let date_range_helper = date_range_selected + .map(|range| { + let span = (range.end - range.start).num_days().abs() + 1; + format!("覆盖 {} 天,点击可修改", span) + }) + .unwrap_or_else(|| "未限制下单日期".to_string()); let pipeline_values = pipeline(); let pipeline_value = pipeline_values .first() @@ -822,13 +839,40 @@ pub fn Orders() -> Element { } div { class: "ui-stack", style: "gap: 0.5rem;", Label { "下单日期" } - DateRangePicker { - value: date_range.clone(), - on_change: { - let mut setter = date_range.clone(); - move |range: Option| setter.set(range) + Popover { + placement: "bottom".to_string(), + trigger: rsx! { + button { + class: "orders-date-trigger", + r#type: "button", + span { class: "orders-date-trigger-text", "{date_range_label}" } + span { class: "orders-date-trigger-icon", "📅" } + } + }, + content: rsx! { + div { class: "orders-date-popover", + DateRangePicker { + value: date_range.clone(), + on_change: { + let mut setter = date_range.clone(); + move |range: Option| setter.set(range) + }, + } + div { class: "orders-date-actions", + Button { + variant: ButtonVariant::Ghost, + size: ButtonSize::Sm, + on_click: { + let mut setter = date_range.clone(); + move |_| setter.set(None) + }, + "清除日期" + } + } + } }, } + span { class: "ui-field-helper", "{date_range_helper}" } } div { class: "ui-stack", style: "gap: 0.5rem;", Label { "订单金额 (¥)" }