fix(frontend): 移动端发布页常玩地区改为固定宫格选择
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user