fix:修复模板问题,修复系统操作逻辑,修复系统的一些BUG
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
from nicegui import ui
|
||||
import os
|
||||
from utils.template_utils import get_template_files
|
||||
|
||||
# 修改点 1:统一导入,避免与变量名 config 冲突
|
||||
from config.config import load_config, save_config
|
||||
from config.config import load_config, save_config
|
||||
|
||||
|
||||
def create_config_page():
|
||||
# 修改点 2:将加载逻辑放入页面生成函数内,确保每次刷新页面获取最新值
|
||||
conf_data = load_config("config.toml")
|
||||
template_options = get_template_files()
|
||||
current_filename = os.path.basename(conf_data.get('source_file', ''))
|
||||
current_filename = os.path.basename(conf_data.get("source_file", ""))
|
||||
|
||||
if current_filename and current_filename not in template_options:
|
||||
template_options.append(current_filename)
|
||||
@@ -16,63 +18,148 @@ def create_config_page():
|
||||
ui.add_head_html('<link href="/assets/style.css" rel="stylesheet" />')
|
||||
|
||||
# 样式修正:添加全屏且不滚动条的 CSS
|
||||
ui.add_head_html('''
|
||||
ui.add_head_html(
|
||||
"""
|
||||
<style>
|
||||
body { overflow: hidden; }
|
||||
.main-card { height: calc(100vh - 100px); display: flex; flex-direction: column; }
|
||||
.q-tab-panels { flex-grow: 1; overflow-y: auto !important; }
|
||||
</style>
|
||||
''')
|
||||
"""
|
||||
)
|
||||
|
||||
with ui.header().classes('app-header items-center justify-between shadow-md'):
|
||||
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')
|
||||
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")
|
||||
# 右侧:署名 + 配置按钮
|
||||
with ui.row().classes('items-center gap-4'):
|
||||
ui.label('By 寒寒 | 这里的每一份评语都充满爱意').classes('text-xs opacity-90')
|
||||
ui.button(icon='home', on_click=lambda: ui.navigate.to('/')).props('flat round color=white')
|
||||
with ui.row().classes("items-center gap-4"):
|
||||
ui.label("By 寒寒 | 这里的每一份评语都充满爱意").classes(
|
||||
"text-xs opacity-90"
|
||||
)
|
||||
ui.button(icon="home", on_click=lambda: ui.navigate.to("/")).props(
|
||||
"flat round color=white"
|
||||
)
|
||||
|
||||
# 修改点 3:使用 flex 布局撑满
|
||||
with ui.card().classes('w-full max-w-5xl mx-auto shadow-lg main-card p-0'):
|
||||
with ui.tabs().classes('w-full') as tabs:
|
||||
tab_path = ui.tab('路径设置', icon='folder')
|
||||
tab_class = ui.tab('班级与教师', icon='school')
|
||||
tab_ai = ui.tab('AI 接口配置', icon='psychology')
|
||||
with ui.card().classes("w-full max-w-5xl mx-auto shadow-lg main-card p-0"):
|
||||
with ui.tabs().classes("w-full") as tabs:
|
||||
tab_path = ui.tab("路径设置", icon="folder")
|
||||
tab_class = ui.tab("班级与教师", icon="school")
|
||||
tab_ai = ui.tab("AI 接口配置", icon="psychology")
|
||||
|
||||
with ui.tab_panels(tabs, value=tab_path).classes('w-full flex-grow bg-transparent'):
|
||||
with ui.tab_panels(tabs, value=tab_path).classes(
|
||||
"w-full flex-grow bg-transparent"
|
||||
):
|
||||
# --- 路径设置 ---
|
||||
with ui.tab_panel(tab_path).classes('w-full p-0'):
|
||||
with ui.column().classes('w-full p-4 gap-4'):
|
||||
source_file = ui.select(options=template_options, label='PPT 模板', value=current_filename).props('outlined fill-input').classes('w-full')
|
||||
excel_file = ui.input('Excel 文件', value=os.path.basename(conf_data.get('excel_file', ''))).props('outlined').classes('w-full')
|
||||
image_folder = ui.input('图片目录', value=os.path.basename(conf_data.get('image_folder', ''))).props('outlined').classes('w-full')
|
||||
output_folder = ui.input('输出目录', value=os.path.basename(conf_data.get('output_folder', 'output'))).props('outlined').classes('w-full')
|
||||
with ui.tab_panel(tab_path).classes("w-full p-0"):
|
||||
with ui.column().classes("w-full p-4 gap-4"):
|
||||
source_file = (
|
||||
ui.select(
|
||||
options=template_options,
|
||||
label="PPT 模板",
|
||||
value=current_filename,
|
||||
)
|
||||
.props("outlined fill-input")
|
||||
.classes("w-full")
|
||||
)
|
||||
excel_file = (
|
||||
ui.input(
|
||||
"Excel 文件",
|
||||
value=os.path.basename(conf_data.get("excel_file", "")),
|
||||
)
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
image_folder = (
|
||||
ui.input(
|
||||
"图片目录",
|
||||
value=os.path.basename(conf_data.get("image_folder", "")),
|
||||
)
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
output_folder = (
|
||||
ui.input(
|
||||
"输出目录",
|
||||
value=os.path.basename(
|
||||
conf_data.get("output_folder", "output")
|
||||
),
|
||||
)
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
|
||||
# --- 班级信息 ---
|
||||
with ui.tab_panel(tab_class).classes('w-full p-0'):
|
||||
with ui.column().classes('w-full p-4 gap-4'):
|
||||
class_name = ui.input('班级名称', value=conf_data.get('class_name', '')).props('outlined').classes('w-full')
|
||||
age_group = ui.select(
|
||||
options=['小班上学期', '小班下学期', '中班上学期', '中班下学期', '大班上学期', '大班下学期'],
|
||||
label='年龄段', value=conf_data.get('age_group', '中班上学期')
|
||||
).props('outlined').classes('w-full')
|
||||
teachers_text = ui.textarea('教师名单', value='\n'.join(conf_data.get('teachers', []))).props('outlined').classes('w-full h-40')
|
||||
class_type = ui.select(
|
||||
options={0: '便宜班', 1: '昂贵班'},
|
||||
label='班级类型', value=conf_data.get('class_type', 0)
|
||||
).props('outlined').classes('w-full')
|
||||
with ui.tab_panel(tab_class).classes("w-full p-0"):
|
||||
with ui.column().classes("w-full p-4 gap-4"):
|
||||
class_name = (
|
||||
ui.input("班级名称", value=conf_data.get("class_name", ""))
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
age_group = (
|
||||
ui.select(
|
||||
options=[
|
||||
"小班上学期",
|
||||
"小班下学期",
|
||||
"中班上学期",
|
||||
"中班下学期",
|
||||
"大班上学期",
|
||||
"大班下学期",
|
||||
],
|
||||
label="年龄段",
|
||||
value=conf_data.get("age_group", "中班上学期"),
|
||||
)
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
teachers_text = (
|
||||
ui.textarea(
|
||||
"教师名单", value="\n".join(conf_data.get("teachers", []))
|
||||
)
|
||||
.props("outlined")
|
||||
.classes("w-full h-40")
|
||||
)
|
||||
class_type = (
|
||||
ui.select(
|
||||
options={0: "便宜班", 1: "昂贵班", 2: "昂贵的双木桥班"},
|
||||
label="班级类型",
|
||||
value=conf_data.get("class_type", 0),
|
||||
)
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
|
||||
# --- AI 配置 ---
|
||||
with ui.tab_panel(tab_ai).classes('w-full p-0'):
|
||||
with ui.column().classes('w-full p-4 gap-4'):
|
||||
ai_key = ui.input('API Key', value=conf_data['ai'].get('api_key', '')).props('outlined password').classes('w-full')
|
||||
ai_url = ui.input('API URL', value=conf_data['ai'].get('api_url', '')).props('outlined').classes('w-full')
|
||||
ai_model = ui.input('Model Name', value=conf_data['ai'].get('model', '')).props('outlined').classes('w-full')
|
||||
ai_prompt = ui.textarea('System Prompt', value=conf_data['ai'].get('prompt', '')).props('outlined').classes('w-full h-full')
|
||||
with ui.tab_panel(tab_ai).classes("w-full p-0"):
|
||||
with ui.column().classes("w-full p-4 gap-4"):
|
||||
ai_key = (
|
||||
ui.input("API Key", value=conf_data["ai"].get("api_key", ""))
|
||||
.props("outlined password")
|
||||
.classes("w-full")
|
||||
)
|
||||
ai_url = (
|
||||
ui.input("API URL", value=conf_data["ai"].get("api_url", ""))
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
ai_model = (
|
||||
ui.input("Model Name", value=conf_data["ai"].get("model", ""))
|
||||
.props("outlined")
|
||||
.classes("w-full")
|
||||
)
|
||||
ai_prompt = (
|
||||
ui.textarea(
|
||||
"System Prompt", value=conf_data["ai"].get("prompt", "")
|
||||
)
|
||||
.props("outlined")
|
||||
.classes("w-full h-full")
|
||||
)
|
||||
# 底部固定按钮
|
||||
with ui.row().classes('w-full p-4'):
|
||||
with ui.row().classes("w-full p-4"):
|
||||
|
||||
async def handle_save():
|
||||
new_data = {
|
||||
"source_file": source_file.value,
|
||||
@@ -81,17 +168,21 @@ def create_config_page():
|
||||
"output_folder": output_folder.value,
|
||||
"class_name": class_name.value,
|
||||
"age_group": age_group.value,
|
||||
"teachers": [t.strip() for t in teachers_text.value.split('\n') if t.strip()],
|
||||
"teachers": [
|
||||
t.strip() for t in teachers_text.value.split("\n") if t.strip()
|
||||
],
|
||||
"class_type": class_type.value,
|
||||
"ai": {
|
||||
"api_key": ai_key.value,
|
||||
"api_url": ai_url.value,
|
||||
"model": ai_model.value,
|
||||
"prompt": ai_prompt.value
|
||||
}
|
||||
"prompt": ai_prompt.value,
|
||||
},
|
||||
}
|
||||
# 修改点 4:直接调用导入的 save_config 函数名
|
||||
success, message = save_config(new_data)
|
||||
ui.notify("配置已保存重启生效", type='positive')
|
||||
ui.notify("配置已保存重启生效", type="positive")
|
||||
|
||||
ui.button('保存配置', on_click=handle_save).classes('w-full py-4').props('outline color=primary')
|
||||
ui.button("保存配置", on_click=handle_save).classes("w-full py-4").props(
|
||||
"outline color=primary"
|
||||
)
|
||||
|
||||
@@ -5,25 +5,35 @@ 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, generate_signature
|
||||
generate_template,
|
||||
generate_comment_all,
|
||||
batch_convert_folder,
|
||||
generate_report,
|
||||
generate_zodiac,
|
||||
generate_signature,
|
||||
)
|
||||
from utils.file_utils import export_templates_folder, initialize_project, export_data
|
||||
from utils.file_utils import initialize_project, open_folder
|
||||
|
||||
config = load_config("config.toml")
|
||||
|
||||
|
||||
def create_header():
|
||||
with ui.header().classes('app-header items-center justify-between shadow-md'):
|
||||
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')
|
||||
|
||||
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")
|
||||
|
||||
# 右侧:署名 + 配置按钮
|
||||
with ui.row().classes('items-center gap-4'):
|
||||
ui.label('By 寒寒 | 这里的每一份评语都充满爱意').classes('text-xs opacity-90')
|
||||
with ui.row().classes("items-center gap-4"):
|
||||
ui.label("By 寒寒 | 这里的每一份评语都充满爱意").classes(
|
||||
"text-xs opacity-90"
|
||||
)
|
||||
# 添加配置按钮
|
||||
ui.button(icon='settings', on_click=lambda: ui.navigate.to('/config')).props('flat round color=white').tooltip('系统配置')
|
||||
ui.button(
|
||||
icon="settings", on_click=lambda: ui.navigate.to("/config")
|
||||
).props("flat round color=white").tooltip("系统配置")
|
||||
|
||||
|
||||
def create_home_page():
|
||||
# 1. 引入外部 CSS
|
||||
@@ -32,62 +42,70 @@ def create_home_page():
|
||||
create_header()
|
||||
|
||||
# 主容器
|
||||
with ui.column().classes('w-full max-w-4xl mx-auto p-4 gap-4 thin-scrollbar'):
|
||||
with ui.column().classes("w-full max-w-4xl mx-auto p-4 gap-4 thin-scrollbar"):
|
||||
|
||||
# === 进度条区域 ===
|
||||
with ui.card().classes('func-card'):
|
||||
app_state.progress_label = ui.label('⛳ 任务进度: 待命').classes('font-bold text-gray-700 mb-1')
|
||||
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 颜色变量
|
||||
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.card().classes("func-card card-core"):
|
||||
ui.label("🛠️ 核心功能").classes("section-title text-green")
|
||||
|
||||
with ui.grid(columns=3).classes('w-full gap-3'):
|
||||
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')
|
||||
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)
|
||||
|
||||
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)
|
||||
func_btn('💴 园长一键签名', 'refresh', generate_signature)
|
||||
|
||||
ui.button("📑 格式转换 (PDF)", on_click=run_convert).props("outline")
|
||||
func_btn("🐂 生肖转化 (生日)", "pets", generate_zodiac)
|
||||
func_btn("💴 园长一键签名", "refresh", generate_signature)
|
||||
|
||||
# === 下方双栏布局 ===
|
||||
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)
|
||||
# 数据管理
|
||||
with ui.card().classes("func-card card-data"):
|
||||
ui.label("⚙️ 系统操作").classes("section-title text-blue")
|
||||
|
||||
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")
|
||||
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')
|
||||
with ui.row().classes("w-full"):
|
||||
ui.button(
|
||||
"📦 打开输出文件夹",
|
||||
on_click=lambda: open_folder(config.get("output_folder")),
|
||||
).props(f"outline")
|
||||
ui.button(
|
||||
"📤 打开数据文件夹",
|
||||
on_click=lambda: open_folder(config.get("data_folder")),
|
||||
).props(f"outline")
|
||||
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')
|
||||
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