package export import ( "database/sql" "io" "strings" "github.com/xuri/excelize/v2" ) // EventResultRow 是分项成绩表的一行。 // Rank 用字符串是为了承载「1(并列)」这种展示值。 type EventResultRow struct { Rank string Bib string TeamName string Participant string ResultText string Unit string Points float64 Status string Remark string } // EventSheet 是一个小项的一个组别(无组别时 Category 为空)对应的一张表。 type EventSheet struct { SportName string EventName string Category string Rows []EventResultRow } // EventsWorkbook 是「分项成绩表」的内容:一个小项可含多个组别 sheet, // 也可承载全部小项(ExportAllXLSX 用)。 type EventsWorkbook struct { Meta Meta Sheets []EventSheet } var eventColumns = []column{ {"名次", 8, kindCenter}, {"号码", 9, kindCenter}, {"单位", 22, kindText}, {"运动员/队", 18, kindText}, {"成绩", 12, kindCenter}, {"积分", 8, kindNumber}, {"状态", 8, kindCenter}, {"备注", 20, kindText}, } // ExportEventXLSX 导出单个小项的成绩表;eventID 为 0 时导出全部小项。 func ExportEventXLSX(db *sql.DB, eventID int64, dst string, opts Options) error { wb, err := LoadEventWorkbook(db, eventID, opts) if err != nil { return err } f, err := eventFile(wb) if err != nil { return err } return saveWorkbook(f, dst) } // WriteEventXLSX 把分项成绩表写成 xlsx 流。 func WriteEventXLSX(w io.Writer, wb EventsWorkbook) error { f, err := eventFile(wb) if err != nil { return err } return f.Write(w) } func eventFile(wb EventsWorkbook) (*excelize.File, error) { return buildWorkbook("成绩表", func(f *excelize.File, st *styleSet) error { return writeEventSheets(f, st, wb.Meta, wb.Sheets, &sheetNamer{}) }) } func writeEventSheets(f *excelize.File, st *styleSet, meta Meta, sheets []EventSheet, namer *sheetNamer) error { meta = meta.normalized() for _, sh := range sheets { name := namer.add(eventSheetBase(sh)) if err := addSheet(f, name); err != nil { return err } if err := writeSheet(f, st, name, meta, eventSheetTitle(meta, sh), eventColumns, eventRows(sh.Rows)); err != nil { return err } } return nil } // eventSheetBase 是工作表名:大项·小项·组别,够短且能区分。 func eventSheetBase(sh EventSheet) string { parts := make([]string, 0, 3) if sh.SportName != "" { parts = append(parts, sh.SportName) } parts = append(parts, sh.EventName) if sh.Category != "" { parts = append(parts, sh.Category) } return strings.Join(parts, "·") } // eventSheetTitle 是表内大标题,带赛事名。 func eventSheetTitle(meta Meta, sh EventSheet) string { base := eventSheetBase(sh) if base == "" { base = "成绩表" } return sheetTitle(meta, base) } func eventRows(rows []EventResultRow) [][]any { out := make([][]any, 0, len(rows)) for _, r := range rows { out = append(out, []any{ r.Rank, r.Bib, r.TeamName, r.Participant, displayResult(r.ResultText, r.Unit), r.Points, r.Status, r.Remark, }) } return out } // displayResult 把成绩与单位拼成「11.58 秒」;单位已含在文本里时不重复拼接。 func displayResult(text, unit string) string { text = strings.TrimSpace(text) unit = strings.TrimSpace(unit) if text == "" { return "" } if unit == "" || strings.HasSuffix(text, unit) { return text } return text + " " + unitLabel(unit) } func unitLabel(unit string) string { switch unit { case "s": return "秒" case "m": return "米" case "pts": return "分" case "次": return "次" default: return unit } }