mirror of
https://github.com/SECTL/SecScore.git
synced 2026-07-19 19:04:23 +08:00
feat: OOBE新增入口页与PostgreSQL自动同步分流
This commit is contained in:
@@ -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>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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": "请选择您偏好的界面语言"
|
||||||
|
|||||||
Reference in New Issue
Block a user