package blueprint import ( "encoding/json" "strings" ) // defaultImportExportOps Z9:业务表默认允许导入/导出。 var defaultImportExportOps = []string{"import", "export"} // defaultListActions Z9:列表页默认工具栏(含导入)。 var defaultListActions = []string{"create", "edit", "delete", "export", "import", "refresh"} // EnsureDefaultImportExport 发布/合并兜底(Z9d): // 可写业务 resource 缺 import/export 则补上;list 页 actions 缺 import 则补上。 // 跳过:只读 resource(仅 list/get);meta.ui.disable_default_import == true。 func (bp *Blueprint) EnsureDefaultImportExport() { if bp == nil { return } if disableDefaultImport(bp) { return } for i := range bp.Apis.Resources { r := &bp.Apis.Resources[i] if resourceReadOnly(r.Operations) { continue } r.Operations = ensureOpList(r.Operations, defaultImportExportOps...) } for i := range bp.Pages { p := &bp.Pages[i] if !strings.EqualFold(strings.TrimSpace(p.Type), "list") { continue } if p.Layout == nil { p.Layout = &PageLayout{} } p.Layout.Actions = ensureListActions(p.Layout.Actions) if p.Layout.ActionLabels == nil { p.Layout.ActionLabels = map[string]string{} } if _, ok := p.Layout.ActionLabels["import"]; !ok { p.Layout.ActionLabels["import"] = "导入 Excel" } if _, ok := p.Layout.ActionLabels["export"]; !ok { p.Layout.ActionLabels["export"] = "导出 Excel" } } } func disableDefaultImport(bp *Blueprint) bool { if len(bp.Meta.UI) == 0 { return false } var ui map[string]any if err := json.Unmarshal(bp.Meta.UI, &ui); err != nil { return false } v, ok := ui["disable_default_import"] if !ok { return false } switch t := v.(type) { case bool: return t case string: return strings.EqualFold(t, "true") || t == "1" default: return false } } func resourceReadOnly(ops []string) bool { if len(ops) == 0 { return false } writable := false for _, op := range ops { switch strings.ToLower(strings.TrimSpace(op)) { case "create", "update", "delete", "import", "export": writable = true } } // 仅 list/get → 只读,不强加 import if !writable { onlyRead := true for _, op := range ops { switch strings.ToLower(strings.TrimSpace(op)) { case "list", "get", "": default: onlyRead = false } } return onlyRead } return false } func ensureOpList(ops []string, add ...string) []string { seen := map[string]struct{}{} out := make([]string, 0, len(ops)+len(add)) for _, op := range ops { op = strings.ToLower(strings.TrimSpace(op)) if op == "" { continue } if _, ok := seen[op]; ok { continue } seen[op] = struct{}{} out = append(out, op) } for _, op := range add { op = strings.ToLower(strings.TrimSpace(op)) if op == "" { continue } if _, ok := seen[op]; ok { continue } seen[op] = struct{}{} out = append(out, op) } return out } func ensureListActions(actions []string) []string { if len(actions) == 0 { return append([]string{}, defaultListActions...) } seen := map[string]struct{}{} out := make([]string, 0, len(actions)+2) for _, a := range actions { a = strings.ToLower(strings.TrimSpace(a)) if a == "" { continue } if _, ok := seen[a]; ok { continue } seen[a] = struct{}{} out = append(out, a) } for _, need := range []string{"import", "export"} { if _, ok := seen[need]; ok { continue } // 插在 refresh 前;若无 refresh 则追加 inserted := false for i, a := range out { if a == "refresh" { out = append(out[:i], append([]string{need}, out[i:]...)...) seen[need] = struct{}{} inserted = true break } } if !inserted { out = append(out, need) seen[need] = struct{}{} } } return out }