diff --git a/server/internal/exporter/sqlbuilder.go b/server/internal/exporter/sqlbuilder.go index 9720e13..3850f49 100644 --- a/server/internal/exporter/sqlbuilder.go +++ b/server/internal/exporter/sqlbuilder.go @@ -10,6 +10,22 @@ import ( "strings" ) +// isZeroID returns true when the value is effectively empty/zero for ID filters. +func isZeroID(v interface{}) bool { + switch t := v.(type) { + case nil: + return true + case string: + s := strings.TrimSpace(t) + return s == "" || s == "0" + case int, int32, int64: + return fmt.Sprint(t) == "0" + case float32, float64: + return fmt.Sprint(t) == "0" || fmt.Sprint(t) == "0.0" + } + return false +} + type BuildRequest struct { MainTable string Datasource string @@ -218,20 +234,56 @@ func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{ need["merchant_key_send"] = true } + // normalize merchant_id_eq: skip 0/空,非零转为 merchant_id_in 以便和 creator_in 做 OR + if v, ok := req.Filters["merchant_id_eq"]; ok { + if isZeroID(v) { + delete(req.Filters, "merchant_id_eq") + } else { + if _, has := req.Filters["merchant_id_in"]; !has { + req.Filters["merchant_id_in"] = []interface{}{v} + } + delete(req.Filters, "merchant_id_eq") + } + } + + // helper: treat "0"/0/空 as未提供的 ID + isZeroID := func(v interface{}) bool { + switch t := v.(type) { + case nil: + return true + case string: + s := strings.TrimSpace(t) + return s == "" || s == "0" + case int, int32, int64: + return fmt.Sprint(t) == "0" + case float32, float64: + return fmt.Sprint(t) == "0" || fmt.Sprint(t) == "0.0" + } + return false + } + // Handle creator_in and merchant_id_in with OR logic if both exist var creatorArgs []interface{} hasCreator := false if v, ok := req.Filters["creator_in"]; ok { switch t := v.(type) { case []interface{}: - creatorArgs = t + for _, x := range t { + if !isZeroID(x) { + creatorArgs = append(creatorArgs, x) + } + } case []int: for _, x := range t { - creatorArgs = append(creatorArgs, x) + if !isZeroID(x) { + creatorArgs = append(creatorArgs, x) + } } case []string: for _, x := range t { - creatorArgs = append(creatorArgs, x) + if !isZeroID(x) { + creatorArgs = append(creatorArgs, x) + } } } if len(creatorArgs) > 0 { @@ -244,14 +296,22 @@ func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{ if v, ok := req.Filters["merchant_id_in"]; ok { switch t := v.(type) { case []interface{}: - merchantArgs = t + for _, x := range t { + if !isZeroID(x) { + merchantArgs = append(merchantArgs, x) + } + } case []int: for _, x := range t { - merchantArgs = append(merchantArgs, x) + if !isZeroID(x) { + merchantArgs = append(merchantArgs, x) + } } case []string: for _, x := range t { - merchantArgs = append(merchantArgs, x) + if !isZeroID(x) { + merchantArgs = append(merchantArgs, x) + } } } if len(merchantArgs) > 0 { @@ -618,6 +678,9 @@ func BuildCountSQL(req BuildRequest, whitelist map[string]bool) (string, []inter if k == "plan_id_eq" && utils.ToString(v) == "0" { continue } + if k == "merchant_id_eq" && isZeroID(v) { + continue + } if tbl, col, ok := sch.FilterColumn(k); ok { switch k { case "create_time_between": diff --git a/web/index.html b/web/index.html index 00da11c..68b39b4 100644 --- a/web/index.html +++ b/web/index.html @@ -240,7 +240,15 @@ - + diff --git a/web/main.js b/web/main.js index a7a5324..c70bcec 100644 --- a/web/main.js +++ b/web/main.js @@ -180,6 +180,72 @@ const app = createApp({ const editRules = ValidationRules.createEditRules(); const exportRules = ValidationRules.createExportRules(); + // ==================== 时间快捷选项 ==================== + const dateShortcuts = [ + { + text: '本日', + value: () => { + const start = new Date(); + start.setHours(0, 0, 0, 0); + const end = new Date(); + end.setHours(23, 59, 59, 999); + return [formatDateTime(start), formatDateTime(end)]; + }, + }, + { + text: '本周', + value: () => { + const now = new Date(); + const day = now.getDay() || 7; // 周一为一周开始 + const start = new Date(now); + start.setDate(start.getDate() - day + 1); + start.setHours(0, 0, 0, 0); + const end = new Date(); + end.setHours(23, 59, 59, 999); + return [formatDateTime(start), formatDateTime(end)]; + }, + }, + { + text: '本月', + value: () => { + const now = new Date(); + const start = new Date(now.getFullYear(), now.getMonth(), 1, 0, 0, 0, 0); + const end = new Date(now); + end.setHours(23, 59, 59, 999); + return [formatDateTime(start), formatDateTime(end)]; + }, + }, + { + text: '上个月', + value: () => { + const now = new Date(); + const start = new Date(now.getFullYear(), now.getMonth() - 1, 1, 0, 0, 0, 0); + const end = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999); + return [formatDateTime(start), formatDateTime(end)]; + }, + }, + { + text: '前3月', + value: () => { + const now = new Date(); + const end = new Date(); + end.setHours(23, 59, 59, 999); + const start = new Date(now.getFullYear(), now.getMonth() - 3, now.getDate(), 0, 0, 0, 0); + return [formatDateTime(start), formatDateTime(end)]; + }, + }, + { + text: '今年', + value: () => { + const now = new Date(); + const start = new Date(now.getFullYear(), 0, 1, 0, 0, 0, 0); + const end = new Date(); + end.setHours(23, 59, 59, 999); + return [formatDateTime(start), formatDateTime(end)]; + }, + }, + ]; + // ==================== 表单引用 ==================== const createFormRef = Vue.ref(null); const editFormRef = Vue.ref(null); @@ -910,6 +976,7 @@ const app = createApp({ createRules, editRules, exportRules, + dateShortcuts, // 表单引用 createFormRef, editFormRef,