field_defaults.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. package presets
  2. import (
  3. "fmt"
  4. "path/filepath"
  5. "reflect"
  6. "time"
  7. "github.com/iancoleman/strcase"
  8. . "github.com/qor5/ui/vuetify"
  9. "github.com/qor5/ui/vuetifyx"
  10. "github.com/qor5/web"
  11. "github.com/qor5/x/i18n"
  12. "github.com/sunfmin/reflectutils"
  13. h "github.com/theplant/htmlgo"
  14. )
  15. type FieldDefaultBuilder struct {
  16. valType reflect.Type
  17. compFunc FieldComponentFunc
  18. setterFunc FieldSetterFunc
  19. }
  20. type FieldMode int
  21. const (
  22. WRITE FieldMode = iota
  23. LIST
  24. DETAIL
  25. )
  26. func NewFieldDefault(t reflect.Type) (r *FieldDefaultBuilder) {
  27. r = &FieldDefaultBuilder{valType: t}
  28. return
  29. }
  30. func (b *FieldDefaultBuilder) ComponentFunc(v FieldComponentFunc) (r *FieldDefaultBuilder) {
  31. b.compFunc = v
  32. return b
  33. }
  34. func (b *FieldDefaultBuilder) SetterFunc(v FieldSetterFunc) (r *FieldDefaultBuilder) {
  35. b.setterFunc = v
  36. return b
  37. }
  38. var numberVals = []interface{}{
  39. int(0), int8(0), int16(0), int32(0), int64(0),
  40. uint(0), uint(8), uint16(0), uint32(0), uint64(0),
  41. float32(0.0), float64(0.0),
  42. }
  43. var stringVals = []interface{}{
  44. string(""),
  45. []rune(""),
  46. []byte(""),
  47. }
  48. var timeVals = []interface{}{
  49. time.Now(),
  50. ptrTime(time.Now()),
  51. }
  52. type FieldDefaults struct {
  53. mode FieldMode
  54. fieldTypes []*FieldDefaultBuilder
  55. excludesPatterns []string
  56. }
  57. func NewFieldDefaults(t FieldMode) (r *FieldDefaults) {
  58. r = &FieldDefaults{
  59. mode: t,
  60. }
  61. r.builtInFieldTypes()
  62. return
  63. }
  64. func (b *FieldDefaults) FieldType(v interface{}) (r *FieldDefaultBuilder) {
  65. return b.fieldTypeByTypeOrCreate(reflect.TypeOf(v))
  66. }
  67. func (b *FieldDefaults) Exclude(patterns ...string) (r *FieldDefaults) {
  68. b.excludesPatterns = patterns
  69. return b
  70. }
  71. func (b *FieldDefaults) InspectFields(val interface{}) (r *FieldsBuilder) {
  72. r, _ = b.inspectFieldsAndCollectName(val, nil)
  73. r.Model(val)
  74. return
  75. }
  76. func (b *FieldDefaults) inspectFieldsAndCollectName(val interface{}, collectType reflect.Type) (r *FieldsBuilder, names []string) {
  77. v := reflect.ValueOf(val)
  78. for v.Elem().Kind() == reflect.Ptr {
  79. v = v.Elem()
  80. }
  81. v = v.Elem()
  82. t := v.Type()
  83. r = &FieldsBuilder{
  84. model: val,
  85. }
  86. r.Defaults(b)
  87. for i := 0; i < t.NumField(); i++ {
  88. f := t.Field(i)
  89. ft := b.fieldTypeByType(f.Type)
  90. if !hasMatched(b.excludesPatterns, f.Name) && ft != nil {
  91. r.Field(f.Name).
  92. ComponentFunc(ft.compFunc).
  93. SetterFunc(ft.setterFunc)
  94. }
  95. if collectType != nil && f.Type == collectType {
  96. names = append(names, strcase.ToSnake(f.Name))
  97. }
  98. }
  99. return
  100. }
  101. func hasMatched(patterns []string, name string) bool {
  102. for _, p := range patterns {
  103. ok, err := filepath.Match(p, name)
  104. if err != nil {
  105. panic(err)
  106. }
  107. if ok {
  108. return true
  109. }
  110. }
  111. return false
  112. }
  113. func (b *FieldDefaults) String() string {
  114. var types []string
  115. for _, t := range b.fieldTypes {
  116. types = append(types, fmt.Sprint(t.valType))
  117. }
  118. return fmt.Sprintf("mode: %d, types %v", b.mode, types)
  119. }
  120. func (b *FieldDefaults) fieldTypeByType(tv reflect.Type) (r *FieldDefaultBuilder) {
  121. for _, ft := range b.fieldTypes {
  122. if ft.valType == tv {
  123. return ft
  124. }
  125. }
  126. return nil
  127. }
  128. func (b *FieldDefaults) fieldTypeByTypeOrCreate(tv reflect.Type) (r *FieldDefaultBuilder) {
  129. if r = b.fieldTypeByType(tv); r != nil {
  130. return
  131. }
  132. r = NewFieldDefault(tv)
  133. if b.mode == LIST {
  134. r.ComponentFunc(cfTextTd)
  135. } else {
  136. r.ComponentFunc(cfTextField)
  137. }
  138. b.fieldTypes = append(b.fieldTypes, r)
  139. return
  140. }
  141. func cfTextTd(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  142. return h.Td(h.Text(field.StringValue(obj)))
  143. }
  144. func cfCheckbox(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  145. return VCheckbox().
  146. FieldName(field.FormKey).
  147. Label(field.Label).
  148. InputValue(reflectutils.MustGet(obj, field.Name).(bool)).
  149. ErrorMessages(field.Errors...).
  150. Disabled(field.Disabled)
  151. }
  152. func cfNumber(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  153. return VTextField().
  154. Type("number").
  155. FieldName(field.FormKey).
  156. Label(field.Label).
  157. Value(fmt.Sprint(reflectutils.MustGet(obj, field.Name))).
  158. ErrorMessages(field.Errors...).
  159. Disabled(field.Disabled)
  160. }
  161. func cfTime(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  162. msgr := i18n.MustGetModuleMessages(ctx.R, CoreI18nModuleKey, Messages_en_US).(*Messages)
  163. val := ""
  164. if v := field.Value(obj); v != nil {
  165. switch vt := v.(type) {
  166. case time.Time:
  167. val = vt.Format("2006-01-02 15:04")
  168. case *time.Time:
  169. val = vt.Format("2006-01-02 15:04")
  170. default:
  171. panic(fmt.Sprintf("unknown time type: %T\n", v))
  172. }
  173. }
  174. return vuetifyx.VXDateTimePicker().
  175. Label(field.Label).
  176. FieldName(field.FormKey).
  177. Value(val).
  178. TimePickerProps(vuetifyx.TimePickerProps{
  179. Format: "24hr",
  180. Scrollable: true,
  181. }).
  182. ClearText(msgr.Clear).
  183. OkText(msgr.OK)
  184. }
  185. func cfTimeSetter(obj interface{}, field *FieldContext, ctx *web.EventContext) (err error) {
  186. v := ctx.R.Form.Get(field.FormKey)
  187. if v == "" {
  188. return reflectutils.Set(obj, field.Name, nil)
  189. }
  190. t, err := time.ParseInLocation("2006-01-02 15:04", v, time.Local)
  191. if err != nil {
  192. return err
  193. }
  194. return reflectutils.Set(obj, field.Name, t)
  195. }
  196. func cfTextField(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  197. return VTextField().
  198. Type("text").
  199. FieldName(field.FormKey).
  200. Label(field.Label).
  201. Value(fmt.Sprint(reflectutils.MustGet(obj, field.Name))).
  202. ErrorMessages(field.Errors...).
  203. Disabled(field.Disabled)
  204. }
  205. func cfReadonlyText(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  206. return vuetifyx.VXReadonlyField().
  207. Label(field.Label).
  208. Value(field.StringValue(obj))
  209. }
  210. func cfReadonlyCheckbox(obj interface{}, field *FieldContext, ctx *web.EventContext) h.HTMLComponent {
  211. return vuetifyx.VXReadonlyField().
  212. Label(field.Label).
  213. Value(reflectutils.MustGet(obj, field.Name)).
  214. Checkbox(true)
  215. }
  216. func (b *FieldDefaults) builtInFieldTypes() {
  217. if b.mode == LIST {
  218. b.FieldType(true).
  219. ComponentFunc(cfTextTd)
  220. for _, v := range numberVals {
  221. b.FieldType(v).
  222. ComponentFunc(cfTextTd)
  223. }
  224. for _, v := range stringVals {
  225. b.FieldType(v).
  226. ComponentFunc(cfTextTd)
  227. }
  228. return
  229. }
  230. if b.mode == DETAIL {
  231. b.FieldType(true).
  232. ComponentFunc(cfReadonlyCheckbox)
  233. for _, v := range numberVals {
  234. b.FieldType(v).
  235. ComponentFunc(cfReadonlyText)
  236. }
  237. for _, v := range stringVals {
  238. b.FieldType(v).
  239. ComponentFunc(cfReadonlyText)
  240. }
  241. return
  242. }
  243. b.FieldType(true).
  244. ComponentFunc(cfCheckbox)
  245. for _, v := range numberVals {
  246. b.FieldType(v).
  247. ComponentFunc(cfNumber)
  248. }
  249. for _, v := range stringVals {
  250. b.FieldType(v).
  251. ComponentFunc(cfTextField)
  252. }
  253. for _, v := range timeVals {
  254. b.FieldType(v).
  255. ComponentFunc(cfTime).
  256. SetterFunc(cfTimeSetter)
  257. }
  258. b.Exclude("ID")
  259. return
  260. }