整理設定隊伍介面並更新 README

This commit is contained in:
2026-04-16 10:19:23 +08:00
parent a1e0e0f16e
commit 31168e830b
3 changed files with 404 additions and 205 deletions

View File

@@ -103,6 +103,12 @@ export function ScoreboardPage({
return players
}, [selectedGroup])
const presetTeams = useMemo(
() =>
selectedGroup?.teams.filter((team) => !team.isPlaceholderA && !team.isPlaceholderB) ?? [],
[selectedGroup],
)
const canArrangeMatch = !hasRecordedPoint
const canScore = scoreState.serving !== null
@@ -198,6 +204,26 @@ export function ScoreboardPage({
})
}
const togglePresetTeam = (team: GroupTeam) => {
setDraftPlayers((current) => {
const removed = removePresetTeamFromDraft(current, team)
if (removed.length !== current.length) {
return removed
}
if (current.length >= 4 || current.length % 2 !== 0) {
return current
}
if (current.includes(team.playerA) || current.includes(team.playerB)) {
return current
}
return [...current, team.playerA, team.playerB]
})
}
const confirmDraftTeams = () => {
if (draftPlayers.length !== 4) {
return
@@ -305,10 +331,10 @@ export function ScoreboardPage({
{pickerOpen ? (
<TeamPickerModal
currentSelectionOrder={currentSelectionOrder}
draftPlayers={draftPlayers}
draftTargetScore={draftTargetScore}
group={selectedGroup}
presetTeams={presetTeams}
selectablePlayers={selectablePlayers}
selectionCount={draftPlayers.length}
sourceLabel={groupSource === 'db' ? 'DB' : groupSource === 'manual' ? '手動' : '-'}
@@ -319,6 +345,7 @@ export function ScoreboardPage({
onConfirm={confirmDraftTeams}
onDraftTargetScoreChange={setDraftTargetScore}
onTogglePlayer={toggleDraftPlayer}
onTogglePresetTeam={togglePresetTeam}
/>
) : null}
@@ -473,10 +500,10 @@ function ScoreboardTeamPanel({
}
type TeamPickerModalProps = {
currentSelectionOrder: string[]
draftPlayers: string[]
draftTargetScore: string
group: RoundGroup
presetTeams: GroupTeam[]
selectablePlayers: string[]
selectionCount: number
sourceLabel: string
@@ -487,13 +514,14 @@ type TeamPickerModalProps = {
onConfirm: () => void
onDraftTargetScoreChange: (value: string) => void
onTogglePlayer: (playerName: string) => void
onTogglePresetTeam: (team: GroupTeam) => void
}
function TeamPickerModal({
currentSelectionOrder,
draftPlayers,
draftTargetScore,
group,
presetTeams,
selectablePlayers,
selectionCount,
sourceLabel,
@@ -504,20 +532,8 @@ function TeamPickerModal({
onConfirm,
onDraftTargetScoreChange,
onTogglePlayer,
onTogglePresetTeam,
}: TeamPickerModalProps) {
const draftTeams = [
draftPlayers.length >= 2 ? `${draftPlayers[0]} / ${draftPlayers[1]}` : '尚未選滿 2 位',
draftPlayers.length >= 4 ? `${draftPlayers[2]} / ${draftPlayers[3]}` : '尚未選滿 2 位',
]
const currentTeams = [
currentSelectionOrder.length >= 2
? `${currentSelectionOrder[0]} / ${currentSelectionOrder[1]}`
: null,
currentSelectionOrder.length >= 4
? `${currentSelectionOrder[2]} / ${currentSelectionOrder[3]}`
: null,
]
return (
<div className="team-picker-overlay" role="presentation" onClick={onClose}>
<div
@@ -540,24 +556,26 @@ function TeamPickerModal({
<div className="team-picker-title">
<span className="team-picker-count">{selectionCount}/4</span>
<div>
<strong></strong>
<strong></strong>
<p>
{group.id} / {sourceLabel} / {targetDate || '-'}
</p>
</div>
</div>
<label className="team-picker-config">
<span></span>
<input
className="team-picker-score-input"
inputMode="numeric"
maxLength={2}
type="text"
value={draftTargetScore}
onChange={(event) => onDraftTargetScoreChange(event.target.value)}
/>
</label>
<div className="team-picker-config-row">
<label className="team-picker-config team-picker-config-compact">
<span></span>
<input
className="team-picker-score-input team-picker-score-input-compact"
inputMode="numeric"
maxLength={2}
type="text"
value={draftTargetScore}
onChange={(event) => onDraftTargetScoreChange(event.target.value)}
/>
</label>
</div>
<div className="team-picker-list">
{selectablePlayers.map((playerName) => {
@@ -605,24 +623,40 @@ function TeamPickerModal({
</section>
<aside className="team-picker-panel team-picker-side-panel">
<div className="picked-team-list">
{[0, 1].map((slotIndex) => {
const teamLabel = draftTeams[slotIndex]
const isCurrent = currentTeams[slotIndex] === teamLabel
<div className="preset-team-block">
<div className="preset-team-head">
<strong></strong>
<small></small>
</div>
return (
<div className="picked-team-card" key={`picked-${slotIndex}`}>
<span className="picked-team-index">{slotIndex + 1}</span>
<div>
<strong>{teamLabel}</strong>
<small>
{slotIndex === 0 ? '第 1、2 位自動成隊' : '第 3、4 位自動成隊'}
{isCurrent ? ' / 目前場上配置' : ''}
</small>
</div>
</div>
)
})}
<div className="preset-team-list">
{presetTeams.map((team) => {
const selectedSlot = getPresetTeamSelectionSlot(draftPlayers, team)
return (
<button
className={
selectedSlot === null
? 'preset-team-card'
: 'preset-team-card preset-team-card-active'
}
key={`preset-team-${team.id}`}
type="button"
onClick={() => onTogglePresetTeam(team)}
>
<span className="preset-team-index">{team.id}</span>
<div>
<strong>{getTeamDisplayName(team)}</strong>
<small>
{selectedSlot === null
? '點擊帶入這組預設隊伍'
: `已帶入第 ${selectedSlot === 0 ? '1' : '2'}`}
</small>
</div>
</button>
)
})}
</div>
</div>
<button className="team-picker-clear" type="button" onClick={onClear}>
@@ -737,6 +771,33 @@ function sanitizeTargetScore(value: string) {
return Math.min(99, Math.max(1, numeric))
}
function removePresetTeamFromDraft(players: string[], team: GroupTeam) {
const firstPairSelected = players[0] === team.playerA && players[1] === team.playerB
const secondPairSelected = players[2] === team.playerA && players[3] === team.playerB
if (firstPairSelected) {
return players.slice(2)
}
if (secondPairSelected) {
return players.slice(0, 2)
}
return players
}
function getPresetTeamSelectionSlot(players: string[], team: GroupTeam) {
if (players[0] === team.playerA && players[1] === team.playerB) {
return 0
}
if (players[2] === team.playerA && players[3] === team.playerB) {
return 1
}
return null
}
function formatClock() {
return new Date().toLocaleTimeString('zh-TW', {
hour: '2-digit',