package presets import ( "fmt" "reflect" "strconv" "strings" "github.com/qor5/web" "github.com/qor5/ui/vuetifyx" ) const autocompleteDataSourceEvent = "autocomplete-data-source-event" type AutocompleteDataResult struct { Items []OptionItem `json:"items"` Total int `json:"total"` Current int `json:"current"` Pages int `json:"pages"` } type OptionItem struct { Text string `json:"text,omitempty"` Value string `json:"value,omitempty"` Icon string `json:"icon,omitempty"` } type AutocompleteDataSourceConfig struct { OptionValue string OptionText interface{} //func(interface{}) string or string OptionIcon func(interface{}) string IsPaging bool KeywordColumns []string SQLConditions []*SQLCondition OrderBy string PerPage int64 } func (b *ListingBuilder) ConfigureAutocompleteDataSource(config *AutocompleteDataSourceConfig, name ...string) *vuetifyx.AutocompleteDataSource { if config == nil { panic("config is required") } if config.OptionValue == "" { config.OptionValue = "ID" } if config.OptionText == nil { config.OptionText = "ID" } if config.KeywordColumns == nil { config.KeywordColumns = b.searchColumns } if config.OrderBy == "" { config.OrderBy = b.orderBy } if config.PerPage == 0 { config.PerPage = b.perPage } if config.PerPage == 0 { config.PerPage = 20 } eventName := autocompleteDataSourceEvent if len(name) > 0 { eventName = eventName + "-" + strings.ToLower(name[0]) } b.mb.RegisterEventFunc(eventName, func(ctx *web.EventContext) (r web.EventResponse, err error) { var ( objs interface{} totalCount int page int64 ) if v, err := strconv.ParseInt(ctx.R.FormValue("page"), 10, 64); err == nil { page = v } searchParams := &SearchParams{ KeywordColumns: b.searchColumns, Keyword: ctx.R.FormValue("keyword"), PerPage: config.PerPage, OrderBy: config.OrderBy, Page: page, } if config.SQLConditions != nil { searchParams.SQLConditions = config.SQLConditions } objs, totalCount, err = b.Searcher(b.mb.NewModelSlice(), searchParams, ctx) if err != nil { return web.EventResponse{}, err } reflectValue := reflect.Indirect(reflect.ValueOf(objs)) var items []OptionItem for i := 0; i < reflectValue.Len(); i++ { var value = fmt.Sprintf("%v", reflect.Indirect(reflectValue.Index(i)).FieldByName(config.OptionValue).Interface()) var text string switch config.OptionText.(type) { case func(interface{}) string: text = config.OptionText.(func(interface{}) string)(reflectValue.Index(i).Interface()) case string: text = fmt.Sprintf("%v", reflect.Indirect(reflectValue.Index(i)).FieldByName(config.OptionText.(string)).Interface()) } var icon string if config.OptionIcon != nil { icon = config.OptionIcon(reflectValue.Index(i).Interface()) } items = append(items, OptionItem{ Text: text, Value: value, Icon: icon, }) } current := int((page-1)*config.PerPage) + len(items) if current > totalCount { current = totalCount } pages := totalCount / int(config.PerPage) if totalCount%int(config.PerPage) > 0 { pages++ } r.Data = AutocompleteDataResult{ Total: totalCount, Current: current, Pages: pages, Items: items, } return }) return &vuetifyx.AutocompleteDataSource{ RemoteURL: b.mb.Info().ListingHref(), EventName: eventName, IsPaging: config.IsPaging, HasIcon: config.OptionIcon != nil, } }