Przeglądaj źródła

feat: 添加MySQL数据库支持及setup_mysql.sql建库脚本

sequoia00 1 miesiąc temu
rodzic
commit
f2c639aa5a
4 zmienionych plików z 79 dodań i 5 usunięć
  1. BIN
      __pycache__/database.cpython-313.pyc
  2. 12 2
      database.py
  3. 33 0
      setup_mysql.sql
  4. 34 3
      templates/base.html

BIN
__pycache__/database.cpython-313.pyc


+ 12 - 2
database.py

@@ -1,9 +1,19 @@
 from sqlmodel import SQLModel, create_engine, Session
 from pathlib import Path
+import os
 
-DATABASE_URL = "sqlite:///./错题本.db"
+MYSQL_USER = os.getenv("DB_USER", "root")
+MYSQL_PASS = os.getenv("DB_PASS", "792199Zhao*")
+MYSQL_HOST = os.getenv("DB_HOST", "localhost")
+MYSQL_PORT = os.getenv("DB_PORT", "3306")
+MYSQL_DB = os.getenv("DB_NAME", "cuowuben")
 
-engine = create_engine(DATABASE_URL, echo=False, connect_args={"check_same_thread": False})
+DATABASE_URL = (
+    f"mysql+pymysql://{MYSQL_USER}:{MYSQL_PASS}@{MYSQL_HOST}:{MYSQL_PORT}/{MYSQL_DB}"
+    "?charset=utf8mb4"
+)
+
+engine = create_engine(DATABASE_URL, echo=False)
 
 def init_db():
     SQLModel.metadata.create_all(engine)

+ 33 - 0
setup_mysql.sql

@@ -0,0 +1,33 @@
+CREATE DATABASE IF NOT EXISTS cuowuben DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;
+USE cuowuben;
+
+CREATE TABLE IF NOT EXISTS subject (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    name VARCHAR(100) NOT NULL,
+    color VARCHAR(20) NOT NULL DEFAULT '#6366f1',
+    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS error_image (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    subject_id INT NOT NULL,
+    date VARCHAR(20) NOT NULL,
+    filename VARCHAR(255) NOT NULL,
+    original_name VARCHAR(255) NOT NULL,
+    upload_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    INDEX idx_error_image_date (date),
+    FOREIGN KEY (subject_id) REFERENCES subject(id) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+CREATE TABLE IF NOT EXISTS evaluation (
+    id INT AUTO_INCREMENT PRIMARY KEY,
+    subject_id INT NOT NULL,
+    date VARCHAR(20) NOT NULL,
+    title VARCHAR(200) NOT NULL,
+    content TEXT,
+    filename VARCHAR(255),
+    upload_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    FOREIGN KEY (subject_id) REFERENCES subject(id) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+
+SELECT 'Database and tables created successfully' AS status;

+ 34 - 3
templates/base.html

@@ -42,7 +42,9 @@
     .sidebar.collapsed .collapse-btn i { transform: rotate(180deg); }
     .sidebar.collapsed main { margin-left: 5rem !important; }
     .sidebar-sub { overflow: hidden; transition: max-height 0.25s ease, opacity 0.2s; max-height: 500px; opacity: 1; }
-    .sidebar.collapsed .sidebar-sub { max-height: 0; opacity: 0; }
+    .sidebar-sub.collapsed { max-height: 0 !important; opacity: 0 !important; }
+    .subject-toggle { transition: transform 0.2s; display: inline-block; }
+    .subject-toggle.open { transform: rotate(90deg); }
     .subject-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.75rem; border-radius: 0.5rem; font-size: 0.8rem; color: #6b7280; text-decoration: none; transition: all 0.15s; cursor: pointer; white-space: nowrap; }
     .subject-item:hover { background: #f3f4f6; color: #374151; }
     .subject-item.active { background: rgba(99,102,241,0.10); color: #6366f1; }
@@ -79,11 +81,12 @@
 
         <!-- 科目管理 + 子菜单 -->
         <div class="mb-1">
-          <a href="/subjects" class="sidebar-link flex items-center gap-3 px-3 py-2.5 rounded-xl text-gray-500 hover:bg-gray-50 transition {% if '/subjects' in request.url.path or '/subject/' in request.url.path %}active{% endif %}">
+          <a href="javascript:void(0)" id="subjectToggleBtn" class="sidebar-link flex items-center gap-3 px-3 py-2.5 rounded-xl text-gray-500 hover:bg-gray-50 transition {% if '/subjects' in request.url.path or '/subject/' in request.url.path %}active{% endif %}">
             <i class="fa-solid fa-folder w-5 text-center text-base flex-shrink-0"></i>
             <span class="sidebar-text text-sm font-medium">科目管理</span>
+            <i class="fa-solid fa-chevron-right text-[10px] text-gray-400 ml-auto subject-toggle" id="subjectToggleIcon"></i>
           </a>
-          <div class="sidebar-sub mt-1 ml-4 pl-3 border-l border-gray-100">
+          <div id="subjectSubmenu" class="sidebar-sub collapsed mt-1 ml-4 pl-3 border-l border-gray-100">
             <div class="sidebar-section-label sidebar-text px-1">全部科目</div>
             {% for s in subjects %}
             <a href="/subject/{{ s.id }}" class="subject-item {% if request.url.path == '/subject/' ~ s.id|string %}active{% endif %}" style="{% if '/subject/' ~ s.id|string ~ '?' in request.url.path %}margin-top:0{% endif %}">
@@ -181,6 +184,34 @@
       }
     })();
 
+    // Subject submenu toggle (collapsed by default)
+    (function(){
+      var submenu = document.getElementById('subjectSubmenu');
+      var icon = document.getElementById('subjectToggleIcon');
+      var btn = document.getElementById('subjectToggleBtn');
+      var saved = localStorage.getItem('subjectMenuOpen');
+      var isOpen = saved === 'true';
+      if(isOpen){
+        submenu.classList.remove('collapsed');
+        if(icon) icon.classList.add('open');
+      }
+      if(btn){
+        btn.addEventListener('click', function(e){
+          e.preventDefault();
+          e.stopPropagation();
+          isOpen = !isOpen;
+          if(isOpen){
+            submenu.classList.remove('collapsed');
+            if(icon) icon.classList.add('open');
+          } else {
+            submenu.classList.add('collapsed');
+            if(icon) icon.classList.remove('open');
+          }
+          localStorage.setItem('subjectMenuOpen', String(isOpen));
+        });
+      }
+    })();
+
     // Lightbox
     let lbImages = [];
     let lbIndex = 0;