fix:添加niceGui库美化页面
This commit is contained in:
BIN
ui/assets/icon.ico
Normal file
BIN
ui/assets/icon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
55
ui/assets/style.css
Normal file
55
ui/assets/style.css
Normal 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
15
ui/core/logger.py
Normal 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
13
ui/core/state.py
Normal 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
73
ui/core/task_runner.py
Normal 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
94
ui/views/home_page.py
Normal 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')
|
||||
Reference in New Issue
Block a user