fix(frontend): 移动端发布页常玩地区改为固定宫格选择

This commit is contained in:
2026-11-13 13:07:02 +08:00
parent d4ee054ac5
commit a57b087aa9
2 changed files with 169 additions and 10 deletions
@@ -200,6 +200,84 @@ function ChoiceField(props: {
)
}
/**
* 移动端「常玩地区」选择器:固定宫格常驻页面,点选即切换,不渲染输入框。
* iOS 上输入型下拉会立刻唤起键盘,把候选列表顶到键盘下面,故移动端改用宫格。
*/
function MobileRegionPicker(props: {
value: string[]
options: string[]
onChange: (value: string[]) => void
}) {
const { value, options, onChange } = props
// 连续快速点选时 React 可能还未重渲染,用 ref 累积最新值,避免后一次点击覆盖前一次。
const valueRef = useRef(value)
valueRef.current = value
const selected = new Set(valueRef.current)
const customValues = valueRef.current.filter(region => !options.includes(region))
function toggle(region: string) {
const current = valueRef.current
const next = current.includes(region)
? current.filter(item => item !== region)
: [...current, region]
valueRef.current = next
onChange(next)
}
return (
<div className="seller-region-picker">
<div className="seller-region-picker__meta">
<span>已选 {value.length} 个地区</span>
{value.length > 0 ? (
<button
type="button"
className="seller-region-picker__clear"
onClick={() => {
valueRef.current = []
onChange([])
}}
>
清空
</button>
) : null}
</div>
<div className="seller-region-grid" role="group" aria-label="常玩地区">
{options.map(region => {
const active = selected.has(region)
return (
<button
key={region}
type="button"
aria-pressed={active}
className={`seller-region-chip${active ? ' is-active' : ''}`}
onClick={() => toggle(region)}
>
{region}
</button>
)
})}
</div>
{customValues.length > 0 ? (
<div className="seller-region-custom">
<span>自定义</span>
{customValues.map(region => (
<button
key={region}
type="button"
aria-pressed={true}
className="seller-region-chip seller-region-chip--custom"
onClick={() => toggle(region)}
>
{region} ×
</button>
))}
</div>
) : null}
</div>
)
}
const PUBLISH_DRAFT_KEY_PREFIX = 'jieyou:seller:publish-draft:v1'
interface StoredPublishDraft extends PublishDraft {
@@ -1276,17 +1354,25 @@ export function SellerListingFormPage({ mobile = false }: { mobile?: boolean } =
onChange={value => updateForm('next_season_pass', value)}
/>
) : null}
<label className="seller-field seller-field--wide">
<div className="seller-field seller-field--wide">
<span className="seller-field__label">常玩地区</span>
<Select
mode="tags"
value={form.common_regions}
placeholder="选择或输入常用登录地区"
options={options.region_options.map(item => ({ value: item, label: item }))}
onChange={value => updateForm('common_regions', value)}
tokenSeparators={[',', ',']}
/>
</label>
{mobile && options.region_options.length > 0 ? (
<MobileRegionPicker
value={form.common_regions}
options={options.region_options}
onChange={value => updateForm('common_regions', value)}
/>
) : (
<Select
mode="tags"
value={form.common_regions}
placeholder="选择或输入常用登录地区"
options={options.region_options.map(item => ({ value: item, label: item }))}
onChange={value => updateForm('common_regions', value)}
tokenSeparators={[',', ',']}
/>
)}
</div>
</div>
</Card>
+73
View File
@@ -181,3 +181,76 @@
.mobile-seller-host .seller-summary {
position: static;
}
/* 移动端:常玩地区固定宫格(避免输入框唤起键盘把下拉顶到键盘后面) */
.mobile-seller-host .seller-region-picker {
display: grid;
gap: 10px;
}
.mobile-seller-host .seller-region-picker__meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
color: var(--ink-muted);
font-size: 12px;
}
.mobile-seller-host .seller-region-picker__clear {
padding: 0;
border: 0;
background: none;
color: var(--brand-seal);
font: inherit;
cursor: pointer;
}
.mobile-seller-host .seller-region-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
.mobile-seller-host .seller-region-chip {
min-height: 38px;
padding: 6px 4px;
border: 1px solid var(--line);
border-radius: 8px;
background: #fff;
color: var(--ink-secondary);
font: inherit;
font-size: 13px;
line-height: 1.3;
cursor: pointer;
transition:
color 0.15s ease,
border-color 0.15s ease,
background 0.15s ease;
}
.mobile-seller-host .seller-region-chip.is-active {
border-color: var(--brand-ink);
background: var(--brand-ink);
color: #fff;
font-weight: 600;
}
.mobile-seller-host .seller-region-chip--custom {
min-height: 30px;
padding: 4px 10px;
border-style: dashed;
border-color: var(--brand-ink);
background: transparent;
color: var(--brand-ink);
font-size: 12px;
}
.mobile-seller-host .seller-region-custom {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 6px;
color: var(--ink-muted);
font-size: 12px;
}