mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
看板支持列表/卡片/网格三种学生视图切换
This commit is contained in:
@@ -21,6 +21,7 @@ interface StudentListConfig {
|
|||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
sql: string
|
sql: string
|
||||||
|
viewMode: BoardStudentViewMode
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BoardConfig {
|
interface BoardConfig {
|
||||||
@@ -40,6 +41,8 @@ interface BoardManagerProps {
|
|||||||
canManage: boolean
|
canManage: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BoardStudentViewMode = "list" | "card" | "grid"
|
||||||
|
|
||||||
interface BoardStudentCardData {
|
interface BoardStudentCardData {
|
||||||
key: string
|
key: string
|
||||||
name: string
|
name: string
|
||||||
@@ -66,6 +69,7 @@ const createDefaultList = (): StudentListConfig => ({
|
|||||||
id: makeId(),
|
id: makeId(),
|
||||||
name: "学生积分榜",
|
name: "学生积分榜",
|
||||||
sql: getDefaultSql(),
|
sql: getDefaultSql(),
|
||||||
|
viewMode: "card",
|
||||||
})
|
})
|
||||||
|
|
||||||
const createDefaultBoard = (): BoardConfig => ({
|
const createDefaultBoard = (): BoardConfig => ({
|
||||||
@@ -86,6 +90,10 @@ const normalizeBoards = (input: unknown): BoardConfig[] => {
|
|||||||
name:
|
name:
|
||||||
typeof list?.name === "string" && list.name.trim() ? list.name.trim() : "学生列表",
|
typeof list?.name === "string" && list.name.trim() ? list.name.trim() : "学生列表",
|
||||||
sql: typeof list?.sql === "string" && list.sql.trim() ? list.sql : getDefaultSql(),
|
sql: typeof list?.sql === "string" && list.sql.trim() ? list.sql : getDefaultSql(),
|
||||||
|
viewMode:
|
||||||
|
list?.viewMode === "list" || list?.viewMode === "card" || list?.viewMode === "grid"
|
||||||
|
? list.viewMode
|
||||||
|
: "card",
|
||||||
}))
|
}))
|
||||||
.filter((list: StudentListConfig) => list.sql.trim())
|
.filter((list: StudentListConfig) => list.sql.trim())
|
||||||
: []
|
: []
|
||||||
@@ -421,7 +429,8 @@ ORDER BY reward_points DESC, score DESC`,
|
|||||||
{
|
{
|
||||||
id: makeId(),
|
id: makeId(),
|
||||||
name: t("board.newList"),
|
name: t("board.newList"),
|
||||||
sql: getDefaultSql(),
|
sql: getDefaultSql(),
|
||||||
|
viewMode: "card",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -594,6 +603,19 @@ ORDER BY reward_points DESC, score DESC`,
|
|||||||
onChange={(presetId) => applyPreset(activeBoard.id, list.id, presetId)}
|
onChange={(presetId) => applyPreset(activeBoard.id, list.id, presetId)}
|
||||||
disabled={!canManage}
|
disabled={!canManage}
|
||||||
/>
|
/>
|
||||||
|
<Select
|
||||||
|
style={{ width: 140 }}
|
||||||
|
value={list.viewMode}
|
||||||
|
onChange={(viewMode: BoardStudentViewMode) =>
|
||||||
|
updateList(activeBoard.id, list.id, { viewMode })
|
||||||
|
}
|
||||||
|
options={[
|
||||||
|
{ value: "list", label: t("board.viewModes.list") },
|
||||||
|
{ value: "card", label: t("board.viewModes.card") },
|
||||||
|
{ value: "grid", label: t("board.viewModes.grid") },
|
||||||
|
]}
|
||||||
|
disabled={!canManage}
|
||||||
|
/>
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
icon={<PlayCircleOutlined />}
|
icon={<PlayCircleOutlined />}
|
||||||
@@ -643,7 +665,12 @@ ORDER BY reward_points DESC, score DESC`,
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "grid",
|
display: "grid",
|
||||||
gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))",
|
gridTemplateColumns:
|
||||||
|
list.viewMode === "grid"
|
||||||
|
? "repeat(auto-fill, minmax(180px, 1fr))"
|
||||||
|
: list.viewMode === "list"
|
||||||
|
? "1fr"
|
||||||
|
: "repeat(auto-fill, minmax(220px, 1fr))",
|
||||||
gap: 12,
|
gap: 12,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -660,7 +687,12 @@ ORDER BY reward_points DESC, score DESC`,
|
|||||||
boxShadow: "0 6px 16px rgba(0, 0, 0, 0.06)",
|
boxShadow: "0 6px 16px rgba(0, 0, 0, 0.06)",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
}}
|
}}
|
||||||
styles={{ body: { padding: "12px 14px" } }}
|
styles={{
|
||||||
|
body: {
|
||||||
|
padding:
|
||||||
|
list.viewMode === "grid" ? "12px" : list.viewMode === "list" ? "10px 12px" : "12px 14px",
|
||||||
|
},
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{rankBadge && (
|
{rankBadge && (
|
||||||
<div
|
<div
|
||||||
@@ -674,11 +706,18 @@ ORDER BY reward_points DESC, score DESC`,
|
|||||||
{rankBadge}
|
{rankBadge}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: list.viewMode === "grid" ? "flex-start" : "center",
|
||||||
|
flexDirection: list.viewMode === "grid" ? "column" : "row",
|
||||||
|
gap: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: 42,
|
width: list.viewMode === "grid" ? 56 : 42,
|
||||||
height: 42,
|
height: list.viewMode === "grid" ? 56 : 42,
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
backgroundColor: avatarColor,
|
backgroundColor: avatarColor,
|
||||||
color: "#fff",
|
color: "#fff",
|
||||||
@@ -692,43 +731,52 @@ ORDER BY reward_points DESC, score DESC`,
|
|||||||
>
|
>
|
||||||
{avatarText}
|
{avatarText}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ minWidth: 0, flex: 1 }}>
|
<div style={{ minWidth: 0, flex: 1, width: "100%" }}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
fontSize: 15,
|
fontSize: list.viewMode === "grid" ? 16 : 15,
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
color: "var(--ss-text-main)",
|
color: "var(--ss-text-main)",
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
textOverflow: "ellipsis",
|
textOverflow: "ellipsis",
|
||||||
whiteSpace: "nowrap",
|
whiteSpace: "nowrap",
|
||||||
|
textAlign: list.viewMode === "grid" ? "center" : "left",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ marginTop: 4, display: "flex", flexWrap: "wrap", gap: 6 }}>
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 4,
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
gap: 6,
|
||||||
|
justifyContent: list.viewMode === "grid" ? "center" : "flex-start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
{item.score !== undefined && (
|
{item.score !== undefined && (
|
||||||
<Tag color={item.score >= 0 ? "success" : "error"} style={{ margin: 0 }}>
|
<Tag color={item.score >= 0 ? "success" : "error"} style={{ margin: 0 }}>
|
||||||
总分: {item.score > 0 ? `+${item.score}` : item.score}
|
{t("board.metrics.totalScore")}: {item.score > 0 ? `+${item.score}` : item.score}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
{item.rewardPoints !== undefined && (
|
{item.rewardPoints !== undefined && (
|
||||||
<Tag color="processing" style={{ margin: 0 }}>
|
<Tag color="processing" style={{ margin: 0 }}>
|
||||||
奖励分: {item.rewardPoints}
|
{t("board.metrics.rewardPoints")}: {item.rewardPoints}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
{item.weekChange !== undefined && (
|
{item.weekChange !== undefined && (
|
||||||
<Tag color={item.weekChange >= 0 ? "success" : "error"} style={{ margin: 0 }}>
|
<Tag color={item.weekChange >= 0 ? "success" : "error"} style={{ margin: 0 }}>
|
||||||
近7天: {item.weekChange > 0 ? `+${item.weekChange}` : item.weekChange}
|
{t("board.metrics.weekChange")}: {item.weekChange > 0 ? `+${item.weekChange}` : item.weekChange}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
{item.weekDeducted !== undefined && (
|
{item.weekDeducted !== undefined && (
|
||||||
<Tag color="gold" style={{ margin: 0 }}>
|
<Tag color="gold" style={{ margin: 0 }}>
|
||||||
近7天扣分: {item.weekDeducted}
|
{t("board.metrics.weekDeducted")}: {item.weekDeducted}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
{item.answeredCount !== undefined && (
|
{item.answeredCount !== undefined && (
|
||||||
<Tag color="cyan" style={{ margin: 0 }}>
|
<Tag color="cyan" style={{ margin: 0 }}>
|
||||||
今日回答: {item.answeredCount}
|
{t("board.metrics.todayAnswered")}: {item.answeredCount}
|
||||||
</Tag>
|
</Tag>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -578,6 +578,18 @@
|
|||||||
"applyPreset": "Apply Preset",
|
"applyPreset": "Apply Preset",
|
||||||
"run": "Run SQL",
|
"run": "Run SQL",
|
||||||
"runAll": "Run All",
|
"runAll": "Run All",
|
||||||
|
"viewModes": {
|
||||||
|
"list": "List View",
|
||||||
|
"card": "Card View",
|
||||||
|
"grid": "Grid View"
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"totalScore": "Score",
|
||||||
|
"rewardPoints": "Reward",
|
||||||
|
"weekChange": "7d",
|
||||||
|
"weekDeducted": "7d Deduct",
|
||||||
|
"todayAnswered": "Today"
|
||||||
|
},
|
||||||
"sqlPlaceholder": "Enter SQL (single SELECT / WITH query only)",
|
"sqlPlaceholder": "Enter SQL (single SELECT / WITH query only)",
|
||||||
"templateHint": "Template variables: {{today_start}}, {{since_7d}}, {{since_30d}}, {{now}}",
|
"templateHint": "Template variables: {{today_start}}, {{since_7d}}, {{since_30d}}, {{now}}",
|
||||||
"templateDescription": "Use template variables directly in SQL. They will be replaced with ISO timestamps at runtime for both SQLite and PostgreSQL.",
|
"templateDescription": "Use template variables directly in SQL. They will be replaced with ISO timestamps at runtime for both SQLite and PostgreSQL.",
|
||||||
|
|||||||
@@ -578,6 +578,18 @@
|
|||||||
"applyPreset": "套用预设",
|
"applyPreset": "套用预设",
|
||||||
"run": "运行 SQL",
|
"run": "运行 SQL",
|
||||||
"runAll": "运行全部",
|
"runAll": "运行全部",
|
||||||
|
"viewModes": {
|
||||||
|
"list": "列表视图",
|
||||||
|
"card": "卡片视图",
|
||||||
|
"grid": "网格视图"
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"totalScore": "总分",
|
||||||
|
"rewardPoints": "奖励分",
|
||||||
|
"weekChange": "近7天",
|
||||||
|
"weekDeducted": "近7天扣分",
|
||||||
|
"todayAnswered": "今日回答"
|
||||||
|
},
|
||||||
"sqlPlaceholder": "输入 SQL(仅支持单条 SELECT / WITH 查询)",
|
"sqlPlaceholder": "输入 SQL(仅支持单条 SELECT / WITH 查询)",
|
||||||
"templateHint": "支持时间模板变量:{{today_start}}、{{since_7d}}、{{since_30d}}、{{now}}",
|
"templateHint": "支持时间模板变量:{{today_start}}、{{since_7d}}、{{since_30d}}、{{now}}",
|
||||||
"templateDescription": "可直接在 SQL 中使用模板变量,运行时会自动替换为 ISO 时间,兼容 SQLite 与 PostgreSQL。",
|
"templateDescription": "可直接在 SQL 中使用模板变量,运行时会自动替换为 ISO 时间,兼容 SQLite 与 PostgreSQL。",
|
||||||
|
|||||||
Reference in New Issue
Block a user