feat(export): 增强SQL构建逻辑以支持ID过滤和时间快捷选项

- 添加isZeroID函数以处理ID过滤,确保在构建SQL时跳过无效ID
- 更新BuildSQL函数,优化merchant_id_eq和creator_in的处理逻辑
- 在前端引入时间快捷选项,提升用户体验,简化时间范围选择
- 更新日期选择器以支持快捷选项,增强导出功能的灵活性
This commit is contained in:
zhouyonggao 2025-12-17 17:14:55 +08:00
parent 8e4d2b4790
commit ec5322f602
3 changed files with 145 additions and 7 deletions

View File

@ -10,6 +10,22 @@ import (
"strings" "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 { type BuildRequest struct {
MainTable string MainTable string
Datasource string Datasource string
@ -218,22 +234,58 @@ func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{
need["merchant_key_send"] = true 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 // Handle creator_in and merchant_id_in with OR logic if both exist
var creatorArgs []interface{} var creatorArgs []interface{}
hasCreator := false hasCreator := false
if v, ok := req.Filters["creator_in"]; ok { if v, ok := req.Filters["creator_in"]; ok {
switch t := v.(type) { switch t := v.(type) {
case []interface{}: case []interface{}:
creatorArgs = t for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x)
}
}
case []int: case []int:
for _, x := range t { for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x) creatorArgs = append(creatorArgs, x)
} }
}
case []string: case []string:
for _, x := range t { for _, x := range t {
if !isZeroID(x) {
creatorArgs = append(creatorArgs, x) creatorArgs = append(creatorArgs, x)
} }
} }
}
if len(creatorArgs) > 0 { if len(creatorArgs) > 0 {
hasCreator = true hasCreator = true
} }
@ -244,16 +296,24 @@ func BuildSQL(req BuildRequest, whitelist map[string]bool) (string, []interface{
if v, ok := req.Filters["merchant_id_in"]; ok { if v, ok := req.Filters["merchant_id_in"]; ok {
switch t := v.(type) { switch t := v.(type) {
case []interface{}: case []interface{}:
merchantArgs = t for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x)
}
}
case []int: case []int:
for _, x := range t { for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x) merchantArgs = append(merchantArgs, x)
} }
}
case []string: case []string:
for _, x := range t { for _, x := range t {
if !isZeroID(x) {
merchantArgs = append(merchantArgs, x) merchantArgs = append(merchantArgs, x)
} }
} }
}
if len(merchantArgs) > 0 { if len(merchantArgs) > 0 {
hasMerchant = true hasMerchant = true
} }
@ -618,6 +678,9 @@ func BuildCountSQL(req BuildRequest, whitelist map[string]bool) (string, []inter
if k == "plan_id_eq" && utils.ToString(v) == "0" { if k == "plan_id_eq" && utils.ToString(v) == "0" {
continue continue
} }
if k == "merchant_id_eq" && isZeroID(v) {
continue
}
if tbl, col, ok := sch.FilterColumn(k); ok { if tbl, col, ok := sch.FilterColumn(k); ok {
switch k { switch k {
case "create_time_between": case "create_time_between":

View File

@ -240,7 +240,15 @@
<el-row :gutter="8"> <el-row :gutter="8">
<el-col :span="24"> <el-col :span="24">
<el-form-item label="时间范围" required show-message prop="dateRange"> <el-form-item label="时间范围" required show-message prop="dateRange">
<el-date-picker v-model="exportForm.dateRange" type="datetimerange" start-placeholder="开始时间" end-placeholder="结束时间" value-format="YYYY-MM-DD HH:mm:ss" style="width:100%" /> <el-date-picker
v-model="exportForm.dateRange"
type="datetimerange"
start-placeholder="开始时间"
end-placeholder="结束时间"
value-format="YYYY-MM-DD HH:mm:ss"
:shortcuts="dateShortcuts"
style="width:100%"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>

View File

@ -180,6 +180,72 @@ const app = createApp({
const editRules = ValidationRules.createEditRules(); const editRules = ValidationRules.createEditRules();
const exportRules = ValidationRules.createExportRules(); 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 createFormRef = Vue.ref(null);
const editFormRef = Vue.ref(null); const editFormRef = Vue.ref(null);
@ -910,6 +976,7 @@ const app = createApp({
createRules, createRules,
editRules, editRules,
exportRules, exportRules,
dateShortcuts,
// 表单引用 // 表单引用
createFormRef, createFormRef,
editFormRef, editFormRef,