import React, { createContext, useState, useEffect } from "react"; import {useHttp} from 'hooks/servicesProvider' import { message} from "hooks/message"; import {DEFAULT_CONFIG} from "app/env" interface SetupContextType { currentStep: number; setCurrentStep: (step: number) => void; } const SetupContext = createContext({ currentStep: 1, setCurrentStep: () => {}, }); // 步骤组件的通用属性接口 interface StepProps { onNext: () => void; } const StepContainer: React.FC<{ title: string; children: React.ReactNode }> = ({ title, children, }) => (

{title}

{children}
); // 通用的导航按钮组件 const NavigationButtons: React.FC = ({ onNext, loading = false, disabled = false }) => (
); // 输入框组件 const InputField: React.FC<{ label: string; name: string; defaultValue?: string | number; hint?: string; required?: boolean; }> = ({ label, name, defaultValue, hint, required = true }) => (

{label} {required && *}

{hint && (

{hint}

)}
); const Introduction: React.FC = ({ onNext }) => (

欢迎使用 Echoes

); const DatabaseConfig: React.FC = ({ onNext }) => { const [dbType, setDbType] = useState("postgresql"); const [loading, setLoading] = useState(false); const api = useHttp(); const validateForm = () => { const getRequiredFields = () => { switch (dbType) { case 'sqllite': return ['db_prefix', 'db_name']; case 'postgresql': case 'mysql': return ['db_host', 'db_prefix', 'db_port', 'db_user', 'db_password', 'db_name']; default: return []; } }; const requiredFields = getRequiredFields(); const emptyFields: string[] = []; requiredFields.forEach(field => { const input = document.querySelector(`[name="${field}"]`) as HTMLInputElement; if (input && (!input.value || input.value.trim() === '')) { emptyFields.push(field); } }); if (emptyFields.length > 0) { const fieldNames = emptyFields.map(field => { switch (field) { case 'db_host': return '数据库地址'; case 'db_prefix': return '数据库前缀'; case 'db_port': return '端口'; case 'db_user': return '用户名'; case 'db_password': return '密码'; case 'db_name': return '数据库名'; default: return field; } }); message.error(`请填写以下必填项:${fieldNames.join('、')}`, '验证失败'); return false; } return true; }; const handleNext = async () => { if (!validateForm()) { return; } setLoading(true); try { const formData = { db_type: dbType, host: (document.querySelector('[name="db_host"]') as HTMLInputElement)?.value?.trim()??"", db_prefix: (document.querySelector('[name="db_prefix"]') as HTMLInputElement)?.value?.trim()??"", port: Number((document.querySelector('[name="db_port"]') as HTMLInputElement)?.value?.trim()??0), user: (document.querySelector('[name="db_user"]') as HTMLInputElement)?.value?.trim()??"", password: (document.querySelector('[name="db_password"]') as HTMLInputElement)?.value?.trim()??"", db_name: (document.querySelector('[name="db_name"]') as HTMLInputElement)?.value?.trim()??"", }; await api.post('/sql', formData); let oldEnv = import.meta.env?? DEFAULT_CONFIG const viteEnv = Object.entries(oldEnv).reduce((acc, [key, value]) => { if (key.startsWith('VITE_')) { acc[key] = value; } return acc; }, {} as Record); const newEnv = { ...viteEnv, VITE_INIT_STATUS: '2' }; await api.dev("/env", { method: "POST", body: JSON.stringify(newEnv), }); Object.assign( newEnv) message.success('数据库配置已保存', '配置成功'); setTimeout(() => onNext(), 1000); } catch (error: any) { console.error( error); message.error(error.message , error.title || '配置失败'); } finally { setLoading(false); } }; return (

数据库类型

{dbType === "postgresql" && ( <> )} {dbType === "mysql" && ( <> )} {dbType === "sqllite" && ( <> )}
); }; interface InstallReplyData { token: string, username: string, password: string, } const AdminConfig: React.FC = ({ onNext }) => { const [loading, setLoading] = useState(false); const api = useHttp(); const handleNext = async () => { setLoading(true); try { const formData = { username: (document.querySelector('[name="admin_username"]') as HTMLInputElement)?.value, password: (document.querySelector('[name="admin_password"]') as HTMLInputElement)?.value, email: (document.querySelector('[name="admin_email"]') as HTMLInputElement)?.value, }; const response = await api.post('/administrator', formData) as InstallReplyData; const data = response; localStorage.setItem('token', data.token); let oldEnv = import.meta.env ?? DEFAULT_CONFIG; const viteEnv = Object.entries(oldEnv).reduce((acc, [key, value]) => { if (key.startsWith('VITE_')) { acc[key] = value; } return acc; }, {} as Record); const newEnv = { ...viteEnv, VITE_INIT_STATUS: '3', VITE_API_USERNAME:data.username, VITE_API_PASSWORD:data.password }; await api.dev("/env", { method: "POST", body: JSON.stringify(newEnv), }); message.success('管理员账号已创建,即将进入下一步', '创建成功'); onNext(); } catch (error: any) { console.error(error); message.error(error.message, error.title || '创建失败'); } finally { setLoading(false); } }; return (
); }; const SetupComplete: React.FC = () => { const api = useHttp(); return (

恭喜!安装已完成

系统正在重启中,请稍候...

); }; // 修改主题切换按钮组件 const ThemeToggle: React.FC = () => { const [isDark, setIsDark] = useState(false); const [isVisible, setIsVisible] = useState(true); useEffect(() => { const isDarkMode = document.documentElement.classList.contains('dark'); setIsDark(isDarkMode); const handleScroll = () => { const currentScrollPos = window.scrollY; setIsVisible(currentScrollPos < 100); // 滚动超过100px就隐藏 }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, []); const toggleTheme = () => { const newIsDark = !isDark; setIsDark(newIsDark); document.documentElement.classList.toggle('dark'); }; return ( ); }; export default function SetupPage() { let step = Number(import.meta.env.VITE_INIT_STATUS)+1; const [currentStep, setCurrentStep] = useState(step); return (
{currentStep === 1 && ( setCurrentStep(currentStep + 1)} /> )} {currentStep === 2 && ( setCurrentStep(currentStep + 1)} /> )} {currentStep === 3 && ( setCurrentStep(currentStep + 1)} /> )} {currentStep === 4 && }
); }