优化排序控件

This commit is contained in:
yml2213
2026-07-13 23:44:46 +08:00
parent e73e766b56
commit 35b0278524
2 changed files with 234 additions and 83 deletions
+41 -12
View File
@@ -268,11 +268,16 @@ function Showcase() {
}
function selectRange(minKey: "minCoin" | "minPrice", maxKey: "maxCoin" | "maxPrice", min: string, max: string) {
setFilters((current) => ({ ...current, [minKey]: min, [maxKey]: max }));
setFilters((current) => {
if (current[minKey] === min && current[maxKey] === max) {
return { ...current, [minKey]: "", [maxKey]: "" };
}
return { ...current, [minKey]: min, [maxKey]: max };
});
}
function setSort(sortKey: string) {
setActiveSort((current) => (current === sortKey && sortKey !== "recommended" ? "recommended" : sortKey));
setActiveSort(sortKey);
setPage(1);
}
@@ -351,7 +356,10 @@ function Showcase() {
) : null}
<section className="filter-board">
<div className="section-title"></div>
<div className="section-title">
<i />
</div>
<div className="filter-grid">
<FilterRow
active={(item) => filters.minCoin === item.min && filters.maxCoin === item.max}
@@ -430,33 +438,39 @@ function Showcase() {
<div className="range-fields">
<label>
<span></span>
<div className="range-inputs">
<Input
inputMode="numeric"
placeholder="最小"
value={filters.minCoin}
onChange={(event) => updateFilter("minCoin", event.target.value)}
/>
<em></em>
<Input
inputMode="numeric"
placeholder="最大"
value={filters.maxCoin}
onChange={(event) => updateFilter("maxCoin", event.target.value)}
/>
</div>
</label>
<label>
<span>KD范围</span>
<div className="range-inputs">
<Input
inputMode="decimal"
placeholder="最小"
value={filters.minKd}
onChange={(event) => updateFilter("minKd", event.target.value)}
/>
<em></em>
<Input
inputMode="decimal"
placeholder="最大"
value={filters.maxKd}
onChange={(event) => updateFilter("maxKd", event.target.value)}
/>
</div>
</label>
</div>
{skinSections.map((section) => (
@@ -472,19 +486,27 @@ function Showcase() {
</div>
<div className="filter-footer">
<Button type="link" onClick={() => setAdvancedOpen((open) => !open)}>
{advancedOpen ? "收起高级筛选" : "展开高级筛选"} <DownOutlined rotate={advancedOpen ? 180 : 0} />
</Button>
<button className="advanced-toggle" type="button" onClick={() => setAdvancedOpen((open) => !open)}>
{advancedOpen ? "收起高级筛选" : "展开高级筛选"}
<DownOutlined rotate={advancedOpen ? 180 : 0} />
</button>
</div>
</section>
<section className="result-area">
<div className="sort-toolbar">
<div className="sort-buttons">
<div className="sort-segment" role="tablist" aria-label="排序方式">
{sortOptions.map((option) => (
<Button key={option.key} className={activeSort === option.key ? "active" : ""} onClick={() => setSort(option.key)}>
{option.label} <DownOutlined />
</Button>
<button
key={option.key}
type="button"
role="tab"
aria-selected={activeSort === option.key}
className={activeSort === option.key ? "active" : ""}
onClick={() => setSort(option.key)}
>
{option.label}
</button>
))}
</div>
<div className="toolbar-actions">
@@ -560,12 +582,19 @@ interface FilterRowProps<T> {
function FilterRow<T>({ active, label, onSelect, options, render }: FilterRowProps<T>) {
return (
<div className="filter-row">
<span>{label}</span>
<span className="filter-label">{label}</span>
<div className="chip-list">
{options.map((item) => {
const text = render(item);
const selected = active(item);
return (
<button key={text} className={active(item) ? "selected" : ""} type="button" onClick={() => onSelect(item)}>
<button
key={text}
className={selected ? "selected" : ""}
type="button"
aria-pressed={selected}
onClick={() => onSelect(item)}
>
{text}
</button>
);
+169 -47
View File
@@ -271,38 +271,49 @@ input {
}
.filter-board {
padding: 18px 22px 10px;
padding: 18px 22px 12px;
}
.section-title {
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 14px;
color: var(--text-main);
font-size: 14px;
font-size: 15px;
font-weight: 900;
letter-spacing: 0.01em;
}
.section-title i {
width: 3px;
height: 14px;
border-radius: 99px;
background: linear-gradient(180deg, #ff8b2f, var(--brand));
}
.filter-grid {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(360px, 0.74fr);
gap: 12px 64px;
gap: 14px 56px;
}
.filter-row {
min-width: 0;
display: grid;
grid-template-columns: 76px minmax(0, 1fr);
grid-template-columns: 72px minmax(0, 1fr);
align-items: start;
gap: 10px;
gap: 12px;
}
.filter-row > span,
.range-fields span {
min-height: 26px;
.filter-label,
.range-fields > label > span {
min-height: 30px;
display: inline-flex;
align-items: center;
color: #344054;
color: #667085;
font-size: 13px;
font-weight: 900;
font-weight: 700;
white-space: nowrap;
}
@@ -310,37 +321,72 @@ input {
min-width: 0;
display: flex;
flex-wrap: wrap;
gap: 7px;
gap: 8px;
}
.chip-list button {
min-width: 42px;
height: 26px;
padding: 0 10px;
border: 1px solid #e6ebf2;
border-radius: 4px;
background: #f7f9fc;
color: #647179;
min-width: 44px;
height: 30px;
padding: 0 12px;
border: 1px solid transparent;
border-radius: 999px;
background: #f4f6f9;
color: #5b6572;
font-size: 12px;
font-weight: 700;
line-height: 1;
cursor: pointer;
transition:
color 0.15s ease,
background 0.15s ease,
border-color 0.15s ease,
box-shadow 0.15s ease,
transform 0.15s ease;
}
.chip-list button:hover {
color: var(--brand-dark);
background: #fff4ea;
border-color: #ffd7b5;
}
.chip-list button.selected {
border-color: var(--brand);
color: var(--brand-dark);
background: var(--brand-soft);
color: #fff;
background: linear-gradient(135deg, #ff8b2f, var(--brand));
border-color: transparent;
box-shadow: 0 6px 14px rgba(255, 107, 0, 0.22);
}
.chip-list button.selected:hover {
color: #fff;
background: linear-gradient(135deg, #ff9a45, var(--brand-dark));
}
.chip-list button:active {
transform: translateY(1px);
}
.compact-select .ant-select {
width: min(270px, 100%);
width: min(280px, 100%);
}
.compact-select .ant-select-selector {
min-height: 34px !important;
border-radius: 10px !important;
border-color: #e6ebf2 !important;
background: #f8fafc !important;
}
.compact-select .ant-select-focused .ant-select-selector {
border-color: var(--brand) !important;
box-shadow: 0 0 0 2px rgba(255, 107, 0, 0.12) !important;
}
.advanced-panel {
display: none;
padding-top: 16px;
border-top: 1px solid #eef2f3;
margin-top: 14px;
border-top: 1px dashed #e8edf3;
margin-top: 16px;
}
.advanced-panel.open {
@@ -354,52 +400,114 @@ input {
gap: 16px 38px;
}
.range-fields label {
.range-fields > label {
display: grid;
grid-template-columns: 76px minmax(0, 1fr) minmax(0, 1fr);
grid-template-columns: 72px minmax(0, 1fr);
align-items: center;
gap: 10px;
gap: 12px;
}
.range-inputs {
min-width: 0;
display: grid;
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
align-items: center;
gap: 8px;
}
.range-inputs em {
color: #98a2b3;
font-size: 12px;
font-style: normal;
font-weight: 700;
}
.range-inputs .ant-input {
border-radius: 10px;
background: #f8fafc;
}
.filter-footer {
justify-content: flex-end;
justify-content: center;
min-height: 44px;
padding-top: 4px;
}
.filter-footer .ant-btn {
color: #394a4f;
font-weight: 800;
.advanced-toggle {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 14px;
border: none;
border-radius: 999px;
background: transparent;
color: #667085;
font-size: 13px;
font-weight: 700;
cursor: pointer;
transition: color 0.15s ease, background 0.15s ease;
}
.advanced-toggle:hover {
color: var(--brand-dark);
background: var(--brand-soft);
}
.advanced-toggle .anticon {
font-size: 11px;
}
.result-area {
margin-top: 14px;
padding: 18px 16px 28px;
padding: 16px 16px 28px;
}
.sort-toolbar {
justify-content: space-between;
gap: 18px;
margin-bottom: 14px;
align-items: center;
gap: 14px;
margin-bottom: 16px;
padding-bottom: 14px;
border-bottom: 1px solid #eef2f6;
}
.sort-buttons {
.sort-segment {
min-width: 0;
display: flex;
display: inline-flex;
flex-wrap: wrap;
gap: 10px;
gap: 4px;
padding: 4px;
border-radius: 12px;
background: #f3f5f8;
border: 1px solid #e8edf3;
}
.sort-buttons .ant-btn {
color: #596872;
background: #f6f8fb;
border-color: #dfe7ec;
font-weight: 800;
.sort-segment button {
height: 32px;
padding: 0 12px;
border: none;
border-radius: 9px;
background: transparent;
color: #667085;
font-size: 13px;
font-weight: 700;
white-space: nowrap;
cursor: pointer;
transition:
color 0.15s ease,
background 0.15s ease,
box-shadow 0.15s ease;
}
.sort-buttons .ant-btn.active {
.sort-segment button:hover {
color: var(--text-main);
background: rgba(255, 255, 255, 0.7);
}
.sort-segment button.active {
color: var(--brand-dark);
background: var(--brand-soft);
border-color: var(--brand);
background: #fff;
box-shadow: 0 2px 8px rgba(21, 32, 43, 0.08);
}
.toolbar-actions {
@@ -408,8 +516,14 @@ input {
gap: 10px;
}
.toolbar-actions .ant-btn {
border-radius: 9px;
font-weight: 800;
}
.toolbar-actions .ant-btn-primary {
font-weight: 900;
box-shadow: 0 6px 14px rgba(255, 107, 0, 0.18);
}
.listing-list {
@@ -742,11 +856,19 @@ input {
}
.filter-row,
.range-fields label {
.range-fields > label {
grid-template-columns: 1fr;
gap: 7px;
}
.sort-segment {
width: 100%;
}
.sort-segment button {
flex: 1 1 auto;
}
.compact-select .ant-select {
width: 100%;
}