// Package webui 渲染采购服务当前可用的服务端页面。 package webui import ( "embed" "fmt" "html/template" "io" "time" "cmbuyer/admin/internal/taskdetail" "cmbuyer/admin/internal/tasks" ) //go:embed templates/*.html var templateFiles embed.FS //go:embed static/tasks.js var tasksScript []byte var shanghaiLocation = time.FixedZone("Asia/Shanghai", 8*60*60) var templates = template.Must(template.New("webui").Funcs(template.FuncMap{ "list": func(values ...any) []any { return values }, "statusLabel": statusLabel, "shanghaiDateTime": func(value time.Time) string { return value.In(shanghaiLocation).Format(time.RFC3339) }, "shanghaiTime": func(value time.Time) string { return value.In(shanghaiLocation).Format("2006-01-02 15:04") }, "canonicalURL": tasks.CanonicalURL, "formatBytes": formatBytes, "taskSafetyTitle": taskSafetyTitle, "taskSafetyText": taskSafetyText, "authorizationStatusLabel": authorizationStatusLabel, "attemptStatusLabel": attemptStatusLabel, "submissionStatusLabel": submissionStatusLabel, "evidenceKindLabel": evidenceKindLabel, }).ParseFS(templateFiles, "templates/*.html")) // LoginData 是登录页面所需的非敏感展示数据。 type LoginData struct { CSRFToken string ReturnTo string Username string Error string } // TasksData 是受保护的建单与任务工作台页面所需数据。 type TasksData struct { CSRFToken string Tasks []tasks.TaskRow Filter tasks.TaskFilter FilterErrors tasks.Errors HasFilter bool StartKey string Form tasks.Form Errors tasks.Errors OpenForm bool FullPage bool FocusField string Success bool } type TaskDetailData struct{ Detail taskdetail.Detail } // RenderLogin 写入登录页。 func RenderLogin(writer io.Writer, data LoginData) error { return templates.ExecuteTemplate(writer, "login.html", data) } // RenderTasks 写入登录后的受保护任务页。 func RenderTasks(writer io.Writer, data TasksData) error { return templates.ExecuteTemplate(writer, "tasks.html", data) } func RenderTaskDetailPage(writer io.Writer, data TaskDetailData) error { return templates.ExecuteTemplate(writer, "task-detail-page.html", data) } func RenderTaskDetailFragment(writer io.Writer, data TaskDetailData) error { return templates.ExecuteTemplate(writer, "task-detail-content", data) } func TasksScript() []byte { return tasksScript } func statusLabel(status string) string { labels := map[string]string{ "DRAFT": "待开始", "PENDING": "已授权待领取", "CLAIMED": "已领取", "ORDERING": "执行中", "NEEDS_MANUAL": "待人工处理", "WAITING_PAYMENT": "待付款", "RECONCILIATION_REQUIRED": "围栏后待调和", "SUCCEEDED": "已完成", "FAILED": "失败", "CANCELED": "已取消", } if label, ok := labels[status]; ok { return label } return "未知状态" } func taskSafetyTitle(status string) string { if status == "WAITING_PAYMENT" { return "订单已创建,系统尚未付款。" } if status == "RECONCILIATION_REQUIRED" { return "订单可能已创建,只能调和同一提交。" } return "系统只创建待付款订单,不会自动付款。" } func taskSafetyText(status string) string { if status == "DRAFT" { return "创建任务不构成授权;请回到列表勾选后开始采购。" } if status == "RECONCILIATION_REQUIRED" { return "围栏保持占用,禁止重新授权、再次提交或释放。" } return "截图只供内部审计,不替代实时价格闸门,也不会触发设备动作。" } func authorizationStatusLabel(status string) string { labels := map[string]string{"ACTIVE": "授权有效", "CLAIMED": "已被领取", "FENCED": "提交围栏已建立", "CONSUMED": "授权已消费", "EXPIRED": "授权已过期", "ABANDONED": "授权已关闭"} if value, ok := labels[status]; ok { return value } return "未知授权状态" } func attemptStatusLabel(status string) string { labels := map[string]string{"CLAIMED": "已领取", "ORDERING": "执行中", "FAILED": "围栏前失败", "FENCED": "已建立围栏", "ABANDONED": "已安全停止"} if value, ok := labels[status]; ok { return value } return "未知执行状态" } func submissionStatusLabel(status string) string { labels := map[string]string{"FENCED": "围栏已建立", "SUBMITTED": "已创建待付款订单", "RECONCILIATION_REQUIRED": "结果待调和", "MANUAL_RESOLVED": "已人工调和"} if value, ok := labels[status]; ok { return value } return "未知提交状态" } func evidenceKindLabel(kind string) string { if kind == "SKU_PANEL_GATE_1" { return "规格面板 · 闸门一" } return "内部截图" } func formatBytes(value int64) string { if value >= 1<<20 { return fmt.Sprintf("%.1f MiB", float64(value)/(1<<20)) } if value >= 1<<10 { return fmt.Sprintf("%.1f KiB", float64(value)/(1<<10)) } return fmt.Sprintf("%d B", value) }