AIDeveloper-PC/requirements_generator/ui/display.py

520 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ui/display.py - 所有 Rich 表格 / 面板展示函数
import os
from typing import Dict, List
from rich.console import Console
from rich.panel import Panel
from rich.rule import Rule
from rich.table import Table
import config
from constants import CHG_TYPE_LABEL, CHG_TYPE_COLOR
from database.models import (
ChangeHistory, FunctionalRequirement, Project,
)
console = Console()
def print_banner() -> None:
console.print()
console.print(Panel.fit(
"[bold cyan]🚀 需求分析 & 代码生成工具[/bold cyan]\n"
"[dim]Powered by LLM · SQLite · Python[/dim]",
border_style="cyan",
))
console.print()
def print_main_menu() -> None:
console.print(Rule("[bold cyan]主菜单[/bold cyan]"))
menu_items = [
("1", "📁 新建项目", "输入需求 → 分解 → 生成代码"),
("2", "🔄 变更项目需求", "变更已有需求 / 变更模块需求 / 新增需求"),
("3", "📋 查看所有项目", "列表展示全部项目"),
("4", "🔍 查看项目详情", "需求列表 / 模块分组 / 变更历史"),
("5", "🗑 删除指定项目", "删除项目及其所有数据"),
("0", "🚪 退出", ""),
]
table = Table(show_header=False, box=None, padding=(0, 2))
table.add_column("选项", style="bold yellow", width=4)
table.add_column("功能", style="bold", width=22)
table.add_column("说明", style="dim", width=38)
for key, name, desc in menu_items:
table.add_row(f"[{key}]", name, desc)
console.print(table)
console.print()
def print_projects_table(projects: List[Project]) -> None:
if not projects:
console.print("[yellow] 暂无项目记录。[/yellow]")
return
table = Table(title="📋 项目列表", show_lines=True)
table.add_column("ID", style="cyan", width=6)
table.add_column("项目名", style="bold", width=22)
table.add_column("语言", style="magenta", width=12)
table.add_column("描述", width=35)
table.add_column("输出目录", style="dim", width=30)
for p in projects:
desc = p.description or ""
table.add_row(
str(p.id), p.name, p.language or "-",
desc[:40] + ("..." if len(desc) > 40 else ""),
p.output_dir or "-",
)
console.print(table)
def print_functional_requirements(reqs: List[FunctionalRequirement]) -> None:
if not reqs:
console.print("[yellow] 暂无功能需求。[/yellow]")
return
priority_color = {"high": "red", "medium": "yellow", "low": "green"}
status_color = {
"pending": "yellow",
"generated": "green",
"failed": "red",
"deleted": "dim",
}
table = Table(title="📋 功能需求列表", show_lines=True)
table.add_column("序号", style="cyan", width=6)
table.add_column("ID", style="dim", width=6)
table.add_column("模块", style="magenta", width=15)
table.add_column("标题", style="bold", width=18)
table.add_column("函数名", width=25)
table.add_column("优先级", width=8)
table.add_column("状态", width=10)
table.add_column("描述", width=35)
for req in reqs:
pc = priority_color.get(req.priority, "white")
sc = status_color.get(req.status, "white")
desc = req.description
table.add_row(
str(req.index_no),
str(req.id) if req.id else "-",
req.module or config.DEFAULT_MODULE,
req.title,
f"[code]{req.function_name}[/code]",
f"[{pc}]{req.priority}[/{pc}]",
f"[{sc}]{req.status}[/{sc}]",
desc[:40] + "..." if len(desc) > 40 else desc,
)
console.print(table)
def print_module_summary(reqs: List[FunctionalRequirement]) -> None:
module_map: Dict[str, List[str]] = {}
for req in reqs:
if req.status == "deleted":
continue
m = req.module or config.DEFAULT_MODULE
module_map.setdefault(m, []).append(req.function_name)
table = Table(title="📦 功能模块分组", show_lines=True)
table.add_column("模块", style="magenta bold", width=20)
table.add_column("函数数量", style="cyan", width=8)
table.add_column("函数列表", width=50)
for module, funcs in sorted(module_map.items()):
table.add_row(module, str(len(funcs)), ", ".join(funcs))
console.print(table)
def print_module_list(reqs: List[FunctionalRequirement]) -> List[str]:
"""打印带序号的模块列表(跳过已删除需求),返回有序模块名列表。"""
module_map: Dict[str, List[FunctionalRequirement]] = {}
for req in reqs:
if req.status == "deleted":
continue
m = req.module or config.DEFAULT_MODULE
module_map.setdefault(m, []).append(req)
table = Table(title="📦 模块列表", show_lines=True)
table.add_column("序号", style="cyan", width=6)
table.add_column("模块名", style="magenta bold", width=22)
table.add_column("需求数量", style="yellow", width=8)
table.add_column("包含函数", width=45)
modules = sorted(module_map.keys())
for i, module in enumerate(modules, 1):
funcs = [r.function_name for r in module_map[module]]
table.add_row(str(i), module, str(len(funcs)), ", ".join(funcs))
console.print(table)
return modules
def print_signatures_preview(signatures: List[dict]) -> None:
table = Table(title="📄 函数签名预览", show_lines=True)
table.add_column("需求编号", style="cyan", width=8)
table.add_column("模块", style="magenta", width=15)
table.add_column("函数名", style="bold", width=22)
table.add_column("参数数", width=6)
table.add_column("返回类型", width=10)
table.add_column("URL", style="dim", width=28)
for sig in signatures:
ret = sig.get("return") or {}
url = sig.get("url", "")
url_display = os.path.basename(url) if url else "[dim]待生成[/dim]"
table.add_row(
sig.get("requirement_id", "-"),
sig.get("module", "-"),
sig.get("name", "-"),
str(len(sig.get("parameters", {}))),
ret.get("type", "void"),
url_display,
)
console.print(table)
def print_change_history(histories: List[ChangeHistory]) -> None:
"""以表格形式展示变更历史,含变更类型、模块、需求 ID 等字段。"""
if not histories:
console.print("[dim] 暂无变更历史。[/dim]")
return
table = Table(title="🕒 变更历史", show_lines=True)
table.add_column("ID", style="cyan", width=6)
table.add_column("时间", style="dim", width=20)
table.add_column("变更类型", width=14)
table.add_column("模块", width=16)
table.add_column("需求ID", width=8)
table.add_column("变更摘要", width=50)
for h in histories:
ct = getattr(h, "change_type", "") or ""
label = CHG_TYPE_LABEL.get(ct, ct)
color = CHG_TYPE_COLOR.get(ct, "white")
mod = getattr(h, "module", "") or "-"
rid = str(getattr(h, "req_id", "") or "-")
ts = str(getattr(h, "created_at", "-"))
summary = h.changes
if len(summary) > 60:
summary = summary[:60] + "..."
table.add_row(
str(h.id), ts,
f"[{color}]{label}[/{color}]",
mod, rid, summary,
)
console.print(table)
def print_req_diff(
old_reqs: List[FunctionalRequirement],
new_reqs: List[FunctionalRequirement],
) -> None:
"""并排展示新旧需求对比表格。"""
console.print()
console.print(Rule("[bold]新旧需求对比[/bold]"))
old_table = Table(
title="❌ 旧需求(将被替换)", show_lines=True, border_style="red",
)
old_table.add_column("ID", style="dim", width=6)
old_table.add_column("标题", style="bold", width=18)
old_table.add_column("函数名", width=22)
old_table.add_column("描述", width=30)
for r in old_reqs:
old_table.add_row(
str(r.id), r.title, r.function_name,
r.description[:30] + "..." if len(r.description) > 30 else r.description,
)
console.print(old_table)
new_table = Table(
title="✅ 新需求(将被创建)", show_lines=True, border_style="green",
)
new_table.add_column("序号", style="cyan", width=6)
new_table.add_column("标题", style="bold", width=18)
new_table.add_column("函数名", width=22)
new_table.add_column("描述", width=30)
for r in new_reqs:
new_table.add_row(
str(r.index_no), r.title, r.function_name,
r.description[:30] + "..." if len(r.description) > 30 else r.description,
)
console.print(new_table)
console.print()# ui/display.py - 所有 Rich 表格 / 面板展示函数
import os
from typing import Dict, List
from rich.console import Console
from rich.panel import Panel
from rich.rule import Rule
from rich.table import Table
import config
from constants import CHG_TYPE_LABEL, CHG_TYPE_COLOR
from database.models import (
ChangeHistory, FunctionalRequirement, Project,
)
console = Console()
def print_banner() -> None:
console.print()
console.print(Panel.fit(
"[bold cyan]🚀 需求分析 & 代码生成工具[/bold cyan]\n"
"[dim]Powered by LLM · SQLite · Python[/dim]",
border_style="cyan",
))
console.print()
def print_main_menu() -> None:
console.print(Rule("[bold cyan]主菜单[/bold cyan]"))
menu_items = [
("1", "📁 新建项目", "输入需求 → 分解 → 生成代码"),
("2", "🔄 变更项目需求", "变更已有需求 / 变更模块需求 / 新增需求"),
("3", "📋 查看所有项目", "列表展示全部项目"),
("4", "🔍 查看项目详情", "需求列表 / 模块分组 / 变更历史"),
("5", "🗑 删除指定项目", "删除项目及其所有数据"),
("0", "🚪 退出", ""),
]
table = Table(show_header=False, box=None, padding=(0, 2))
table.add_column("选项", style="bold yellow", width=4)
table.add_column("功能", style="bold", width=22)
table.add_column("说明", style="dim", width=38)
for key, name, desc in menu_items:
table.add_row(f"[{key}]", name, desc)
console.print(table)
console.print()
def print_projects_table(projects: List[Project]) -> None:
if not projects:
console.print("[yellow] 暂无项目记录。[/yellow]")
return
table = Table(title="📋 项目列表", show_lines=True)
table.add_column("ID", style="cyan", width=6)
table.add_column("项目名", style="bold", width=22)
table.add_column("语言", style="magenta", width=12)
table.add_column("描述", width=35)
table.add_column("输出目录", style="dim", width=30)
for p in projects:
desc = p.description or ""
table.add_row(
str(p.id), p.name, p.language or "-",
desc[:40] + ("..." if len(desc) > 40 else ""),
p.output_dir or "-",
)
console.print(table)
def print_functional_requirements(reqs: List[FunctionalRequirement]) -> None:
if not reqs:
console.print("[yellow] 暂无功能需求。[/yellow]")
return
priority_color = {"high": "red", "medium": "yellow", "low": "green"}
status_color = {
"pending": "yellow",
"generated": "green",
"failed": "red",
"deleted": "dim",
}
table = Table(title="📋 功能需求列表", show_lines=True)
table.add_column("序号", style="cyan", width=6)
table.add_column("ID", style="dim", width=6)
table.add_column("模块", style="magenta", width=15)
table.add_column("标题", style="bold", width=18)
table.add_column("函数名", width=25)
table.add_column("优先级", width=8)
table.add_column("状态", width=10)
table.add_column("描述", width=35)
for req in reqs:
pc = priority_color.get(req.priority, "white")
sc = status_color.get(req.status, "white")
desc = req.description
table.add_row(
str(req.index_no),
str(req.id) if req.id else "-",
req.module or config.DEFAULT_MODULE,
req.title,
f"[code]{req.function_name}[/code]",
f"[{pc}]{req.priority}[/{pc}]",
f"[{sc}]{req.status}[/{sc}]",
desc[:40] + "..." if len(desc) > 40 else desc,
)
console.print(table)
def print_module_summary(reqs: List[FunctionalRequirement]) -> None:
module_map: Dict[str, List[str]] = {}
for req in reqs:
if req.status == "deleted":
continue
m = req.module or config.DEFAULT_MODULE
module_map.setdefault(m, []).append(req.function_name)
table = Table(title="📦 功能模块分组", show_lines=True)
table.add_column("模块", style="magenta bold", width=20)
table.add_column("函数数量", style="cyan", width=8)
table.add_column("函数列表", width=50)
for module, funcs in sorted(module_map.items()):
table.add_row(module, str(len(funcs)), ", ".join(funcs))
console.print(table)
def print_module_list(reqs: List[FunctionalRequirement]) -> List[str]:
"""打印带序号的模块列表(跳过已删除需求),返回有序模块名列表。"""
module_map: Dict[str, List[FunctionalRequirement]] = {}
for req in reqs:
if req.status == "deleted":
continue
m = req.module or config.DEFAULT_MODULE
module_map.setdefault(m, []).append(req)
table = Table(title="📦 模块列表", show_lines=True)
table.add_column("序号", style="cyan", width=6)
table.add_column("模块名", style="magenta bold", width=22)
table.add_column("需求数量", style="yellow", width=8)
table.add_column("包含函数", width=45)
modules = sorted(module_map.keys())
for i, module in enumerate(modules, 1):
funcs = [r.function_name for r in module_map[module]]
table.add_row(str(i), module, str(len(funcs)), ", ".join(funcs))
console.print(table)
return modules
def print_signatures_preview(signatures: List[dict]) -> None:
table = Table(title="📄 函数签名预览", show_lines=True)
table.add_column("需求编号", style="cyan", width=8)
table.add_column("模块", style="magenta", width=15)
table.add_column("函数名", style="bold", width=22)
table.add_column("参数数", width=6)
table.add_column("返回类型", width=10)
table.add_column("URL", style="dim", width=28)
for sig in signatures:
ret = sig.get("return") or {}
url = sig.get("url", "")
url_display = os.path.basename(url) if url else "[dim]待生成[/dim]"
table.add_row(
sig.get("requirement_id", "-"),
sig.get("module", "-"),
sig.get("name", "-"),
str(len(sig.get("parameters", {}))),
ret.get("type", "void"),
url_display,
)
console.print(table)
def print_change_history(histories: List[ChangeHistory]) -> None:
"""以表格形式展示变更历史,含变更类型、模块、需求 ID 等字段。"""
if not histories:
console.print("[dim] 暂无变更历史。[/dim]")
return
table = Table(title="🕒 变更历史", show_lines=True)
table.add_column("ID", style="cyan", width=6)
table.add_column("时间", style="dim", width=20)
table.add_column("变更类型", width=14)
table.add_column("模块", width=16)
table.add_column("需求ID", width=8)
table.add_column("变更摘要", width=50)
for h in histories:
ct = getattr(h, "change_type", "") or ""
label = CHG_TYPE_LABEL.get(ct, ct)
color = CHG_TYPE_COLOR.get(ct, "white")
mod = getattr(h, "module", "") or "-"
rid = str(getattr(h, "req_id", "") or "-")
ts = str(getattr(h, "created_at", "-"))
summary = h.changes
if len(summary) > 60:
summary = summary[:60] + "..."
table.add_row(
str(h.id), ts,
f"[{color}]{label}[/{color}]",
mod, rid, summary,
)
console.print(table)
def print_req_diff(
old_reqs: List[FunctionalRequirement],
new_reqs: List[FunctionalRequirement],
) -> None:
"""并排展示新旧需求对比表格。"""
console.print()
console.print(Rule("[bold]新旧需求对比[/bold]"))
old_table = Table(
title="❌ 旧需求(将被替换)", show_lines=True, border_style="red",
)
old_table.add_column("ID", style="dim", width=6)
old_table.add_column("标题", style="bold", width=18)
old_table.add_column("函数名", width=22)
old_table.add_column("描述", width=30)
for r in old_reqs:
old_table.add_row(
str(r.id), r.title, r.function_name,
r.description[:30] + "..." if len(r.description) > 30 else r.description,
)
console.print(old_table)
new_table = Table(
title="✅ 新需求(将被创建)", show_lines=True, border_style="green",
)
new_table.add_column("序号", style="cyan", width=6)
new_table.add_column("标题", style="bold", width=18)
new_table.add_column("函数名", width=22)
new_table.add_column("描述", width=30)
for r in new_reqs:
new_table.add_row(
str(r.index_no), r.title, r.function_name,
r.description[:30] + "..." if len(r.description) > 30 else r.description,
)
console.print(new_table)
console.print()
def display_change_history(histories: list[dict]) -> None:
"""
展示变更历史表格。
Args:
histories: get_change_histories() 返回的 dict 列表,
每条包含 id / change_time / status /
change_type / module / req_id / summary
"""
if not histories:
console.print("[yellow]暂无变更历史记录。[/yellow]")
return
table = Table(
title="变更历史",
box=box.ROUNDED,
show_lines=True,
header_style="bold cyan",
)
table.add_column("ID", style="dim", width=6)
table.add_column("变更时间", style="white", width=20)
table.add_column("类型", style="cyan", width=14)
table.add_column("模块", style="green", width=16)
table.add_column("需求 ID", style="yellow", width=8)
table.add_column("摘要", style="white", width=40)
table.add_column("状态", style="magenta",width=10)
for h in histories:
# histories 已是 dict由 get_change_histories 反序列化)
change_time = h.get("change_time")
time_str = (
change_time.strftime("%Y-%m-%d %H:%M:%S")
if hasattr(change_time, "strftime")
else str(change_time or "")
)
status = h.get("status", "")
status_style = {
"pending": "[yellow]pending[/yellow]",
"confirmed": "[green]confirmed[/green]",
}.get(status, status)
table.add_row(
str(h.get("id", "")),
time_str,
str(h.get("change_type", "")),
str(h.get("module", "")),
str(h.get("req_id") or ""),
str(h.get("summary", ""))[:60],
status_style,
)
console.print(table)