// Package views // @Link https://github.com/bufanyun/hotgo // @Copyright Copyright (c) 2023 HotGo CLI // @Author Ms <133814250@qq.com> // @License https://github.com/bufanyun/hotgo/blob/master/LICENSE package views import ( "bytes" "context" "errors" "fmt" "hotgo/internal/dao" "hotgo/internal/library/dict" "hotgo/internal/model/input/sysin" "hotgo/utility/convert" "strings" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/util/gconv" ) type StateItem struct { Name string DefaultValue interface{} Dc string DataType string // 新增字段:存储字段类型信息 } func (l *gCurd) webModelTplData(ctx context.Context, in *CurdPreviewInput) (data g.Map, err error) { data = make(g.Map) data["stateItems"] = l.generateWebModelStateItems(ctx, in) data["rules"] = l.generateWebModelRules(ctx, in) data["formSchema"] = l.generateWebModelFormSchema(ctx, in) if data["columns"], err = l.generateWebModelColumns(ctx, in); err != nil { return nil, err } // 根据表单生成情况,按需导包 data["import"], data["const"] = l.generateWebModelImport(ctx, in) return } func (l *gCurd) generateWebModelImport(ctx context.Context, in *CurdPreviewInput) (string, string) { importBuffer := bytes.NewBuffer(nil) constBuffer := bytes.NewBuffer(nil) importBuffer.WriteString("import { h, ref } from 'vue';\n") // 导入基础组件 if len(in.options.Step.ImportModel.NaiveUI) > 0 { importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.NaiveUI) + " from 'naive-ui';\n") } importBuffer.WriteString("import { cloneDeep } from 'lodash-es';\n") // 导入表单搜索 if in.options.Step.HasSearchForm { importBuffer.WriteString("import { FormSchema } from '@/components/Form';\n") } // 导入工具类 if len(in.options.Step.ImportModel.UtilsIs) > 0 { importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsIs) + " from '@/utils/is';\n") } if len(in.options.Step.ImportModel.UtilsUrl) > 0 { importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsUrl) + " from '@/utils/urlUtils';\n") } if len(in.options.Step.ImportModel.UtilsDate) > 0 { importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsDate) + " from '@/utils/dateUtil';\n") } if in.options.Step.HasRulesValidator { importBuffer.WriteString("import { validate } from '@/utils/validateUtil';\n") } if len(in.options.Step.ImportModel.UtilsHotGo) > 0 { importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsHotGo) + " from '@/utils/hotgo';\n") } if len(in.options.Step.ImportModel.UtilsIndex) > 0 { importBuffer.WriteString("import " + ImportWebMethod(in.options.Step.ImportModel.UtilsIndex) + " from '@/utils';\n") } // 导入api var importApiMethod []string if in.options.Step.HasSwitch { importApiMethod = append(importApiMethod, "Switch") } if in.options.Step.IsTreeTable { importApiMethod = append(importApiMethod, "TreeOption") } if len(importApiMethod) > 0 { importBuffer.WriteString("import " + ImportWebMethod(importApiMethod) + " from '" + in.options.ImportWebApi + "';\n") } // 导入字典选项 if in.options.DictOps.Has { importBuffer.WriteString("import { useDictStore } from '@/store/modules/dict';\n") constBuffer.WriteString("const dict = useDictStore();\n") } if in.options.Step.HasSwitch { importBuffer.WriteString("import { usePermission } from '@/hooks/web/usePermission';\n") constBuffer.WriteString("const { hasPermission } = usePermission();\n") constBuffer.WriteString("const $message = window['$message'];\n") } return importBuffer.String(), constBuffer.String() } func (l *gCurd) generateWebModelStateItems(ctx context.Context, in *CurdPreviewInput) (items []*StateItem) { for _, field := range in.masterFields { dataType := field.DataType if dataType == "" && field.SqlType != "" { if parts := strings.SplitN(field.SqlType, "(", 2); len(parts) > 0 { dataType = parts[0] } } var value = field.DefaultValue if value == nil { value = "null" } //注释为空判断 避免模版生成的model.ts重复加引号 //if value == "" { // value = `''` //} // 选项组件默认值调整 if gconv.Int(value) == 0 && IsSelectFormMode(field.FormMode) { value = "null" } if field.Name == "status" { value = 1 } if field.FormMode == FormModeSwitch { value = 2 } if field.FormMode == FormModeInputDynamic { value = "[]" } items = append(items, &StateItem{ Name: field.TsName, DefaultValue: value, Dc: field.Dc, }) // 查询用户摘要 if field.IsList && in.options.Step.HasHookMemberSummary && IsMemberSummaryField(field.Name) { items = append(items, &StateItem{ Name: field.TsName + "Summa?: null | MemberSumma", DefaultValue: "null", DataType: dataType, Dc: field.Dc + "摘要信息", }) } } return } func (l *gCurd) generateWebModelDictOptions(ctx context.Context, in *CurdPreviewInput) error { type DictType struct { Id int64 `json:"id"` Type string `json:"type"` } var ( dictTypeIds []int64 dictTypeList []*DictType builtinDictTypeIds []int64 builtinDictTypeList []*DictType ) for _, field := range in.masterFields { if field.DictType > 0 { dictTypeIds = append(dictTypeIds, field.DictType) } if field.DictType < 0 { builtinDictTypeIds = append(builtinDictTypeIds, field.DictType) } } dictTypeIds = convert.UniqueSlice(dictTypeIds) builtinDictTypeIds = convert.UniqueSlice(builtinDictTypeIds) if len(dictTypeIds) == 0 && len(builtinDictTypeIds) == 0 { return nil } if len(dictTypeIds) > 0 { err := dao.SysDictType.Ctx(ctx). Fields(dao.SysDictType.Columns().Id, dao.SysDictType.Columns().Type). WhereIn(dao.SysDictType.Columns().Id, dictTypeIds). Scan(&dictTypeList) if err != nil { return err } } if len(builtinDictTypeIds) > 0 { for _, id := range builtinDictTypeIds { typ, err := dict.GetTypeById(ctx, id) if err != nil && !errors.Is(err, dict.NotExistKeyError) { return err } if len(typ) > 0 { row := new(DictType) row.Id = id row.Type = typ builtinDictTypeList = append(builtinDictTypeList, row) } } } if len(dictTypeList) == 0 && len(builtinDictTypeList) == 0 { return nil } if len(builtinDictTypeList) > 0 { dictTypeList = append(dictTypeList, builtinDictTypeList...) } in.options.DictOps.Has = true for _, v := range dictTypeList { // 字段映射字典 for _, field := range in.masterFields { if field.DictType != 0 && v.Id == field.DictType { in.options.dictMap[field.TsName] = v.Type in.options.DictOps.Schemas = append(in.options.DictOps.Schemas, &OptionsSchemasField{ Field: field.TsName, Type: v.Type, }) } } in.options.DictOps.Types = append(in.options.DictOps.Types, v.Type) } return nil } func (l *gCurd) generateWebModelRules(ctx context.Context, in *CurdPreviewInput) string { buffer := bytes.NewBuffer(nil) buffer.WriteString("export const rules = {\n") for _, field := range in.masterFields { if !field.IsEdit || (!field.Required && (field.FormRole == "" || field.FormRole == FormRoleNone)) { continue } in.options.Step.HasRules = true if field.FormRole == "" || field.FormRole == FormRoleNone || field.FormRole == "required" { buffer.WriteString(fmt.Sprintf(" %s: {\n required: %v,\n trigger: ['blur', 'input'],\n type: '%s',\n message: '请输入%s',\n },\n", field.TsName, field.Required, field.TsType, field.Dc)) } else { in.options.Step.HasRulesValidator = true buffer.WriteString(fmt.Sprintf(" %s: {\n required: %v,\n trigger: ['blur', 'input'],\n type: '%s',\n validator: validate.%v,\n },\n", field.TsName, field.Required, field.TsType, field.FormRole)) } } buffer.WriteString("};\n") return buffer.String() } func (l *gCurd) generateWebModelFormSchema(ctx context.Context, in *CurdPreviewInput) string { buffer := bytes.NewBuffer(nil) buffer.WriteString("export const schemas = ref([\n") // 主表 l.generateWebModelFormSchemaEach(buffer, in.masterFields, in) // 关联表 if len(in.options.Join) > 0 { for _, v := range in.options.Join { if !isEffectiveJoin(v) { continue } l.generateWebModelFormSchemaEach(buffer, v.Columns, in) } } buffer.WriteString("]);\n") return buffer.String() } // generateWebDictOption 生产字典选项 func (l *gCurd) generateWebDictOption(typ any) string { return fmt.Sprintf(`dict.getOption('%v')`, typ) } func (l *gCurd) generateWebModelFormSchemaEach(buffer *bytes.Buffer, fields []*sysin.GenCodesColumnListModel, in *CurdPreviewInput) { for _, field := range fields { if !field.IsQuery { continue } in.options.Step.HasSearchForm = true // 查询用户摘要 if field.IsQuery && in.options.Step.HasQueryMemberSummary && IsMemberSummaryField(field.Name) { buffer.WriteString(fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n placeholder: '请输入ID|用户名|姓名|手机号',\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NInput", field.Dc)) continue } var ( defaultComponent = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n placeholder: '请输入%s',\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NInput", field.Dc, field.Dc) component string ) // 这里根据编辑表单组件来进行推断,如果没有则使用默认input,这可能会导致和查询条件所需参数不符的情况 switch field.FormMode { case FormModeInput, FormModeInputTextarea, FormModeInputEditor, FormModeInputYaml: component = defaultComponent case FormModeInputNumber: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n placeholder: '请输入%s',\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NInputNumber", field.Dc, field.Dc) case FormModeDate: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "date", "defShortcuts()") in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defShortcuts") case FormModeDateRange: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "daterange", "defRangeShortcuts()") in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defRangeShortcuts") case FormModeTime: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "datetime", "defShortcuts()") in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defShortcuts") case FormModeTimeRange: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n componentProps: {\n type: '%s',\n clearable: true,\n shortcuts: %s,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NDatePicker", field.Dc, "datetimerange", "defRangeShortcuts()") in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "defRangeShortcuts") case FormModeSwitch: fallthrough case FormModeRadio: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n giProps: {\n //span: 24,\n },\n componentProps: {\n options: %v,\n onUpdateChecked: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NRadioGroup", field.Dc, l.generateWebDictOption(in.options.dictMap[field.TsName])) case FormModeCheckbox: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n giProps: {\n span: 1,\n },\n componentProps: {\n placeholder: '请选择%s',\n options: %v,\n onUpdateChecked: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NCheckbox", field.Dc, field.Dc, l.generateWebDictOption(in.options.dictMap[field.TsName])) case FormModeSelect: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n defaultValue: null,\n componentProps: {\n placeholder: '请选择%s',\n options: %v,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NSelect", field.Dc, field.Dc, l.generateWebDictOption(in.options.dictMap[field.TsName])) case FormModeSelectMultiple: component = fmt.Sprintf(" {\n field: '%s',\n component: '%s',\n label: '%s',\n defaultValue: null,\n componentProps: {\n multiple: true,\n placeholder: '请选择%s',\n options: %v,\n onUpdateValue: (e: any) => {\n console.log(e);\n },\n },\n },\n", field.TsName, "NSelect", field.Dc, field.Dc, l.generateWebDictOption(in.options.dictMap[field.TsName])) default: component = defaultComponent } buffer.WriteString(component) } } func (l *gCurd) generateWebModelColumns(ctx context.Context, in *CurdPreviewInput) (string, error) { buffer := bytes.NewBuffer(nil) buffer.WriteString("export const columns = [\n") // 主表 if err := l.generateWebModelColumnsEach(buffer, in, in.masterFields); err != nil { return "", err } // 关联表 if len(in.options.Join) > 0 { for _, v := range in.options.Join { if !isEffectiveJoin(v) { continue } if err := l.generateWebModelColumnsEach(buffer, in, v.Columns); err != nil { return "", err } } } buffer.WriteString("];\n") return buffer.String(), nil } func (l *gCurd) generateWebModelColumnsEach(buffer *bytes.Buffer, in *CurdPreviewInput, fields []*sysin.GenCodesColumnListModel) (err error) { for _, field := range fields { if !field.IsList { continue } var ( defaultComponent = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n },\n", field.Dc, field.TsName, field.Align, field.Width) component string ) // 查询用户摘要 if in.options.Step.HasHookMemberSummary && IsMemberSummaryField(field.Name) { buffer.WriteString(fmt.Sprintf(" {\n title: '%v',\n key: '%v',\n align: '%v',\n width: %v,\n render(row: State) {\n return renderPopoverMemberSumma(row.%vSumma);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName)) in.options.Step.ImportModel.UtilsIndex = append(in.options.Step.ImportModel.UtilsIndex, []string{"renderPopoverMemberSumma", "MemberSumma"}...) continue } // 这里根据编辑表单组件来进行推断,如果没有则使用默认input,这可能会导致和查询条件所需参数不符的情况 switch field.FormMode { case FormModeDate: component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return formatToDate(row.%s);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName) in.options.Step.ImportModel.UtilsDate = append(in.options.Step.ImportModel.UtilsDate, "formatToDate") case FormModeRadio: fallthrough case FormModeSelect: if g.IsEmpty(in.options.dictMap[field.TsName]) { err = gerror.Newf("设置单选下拉框选项时,必须选择字典类型,字段名称:%v", field.Name) return } component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return renderOptionTag('%v', row.%v);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, in.options.dictMap[field.TsName], field.TsName) in.options.Step.ImportModel.UtilsIndex = append(in.options.Step.ImportModel.UtilsIndex, "renderOptionTag") case FormModeSelectMultiple: if g.IsEmpty(in.options.dictMap[field.TsName]) { err = gerror.Newf("设置多选下拉框选项时,必须选择字典类型,字段名称:%v", field.Name) return } component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n if (isNullObject(row.%s) || !isArray(row.%s)) {\n return ``;\n }\n return row.%s.map((tagKey) => {\n return renderOptionTag('%s', row.tagKey)\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName, field.TsName, field.TsName, in.options.dictMap[field.TsName]) in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NTag") in.options.Step.ImportModel.UtilsIs = append(in.options.Step.ImportModel.UtilsIs, "isNullObject") case FormModeUploadImage: component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return renderImage(row.%v);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName) in.options.Step.ImportModel.UtilsIndex = append(in.options.Step.ImportModel.UtilsIndex, "renderImage") case FormModeUploadImages: component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return renderImageGroup(row.%v);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName) in.options.Step.ImportModel.UtilsIndex = append(in.options.Step.ImportModel.UtilsIndex, "renderImageGroup") case FormModeUploadFile: component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return renderFile(row.%v);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName) in.options.Step.ImportModel.UtilsIndex = append(in.options.Step.ImportModel.UtilsIndex, "renderFile") case FormModeUploadFiles: component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return renderFileGroup(row.%v);\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, field.TsName) in.options.Step.ImportModel.UtilsIndex = append(in.options.Step.ImportModel.UtilsIndex, "renderFileGroup") case FormModeSwitch: component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return h(%s, {\n value: row.%s === 1,\n checked: '开启',\n unchecked: '关闭',\n disabled: !hasPermission(['%s']),\n onUpdateValue: function (e) {\n console.log('onUpdateValue e:' + JSON.stringify(e));\n row.%s = e ? 1 : 2;\n Switch({ %s: row.%s, key: '%s', value: row.%s }).then((_res) => {\n $message.success('操作成功');\n });\n },\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, "NSwitch", field.TsName, "/"+in.options.ApiPrefix+"/switch", field.TsName, in.pk.TsName, in.pk.TsName, convert.CamelCaseToUnderline(field.TsName), field.TsName) in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NSwitch") case FormModeRate: component = fmt.Sprintf(" {\n title: '%s',\n key: '%s',\n align: '%v',\n width: %v,\n render(row: State) {\n return h(%s, {\n allowHalf: true,\n readonly: true,\n defaultValue: row.%s,\n });\n },\n },\n", field.Dc, field.TsName, field.Align, field.Width, "NRate", field.TsName) in.options.Step.ImportModel.NaiveUI = append(in.options.Step.ImportModel.NaiveUI, "NRate") default: component = defaultComponent } buffer.WriteString(component) } return }