优化排序控件

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