fix:添加niceGui库美化页面

This commit is contained in:
2025-12-13 21:13:25 +08:00
parent 93d1e8687a
commit 3a4a9df751
14 changed files with 1555 additions and 41 deletions

BIN
ui/assets/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

55
ui/assets/style.css Normal file
View File

@@ -0,0 +1,55 @@
/* assets/style.css */
/* 全局字体 */
body {
font-family: "微软雅黑", "Microsoft YaHei", sans-serif;
background-color: #f0f4f8;
}
/* 标题栏 */
.app-header {
background-color: #2E8B57; /* SeaGreen */
color: white;
}
/* 卡片通用样式 */
.func-card {
width: 100%;
padding: 1rem;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
background-color: white;
border-radius: 0.5rem;
}
/* 核心功能区顶部边框 */
.card-core {
border-top: 4px solid #16a34a; /* green-600 */
}
/* 数据管理区顶部边框 */
.card-data {
border-top: 4px solid #3b82f6; /* blue-500 */
}
/* 系统操作区顶部边框 */
.card-system {
border-top: 4px solid #ef4444; /* red-500 */
}
.card-logging {
border-top: 4px solid #9c1be0;
}
/* 标题文字 */
.section-title {
font-size: 1.125rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
/* 绿色标题 */
.text-green { color: #166534; }
/* 蓝色标题 */
.text-blue { color: #1e40af; }
/* 红色标题 */
.text-red { color: #991b1b; }

15
ui/core/logger.py Normal file
View File

@@ -0,0 +1,15 @@
import sys
from loguru import logger
from ui.core.state import app_state
class GuiLogger:
def write(self, message):
if app_state.log_element:
app_state.log_element.push(message.strip())
def setup_logger():
logger.remove()
# 控制台输出
logger.add(sys.stderr, format="{time:HH:mm:ss} | {level} | {message}")
# GUI 输出
logger.add(GuiLogger(), format="{time:HH:mm:ss} | {level} | {message}", level="INFO")

13
ui/core/state.py Normal file
View File

@@ -0,0 +1,13 @@
import threading
class AppState:
def __init__(self):
self.stop_event = threading.Event()
self.is_running = False
# 这些 UI 元素的引用将在 UI 初始化时被赋值
self.progress_bar = None
self.progress_label = None
self.log_element = None
# 创建全局单例
app_state = AppState()

73
ui/core/task_runner.py Normal file
View File

@@ -0,0 +1,73 @@
import tkinter as tk
from tkinter import filedialog
from nicegui import ui, run
from loguru import logger
from ui.core.state import app_state
async def select_folder():
"""在 Native 模式下弹窗选择文件夹"""
def _pick():
root = tk.Tk()
root.withdraw()
root.attributes("-topmost", True)
path = filedialog.askdirectory()
root.destroy()
return path
return await run.io_bound(_pick)
async def run_task(func, *args, **kwargs):
"""通用任务执行器"""
if app_state.is_running:
ui.notify("当前有任务正在运行,请稍候...", type="warning")
return
# 1. 状态重置
app_state.is_running = True
app_state.stop_event.clear()
if app_state.progress_bar: app_state.progress_bar.set_value(0)
if app_state.progress_label: app_state.progress_label.set_text("🚀 正在启动任务...")
# 2. 定义进度条回调
def progress_callback(current, total, task_name="任务"):
if total <= 0:
pct = 0
text = f"{task_name}: 准备中..."
else:
pct = current / total
text = f"{task_name}: {current}/{total} ({int(pct * 100)}%)"
# 更新 UI
if app_state.progress_bar: app_state.progress_bar.set_value(pct)
if app_state.progress_label: app_state.progress_label.set_text(text)
# 3. 组装参数
kwargs['progress_callback'] = progress_callback
kwargs['stop_event'] = app_state.stop_event
# 4. 执行
try:
# 适配器:检查函数是否接受 stop_event
def _exec():
try:
func(*args, **kwargs)
except TypeError:
kwargs.pop('stop_event', None)
func(*args, **kwargs)
await run.io_bound(_exec)
if app_state.progress_label: app_state.progress_label.set_text("✅ 任务完成")
ui.notify("任务执行成功!", type="positive")
except Exception as e:
logger.error(f"任务出错: {e}")
if app_state.progress_label: app_state.progress_label.set_text(f"❌ 错误: {str(e)}")
ui.notify(f"任务失败: {e}", type="negative")
finally:
app_state.is_running = False
if app_state.progress_bar: app_state.progress_bar.set_value(0)

94
ui/views/home_page.py Normal file
View File

@@ -0,0 +1,94 @@
from nicegui import ui, app
from config.config import load_config
from ui.core.state import app_state
from ui.core.task_runner import run_task, select_folder
# 导入业务函数
from utils.generate_utils import (
generate_template, generate_comment_all,
batch_convert_folder, generate_report, generate_zodiac
)
from utils.file_utils import export_templates_folder, initialize_project, export_data
config = load_config("config.toml")
def create_header():
with ui.header().classes('app-header items-center justify-between shadow-md'):
with ui.row().classes('items-center gap-2'):
ui.image('/assets/icon.ico').classes('w-8 h-8').props('fit=contain')
ui.label('尚城幼儿园成长报告助手').classes('text-xl font-bold')
ui.label('By 寒寒 | 这里的每一份评语都充满爱意').classes('text-xs opacity-90')
def create_page():
# 1. 引入外部 CSS
ui.add_head_html('<link href="/assets/style.css" rel="stylesheet" />')
create_header()
# 主容器
with ui.column().classes('w-full max-w-4xl mx-auto p-4 gap-4'):
# === 进度条区域 ===
with ui.card().classes('func-card'):
app_state.progress_label = ui.label('⛳ 任务进度: 待命').classes('font-bold text-gray-700 mb-1')
# 使用 NiceGUI 原生属性配合 CSS 类
app_state.progress_bar = ui.linear_progress(value=0, show_value=False).classes('h-4 rounded')
app_state.progress_bar.props('color=positive') # 使用 Quasar 颜色变量
# === 核心功能区 ===
with ui.card().classes('func-card card-core'):
ui.label('🛠️ 核心功能').classes('section-title text-green')
with ui.grid(columns=3).classes('w-full gap-3'):
# 辅助函数:快速创建按钮
def func_btn(text, icon, func):
ui.button(text, on_click=lambda: run_task(func)).props(f'outline').classes('w-full')
func_btn('📁 生成图片路径', 'image', generate_template)
func_btn('🤖 生成评语 (AI)', 'smart_toy', generate_comment_all)
func_btn('📊 生成报告 (PPT)', 'analytics', generate_report)
# 特殊处理带参数的
async def run_convert():
await run_task(batch_convert_folder, config.get("output_folder"))
ui.button('📑 格式转换 (PDF)', on_click=run_convert).props('outline')
func_btn('🐂 生肖转化 (生日)', 'pets', generate_zodiac)
# === 下方双栏布局 ===
with ui.grid(columns=2).classes('w-full gap-4'):
# 数据管理
with ui.card().classes('func-card card-data'):
ui.label('📦 数据管理').classes('section-title text-blue')
with ui.row().classes('w-full'):
async def do_export(func):
path = await select_folder()
if path: await run_task(func, path)
ui.button('📦 导出模板', on_click=lambda: do_export(export_templates_folder)).props(f'outline')
ui.button('📤 导出备份', on_click=lambda: do_export(export_data)).props(f'outline')
# 系统操作
with ui.card().classes('func-card card-system'):
ui.label('⚙️ 系统操作').classes('section-title text-red')
with ui.row().classes('w-full'):
def stop_now():
if app_state.is_running:
app_state.stop_event.set()
ui.notify("发送停止信号...", type="warning")
ui.button('⛔ 停止', on_click=stop_now).props('color=negative').classes('flex-1')
async def reset_sys():
await run_task(initialize_project)
ui.button('⚠️ 初始化', on_click=reset_sys).props('outline color=warning').classes('flex-1')
# === 日志区 ===
with ui.card().classes('func-card card-logging'):
with ui.expansion('📝 系统实时日志',value=True).classes('w-full bg-white shadow-sm rounded'):
app_state.log_element = ui.log(max_lines=200).classes('w-full h-40 font-mono text-xs bg-gray-100 p-2')