feat: OOBE新增入口页与PostgreSQL自动同步分流

This commit is contained in:
JSR
2026-03-19 18:16:40 +08:00
parent 62ae72a073
commit 8f68b972b5
3 changed files with 192 additions and 59 deletions
+112 -5
View File
@@ -13,7 +13,15 @@ interface oobeProps {
onComplete: () => void onComplete: () => void
} }
type oobeStep = "language" | "theme" | "password" | "students" | "reasons" | "start" type oobeStep =
| "entry"
| "postgresql"
| "language"
| "theme"
| "password"
| "students"
| "reasons"
| "start"
interface studentItem { interface studentItem {
name: string name: string
@@ -75,8 +83,9 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
const { currentTheme, setTheme, themes, applyTheme } = useTheme() const { currentTheme, setTheme, themes, applyTheme } = useTheme()
const [messageApi, contextHolder] = message.useMessage() const [messageApi, contextHolder] = message.useMessage()
const [currentStep, setCurrentStep] = useState<oobeStep>("language") const [currentStep, setCurrentStep] = useState<oobeStep>("entry")
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [pgAutoLoading, setPgAutoLoading] = useState(false)
const [selectedLanguage, setSelectedLanguage] = useState<AppLanguage>("zh-CN") const [selectedLanguage, setSelectedLanguage] = useState<AppLanguage>("zh-CN")
const [workingTheme, setWorkingTheme] = useState<themeConfig | null>(null) const [workingTheme, setWorkingTheme] = useState<themeConfig | null>(null)
@@ -88,10 +97,11 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
const [newReasonDelta, setNewReasonDelta] = useState(1) const [newReasonDelta, setNewReasonDelta] = useState(1)
const [adminPassword, setAdminPassword] = useState("") const [adminPassword, setAdminPassword] = useState("")
const [pointsPassword, setPointsPassword] = useState("") const [pointsPassword, setPointsPassword] = useState("")
const [pgConnectionString, setPgConnectionString] = useState("")
const fileInputRef = useRef<HTMLInputElement>(null) const fileInputRef = useRef<HTMLInputElement>(null)
const steps: oobeStep[] = ["language", "theme", "password", "students", "reasons", "start"] const steps: oobeStep[] = ["entry", "language", "theme", "password", "students", "reasons", "start"]
const stepIndex = steps.indexOf(currentStep) + 1 const stepIndex = steps.indexOf(currentStep) + 1
const totalSteps = steps.length const totalSteps = steps.length
@@ -325,6 +335,63 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
handleNext() handleNext()
} }
const markWizardCompleted = async () => {
if (!(window as any).api) throw new Error("api not ready")
const res = await (window as any).api.setSetting("is_wizard_completed", true)
if (!res?.success) throw new Error("failed")
}
const handleSkipToApp = async () => {
setLoading(true)
try {
await markWizardCompleted()
showOobeMessage("success", t("common.success"))
onComplete()
} catch (e: any) {
showOobeMessage("error", e?.message || t("common.error"))
} finally {
setLoading(false)
}
}
const handleConnectPgAndSync = async () => {
const connectionString = pgConnectionString.trim()
if (!connectionString) {
showOobeMessage("warning", t("settings.database.enterConnectionString"))
return
}
setPgAutoLoading(true)
try {
if (!(window as any).api) throw new Error("api not ready")
const switchRes = await (window as any).api.dbSwitchConnection(connectionString)
if (!switchRes?.success || switchRes?.data?.type !== "postgresql") {
throw new Error(switchRes?.message || t("settings.database.switchFailed"))
}
const previewRes = await (window as any).api.dbSyncPreview()
if (!previewRes?.success || !previewRes?.data?.can_sync) {
throw new Error(previewRes?.message || previewRes?.data?.message || t("settings.database.uploadFailed"))
}
if (previewRes?.data?.need_sync) {
const syncApplyRes = await (window as any).api.dbSyncApply("keep_local")
if (!syncApplyRes?.success || !syncApplyRes?.data?.success) {
throw new Error(
syncApplyRes?.data?.message || syncApplyRes?.message || t("settings.database.uploadFailed")
)
}
}
await markWizardCompleted()
showOobeMessage("success", t("settings.database.uploadSuccess"))
onComplete()
} catch (e: any) {
showOobeMessage("error", e?.message || t("settings.database.uploadFailed"))
} finally {
setPgAutoLoading(false)
}
}
const handleFinish = async () => { const handleFinish = async () => {
setLoading(true) setLoading(true)
try { try {
@@ -374,8 +441,7 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
ensureSuccess(authRes, t("common.error")) ensureSuccess(authRes, t("common.error"))
} }
const res = await (window as any).api.setSetting("is_wizard_completed", true) await markWizardCompleted()
ensureSuccess(res, "failed")
showOobeMessage("success", t("common.success")) showOobeMessage("success", t("common.success"))
onComplete() onComplete()
@@ -392,6 +458,45 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
const renderStepContent = () => { const renderStepContent = () => {
switch (currentStep) { switch (currentStep) {
case "entry":
return (
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<Typography.Text type="secondary">{t("oobe.steps.entry.description")}</Typography.Text>
<Button type="primary" size="large" onClick={() => setCurrentStep("language")}>
{t("oobe.steps.entry.enterOobe")}
</Button>
<Button size="large" onClick={() => setCurrentStep("postgresql")}>
{t("oobe.steps.entry.connectPostgresAutoSync")}
</Button>
<Button size="large" onClick={handleSkipToApp} loading={loading}>
{t("oobe.steps.entry.skipDirect")}
</Button>
</div>
)
case "postgresql":
return (
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
<Typography.Text type="secondary">
{t("oobe.steps.postgresql.description")}
</Typography.Text>
<Input
value={pgConnectionString}
onChange={(e) => setPgConnectionString(e.target.value)}
placeholder={t("oobe.steps.postgresql.connectionPlaceholder")}
/>
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{t("settings.database.connectionExample")}
</Typography.Text>
<div style={{ display: "flex", gap: 8 }}>
<Button onClick={() => setCurrentStep("entry")}>{t("common.prev")}</Button>
<Button type="primary" loading={pgAutoLoading} onClick={handleConnectPgAndSync}>
{t("oobe.steps.postgresql.autoSyncAndEnter")}
</Button>
</div>
</div>
)
case "language": case "language":
return ( return (
<div style={{ display: "flex", flexDirection: "column", gap: 16 }}> <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
@@ -802,6 +907,7 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
<div style={{ minHeight: 200, marginBottom: 24 }}>{renderStepContent()}</div> <div style={{ minHeight: 200, marginBottom: 24 }}>{renderStepContent()}</div>
{currentStep !== "entry" && currentStep !== "postgresql" && (
<div <div
style={{ style={{
display: "flex", display: "flex",
@@ -874,6 +980,7 @@ export const OOBE: React.FC<oobeProps> = ({ visible, onComplete }) => {
)} )}
</div> </div>
</div> </div>
)}
</div> </div>
</div> </div>
) )
+13
View File
@@ -42,6 +42,19 @@
"step": "Step {{current}}/{{total}}", "step": "Step {{current}}/{{total}}",
"skip": "Skip", "skip": "Skip",
"steps": { "steps": {
"entry": {
"title": "Get Started",
"description": "Choose how you want to proceed",
"enterOobe": "Enter OOBE",
"connectPostgresAutoSync": "Connect PostgreSQL with Auto Sync",
"skipDirect": "Skip OOBE and Enter"
},
"postgresql": {
"title": "Connect PostgreSQL",
"description": "Enter a PostgreSQL connection string. The app will connect and auto-sync before entering.",
"connectionPlaceholder": "postgresql://user:password@host:port/database?sslmode=require",
"autoSyncAndEnter": "Connect, Auto Sync, and Enter"
},
"language": { "language": {
"title": "Select Language", "title": "Select Language",
"description": "Choose your preferred interface language" "description": "Choose your preferred interface language"
+13
View File
@@ -42,6 +42,19 @@
"step": "第 {{current}}/{{total}} 步", "step": "第 {{current}}/{{total}} 步",
"skip": "跳过", "skip": "跳过",
"steps": { "steps": {
"entry": {
"title": "开始配置",
"description": "请选择接下来要进行的操作",
"enterOobe": "进入OOBE",
"connectPostgresAutoSync": "连接PostgreSQL自动同步",
"skipDirect": "跳过OOBE,直接进入"
},
"postgresql": {
"title": "连接 PostgreSQL",
"description": "输入 PostgreSQL 连接字符串,系统将自动连接并同步后进入应用",
"connectionPlaceholder": "postgresql://user:password@host:port/database?sslmode=require",
"autoSyncAndEnter": "连接并自动同步后进入"
},
"language": { "language": {
"title": "选择语言", "title": "选择语言",
"description": "请选择您偏好的界面语言" "description": "请选择您偏好的界面语言"