diff --git a/src-tauri/src/commands/database.rs b/src-tauri/src/commands/database.rs index cd4d4ce..7933ff1 100644 --- a/src-tauri/src/commands/database.rs +++ b/src-tauri/src/commands/database.rs @@ -11,6 +11,7 @@ use crate::db::entities::{ reasons, reward_redemptions, reward_settings, score_events, student_tags, students, tags, }; use crate::db::migration::run_migration; +use crate::services::logger::LogLevel; use crate::services::permission::PermissionLevel; use crate::services::settings::{SettingsKey, SettingsValue}; use crate::state::AppState; @@ -1052,24 +1053,63 @@ pub async fn db_switch_connection( ) -> Result, String> { check_admin_permission(&state)?; + let state_guard = state.read(); + let logger = state_guard.logger.read(); + logger.log( + LogLevel::Info, + &format!("Database switch requested, connection_string length: {}", connection_string.len()), + Some("database"), + None, + ); + drop(state_guard); + let (db_type, saved_connection_string, saved_status, conn) = if connection_string .starts_with("postgres://") || connection_string.starts_with("postgresql://") { + logger.log( + LogLevel::Info, + "Switching to PostgreSQL database", + Some("database"), + None, + ); let conn = timeout( Duration::from_secs(DB_CONNECT_TIMEOUT_SECS), create_postgres_connection(&connection_string), ) .await .map_err(|_| "PostgreSQL 连接超时,请检查网络或连接字符串".to_string())? - .map_err(|e| e.to_string())?; + .map_err(|e| { + logger.log( + LogLevel::Error, + &format!("PostgreSQL connection failed: {}", e), + Some("database"), + None, + ); + e.to_string() + })?; timeout( Duration::from_secs(DB_MIGRATION_TIMEOUT_SECS), run_migration(&conn, DatabaseType::PostgreSQL), ) .await .map_err(|_| "PostgreSQL 初始化超时,请稍后重试".to_string())? - .map_err(|e| e.to_string())?; + .map_err(|e| { + logger.log( + LogLevel::Error, + &format!("PostgreSQL migration failed: {}", e), + Some("database"), + None, + ); + e.to_string() + })?; + + logger.log( + LogLevel::Info, + "PostgreSQL connection and migration successful", + Some("database"), + None, + ); ( "postgresql".to_string(), @@ -1078,6 +1118,12 @@ pub async fn db_switch_connection( conn, ) } else { + logger.log( + LogLevel::Info, + "Switching to SQLite database", + Some("database"), + None, + ); let path = if connection_string.starts_with("sqlite://") { connection_string .strip_prefix("sqlite://") @@ -1089,10 +1135,33 @@ pub async fn db_switch_connection( let conn = create_sqlite_connection(&path) .await - .map_err(|e| e.to_string())?; + .map_err(|e| { + logger.log( + LogLevel::Error, + &format!("SQLite connection failed: {}", e), + Some("database"), + None, + ); + e.to_string() + })?; run_migration(&conn, DatabaseType::SQLite) .await - .map_err(|e| e.to_string())?; + .map_err(|e| { + logger.log( + LogLevel::Error, + &format!("SQLite migration failed: {}", e), + Some("database"), + None, + ); + e.to_string() + })?; + + logger.log( + LogLevel::Info, + "SQLite connection and migration successful", + Some("database"), + None, + ); ( "sqlite".to_string(), @@ -1151,6 +1220,15 @@ pub async fn db_switch_connection( }, ); + let state_guard = state.read(); + let logger = state_guard.logger.read(); + logger.log( + LogLevel::Info, + &format!("Database switched successfully to {}", db_type), + Some("database"), + None, + ); + Ok(IpcResponse::success(SwitchConnectionResult { db_type })) } diff --git a/src/assets/main.css b/src/assets/main.css index 37c3c53..d9ecd78 100644 --- a/src/assets/main.css +++ b/src/assets/main.css @@ -124,6 +124,92 @@ select { padding-top: 14px; padding-bottom: 14px; } + + .ant-modal { + max-width: 100vw !important; + width: 100vw !important; + margin: 0 !important; + padding: 0 !important; + top: 0 !important; + bottom: 0 !important; + left: 0 !important; + right: 0 !important; + } + + .ant-modal-content { + height: 100vh !important; + border-radius: 0 !important; + display: flex !important; + flex-direction: column !important; + } + + .ant-modal-body { + flex: 1 !important; + overflow-y: auto !important; + overflow-x: hidden !important; + -webkit-overflow-scrolling: touch !important; + } + + .ant-modal-wrap { + overflow: hidden !important; + } + + .ant-select-dropdown { + max-height: 50vh !important; + overflow-y: auto !important; + -webkit-overflow-scrolling: touch !important; + } + + .ant-table-wrapper { + overflow-x: auto !important; + -webkit-overflow-scrolling: touch !important; + } + + .ant-table { + min-width: 600px !important; + } +} + +@media (max-width: 480px) { + .ant-modal { + max-width: 100vw !important; + width: 100vw !important; + margin: 0 !important; + padding: 0 !important; + } + + .ant-modal-content { + height: 100vh !important; + border-radius: 0 !important; + display: flex !important; + flex-direction: column !important; + } + + .ant-modal-body { + flex: 1 !important; + overflow-y: auto !important; + overflow-x: hidden !important; + -webkit-overflow-scrolling: touch !important; + } + + .ant-modal-wrap { + overflow: hidden !important; + } + + .ant-table-container { + overflow-x: auto !important; + -webkit-overflow-scrolling: touch !important; + } + + .ant-table { + min-width: 600px !important; + } + + .ant-select-dropdown { + max-height: 50vh !important; + overflow-y: auto !important; + -webkit-overflow-scrolling: touch !important; + } } .ss-table-center .ant-table th, diff --git a/src/components/OOBE/OOBE.tsx b/src/components/OOBE/OOBE.tsx index fa14ae1..5fe5d9f 100644 --- a/src/components/OOBE/OOBE.tsx +++ b/src/components/OOBE/OOBE.tsx @@ -7,6 +7,7 @@ import { useTheme } from "../../contexts/ThemeContext" import { changeLanguage, AppLanguage, languageOptions } from "../../i18n" import type { themeConfig } from "../../preload/types" import logoSvg from "../../assets/logoHD.svg" +import { useResponsive, useScreenSize } from "../../hooks/useResponsive" interface oobeProps { visible: boolean @@ -100,6 +101,9 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { const { t } = useTranslation() const { currentTheme, setTheme, themes, applyTheme } = useTheme() const [messageApi, contextHolder] = message.useMessage() + const breakpoint = useResponsive() + const { width: screenWidth, height: screenHeight } = useScreenSize() + const isMobile = breakpoint === "xs" || breakpoint === "sm" const [currentStep, setCurrentStep] = useState("entry") const [loading, setLoading] = useState(false) @@ -902,6 +906,7 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { display: "flex", alignItems: "center", justifyContent: "center", + overflow: "hidden", }} > @@ -913,17 +918,21 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { background: isDark ? "rgba(15, 25, 45, 0.55)" : "rgba(255, 255, 255, 0.65)", backdropFilter: "blur(20px)", borderRadius: 16, - padding: 32, + padding: isMobile ? 20 : 32, width: 480, - maxWidth: "90vw", + maxWidth: isMobile ? "100vw" : "90vw", + maxHeight: isMobile ? "100vh" : "90vh", boxShadow: isDark ? "0 8px 32px rgba(0, 0, 0, 0.3)" : "0 8px 32px rgba(0, 0, 0, 0.1)", border: isDark ? "1px solid rgba(255, 255, 255, 0.08)" : "1px solid rgba(0, 0, 0, 0.06)", fontFamily: "var(--ss-font-family)", + display: "flex", + flexDirection: "column", + overflow: "hidden", }} > {contextHolder} -
+
= ({ visible, onComplete }) => {
-
+
= ({ visible, onComplete }) => {
-
{renderStepContent()}
+
+ {renderStepContent()} +
{currentStep !== "entry" && currentStep !== "postgresql" && (
= ({ visible, onComplete }) => { display: "flex", justifyContent: "space-between", alignItems: "center", + flexShrink: 0, + flexWrap: isMobile ? "wrap" : "nowrap", + gap: isMobile ? 8 : 0, }} > -
+
{currentStep !== "language" && ( )} @@ -968,6 +991,9 @@ export const OOBE: React.FC = ({ visible, onComplete }) => { gap: 8, color: isDark ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.45)", fontSize: 12, + order: isMobile ? 3 : 2, + width: isMobile ? "100%" : "auto", + justifyContent: isMobile ? "center" : "flex-start", }} >
= ({ visible, onComplete }) => { {t("oobe.step", { current: stepIndex, total: totalSteps })}
-
+
{currentStep !== "start" ? (
- + @@ -903,6 +911,7 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission onClick={switchToSQLite} loading={pgSwitchLoading} disabled={!canAdmin || pgConnectionStatus.type === "sqlite"} + style={{ width: isMobile ? "100%" : "auto" }} > {t("settings.database.switchToSQLite")} @@ -910,6 +919,7 @@ export const Settings: React.FC<{ permission: permissionLevel }> = ({ permission onClick={uploadLocalToRemote} loading={pgUploadLoading} disabled={!canAdmin || pgConnectionStatus.type !== "postgresql"} + style={{ width: isMobile ? "100%" : "auto" }} > {t("settings.database.uploadButton")} diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index ce03422..70f7059 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -14,6 +14,7 @@ import { } from "@ant-design/icons" import { useState, useEffect } from "react" import { useTranslation } from "react-i18next" +import { useResponsive } from "../hooks/useResponsive" import appLogo from "../assets/logoHD.svg" const { Sider } = Layout @@ -44,6 +45,8 @@ export function Sidebar({ onFloatingExpandedChange, }: SidebarProps): React.JSX.Element { const { t } = useTranslation() + const breakpoint = useResponsive() + const isMobile = breakpoint === "xs" || breakpoint === "sm" const [dbStatus, setDbStatus] = useState({ type: "sqlite", connected: true }) const [syncLoading, setSyncLoading] = useState(false) const [messageApi, contextHolder] = message.useMessage() @@ -254,7 +257,16 @@ export function Sidebar({
{!hideMenu && ( -
+