精简看板顶部操作并移除看板配置区

This commit is contained in:
JSR
2026-03-20 19:07:39 +08:00
parent 30c91094f1
commit 90160d2c45
3 changed files with 84 additions and 35 deletions
+82 -35
View File
@@ -83,6 +83,11 @@ interface DragState {
containerSize: number
}
const getFirstLeafId = (node: LayoutNode): string => {
if (node.type === "leaf") return node.id
return getFirstLeafId(node.first)
}
const makeId = () =>
typeof crypto !== "undefined" && "randomUUID" in crypto
? crypto.randomUUID()
@@ -345,6 +350,9 @@ export const BoardManager: React.FC<BoardManagerProps> = ({ canManage }) => {
const [errorMap, setErrorMap] = useState<Record<string, string>>({})
const [dragState, setDragState] = useState<DragState | null>(null)
const [editingListId, setEditingListId] = useState<string | null>(null)
const [selectedLeafNodeId, setSelectedLeafNodeId] = useState<string | null>(null)
const [renameVisible, setRenameVisible] = useState(false)
const [renameValue, setRenameValue] = useState("")
const panelRefs = useRef<Record<string, HTMLDivElement | null>>({})
@@ -511,6 +519,15 @@ ORDER BY reward_points DESC, score DESC`,
if (!activeBoardId && boards.length > 0) setActiveBoardId(boards[0].id)
}, [activeBoardId, boards])
useEffect(() => {
if (!activeBoard) {
setSelectedLeafNodeId(null)
return
}
const firstLeafId = getFirstLeafId(activeBoard.layout)
setSelectedLeafNodeId((prev) => prev || firstLeafId)
}, [activeBoard?.id, activeBoard?.layout])
useEffect(() => {
if (!activeBoard) return
runAllInBoard(activeBoard).catch(() => void 0)
@@ -567,6 +584,18 @@ ORDER BY reward_points DESC, score DESC`,
)
}
const openRenameModal = () => {
if (!activeBoard) return
setRenameValue(activeBoard.name)
setRenameVisible(true)
}
const submitRenameBoard = () => {
if (!activeBoard) return
updateBoardName(activeBoard.id, renameValue.trim() || t("board.untitledBoard"))
setRenameVisible(false)
}
const addBoard = () => {
if (!canManage) return
const newBoard = createDefaultBoard()
@@ -628,6 +657,11 @@ ORDER BY reward_points DESC, score DESC`,
)
}
const splitSelectedLeaf = (direction: SplitDirection) => {
if (!activeBoard || !selectedLeafNodeId) return
addListBySplit(activeBoard.id, selectedLeafNodeId, direction)
}
const removeList = (boardId: string, listId: string) => {
if (!canManage) return
@@ -874,10 +908,18 @@ ORDER BY reward_points DESC, score DESC`,
const renderLeafPanel = (board: BoardConfig, leaf: LayoutLeafNode): React.JSX.Element => {
const list = board.lists.find((item) => item.id === leaf.listId)
if (!list) return <Empty description={t("common.noData")} />
const isSelected = selectedLeafNodeId === leaf.id
return (
<Card
style={{ height: "100%", backgroundColor: "var(--ss-card-bg)", border: "1px solid var(--ss-border-color)" }}
onClick={() => setSelectedLeafNodeId(leaf.id)}
style={{
height: "100%",
backgroundColor: "var(--ss-card-bg)",
border: isSelected ? "1px solid var(--ant-color-primary, #1677ff)" : "1px solid var(--ss-border-color)",
boxShadow: isSelected ? "0 8px 18px rgba(22, 119, 255, 0.14)" : undefined,
cursor: "pointer",
}}
styles={{ body: { height: "100%", display: "flex", flexDirection: "column", padding: 12 } }}
title={<span style={{ fontWeight: 600 }}>{list.name}</span>}
extra={
@@ -888,12 +930,6 @@ ORDER BY reward_points DESC, score DESC`,
<Button size="small" onClick={() => setEditingListId(list.id)} icon={<EditOutlined />}>
{t("board.editList")}
</Button>
<Button size="small" disabled={!canManage} onClick={() => addListBySplit(board.id, leaf.id, "horizontal")}>
{t("board.splitHorizontal")}
</Button>
<Button size="small" disabled={!canManage} onClick={() => addListBySplit(board.id, leaf.id, "vertical")}>
{t("board.splitVertical")}
</Button>
<Popconfirm
title={t("board.removeListConfirm")}
onConfirm={() => removeList(board.id, list.id)}
@@ -996,6 +1032,23 @@ ORDER BY reward_points DESC, score DESC`,
<Tag color={canManage ? "success" : "default"}>{canManage ? t("board.editable") : t("board.readonly")}</Tag>
</Space>
<Space>
<Button
icon={<PlusOutlined />}
onClick={() => splitSelectedLeaf("horizontal")}
disabled={!canManage || !activeBoard || !selectedLeafNodeId}
>
{t("board.splitHorizontal")}
</Button>
<Button
icon={<PlusOutlined />}
onClick={() => splitSelectedLeaf("vertical")}
disabled={!canManage || !activeBoard || !selectedLeafNodeId}
>
{t("board.splitVertical")}
</Button>
<Button icon={<EditOutlined />} onClick={openRenameModal} disabled={!canManage || !activeBoard}>
{t("board.renameBoard")}
</Button>
<Button icon={<ReloadOutlined />} loading={loading} onClick={fetchBoards}>
{t("common.refresh")}
</Button>
@@ -1016,34 +1069,17 @@ ORDER BY reward_points DESC, score DESC`,
{activeBoard ? (
<Space direction="vertical" size={16} style={{ width: "100%" }}>
<Card
title={t("board.boardConfig")}
extra={
<Space>
<Button onClick={() => runAllInBoard(activeBoard)} icon={<PlayCircleOutlined />}>
{t("board.runAll")}
</Button>
<Popconfirm title={t("board.removeBoardConfirm")} onConfirm={() => removeBoard(activeBoard.id)} disabled={!canManage}>
<Button danger icon={<DeleteOutlined />} disabled={!canManage}>
{t("board.removeBoard")}
</Button>
</Popconfirm>
</Space>
}
style={{ backgroundColor: "var(--ss-card-bg)" }}
>
<Input
value={activeBoard.name}
onChange={(e) => updateBoardName(activeBoard.id, e.target.value.trim())}
placeholder={t("board.boardNamePlaceholder")}
disabled={!canManage}
/>
{saving && (
<Typography.Text type="secondary" style={{ display: "block", marginTop: 8 }}>
{t("board.saving")}
</Typography.Text>
)}
</Card>
<Space>
<Button onClick={() => runAllInBoard(activeBoard)} icon={<PlayCircleOutlined />}>
{t("board.runAll")}
</Button>
<Popconfirm title={t("board.removeBoardConfirm")} onConfirm={() => removeBoard(activeBoard.id)} disabled={!canManage}>
<Button danger icon={<DeleteOutlined />} disabled={!canManage}>
{t("board.removeBoard")}
</Button>
</Popconfirm>
{saving && <Typography.Text type="secondary">{t("board.saving")}</Typography.Text>}
</Space>
{renderBoardWorkspace()}
</Space>
@@ -1054,6 +1090,17 @@ ORDER BY reward_points DESC, score DESC`,
)}
</Space>
<Modal
title={t("board.renameBoard")}
open={renameVisible}
onCancel={() => setRenameVisible(false)}
onOk={submitRenameBoard}
okText={t("common.confirm")}
cancelText={t("common.cancel")}
>
<Input value={renameValue} onChange={(e) => setRenameValue(e.target.value)} placeholder={t("board.boardNamePlaceholder")} />
</Modal>
<Modal
title={t("board.sqlEditorTitle")}
open={Boolean(editingList)}
+1
View File
@@ -568,6 +568,7 @@
"removeBoardConfirm": "Delete current board?",
"boardConfig": "Board Config",
"boardNamePlaceholder": "Enter board name",
"renameBoard": "Rename Board",
"newBoard": "New Board",
"untitledBoard": "Untitled Board",
"addList": "Add Student List",
+1
View File
@@ -568,6 +568,7 @@
"removeBoardConfirm": "确认删除当前看板?",
"boardConfig": "看板配置",
"boardNamePlaceholder": "输入看板名称",
"renameBoard": "重命名看板",
"newBoard": "新看板",
"untitledBoard": "未命名看板",
"addList": "新增学生列表",