kra/pkg/utils/autocode/template_funcs.go

1783 lines
60 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package autocode
import (
"fmt"
"slices"
"strings"
"text/template"
)
// AutoCodeField 自动代码字段(用于模板函数)
type AutoCodeField struct {
FieldName string `json:"fieldName"`
FieldDesc string `json:"fieldDesc"`
FieldType string `json:"fieldType"`
FieldJson string `json:"fieldJson"`
DataTypeLong string `json:"dataTypeLong"`
Comment string `json:"comment"`
ColumnName string `json:"columnName"`
FieldSearchType string `json:"fieldSearchType"`
FieldSearchHide bool `json:"fieldSearchHide"`
DictType string `json:"dictType"`
Form bool `json:"form"`
Table bool `json:"table"`
Desc bool `json:"desc"`
Excel bool `json:"excel"`
Require bool `json:"require"`
DefaultValue string `json:"defaultValue"`
ErrorText string `json:"errorText"`
Clearable bool `json:"clearable"`
Sort bool `json:"sort"`
PrimaryKey bool `json:"primaryKey"`
DataSource *DataSource `json:"dataSource"`
CheckDataSource bool `json:"checkDataSource"`
FieldIndexType string `json:"fieldIndexType"`
}
// DataSource 数据源
type DataSource struct {
DBName string `json:"dbName"`
Table string `json:"table"`
Label string `json:"label"`
Value string `json:"value"`
Association int `json:"association"`
HasDeletedAt bool `json:"hasDeletedAt"`
}
// CustomMethod 自定义查询方法
// 用于在 GORM Gen 生成的代码中添加自定义查询方法
type CustomMethod struct {
Name string `json:"name"` // 方法名称 (如: FindByName, GetActiveUsers)
Description string `json:"description"` // 方法描述
ReturnType string `json:"returnType"` // 返回类型: single(单个), list(列表), count(计数), exists(存在检查)
Params []*CustomMethodParam `json:"params"` // 方法参数
Conditions []*CustomCondition `json:"conditions"` // 查询条件
OrderBy string `json:"orderBy"` // 排序字段 (如: created_at DESC)
Limit int `json:"limit"` // 限制数量 (0表示不限制)
}
// CustomMethodParam 自定义方法参数
type CustomMethodParam struct {
Name string `json:"name"` // 参数名称
Type string `json:"type"` // 参数类型 (string, int, uint, bool, time.Time等)
FieldName string `json:"fieldName"` // 对应的字段名 (用于生成查询条件)
}
// CustomCondition 自定义查询条件
type CustomCondition struct {
FieldName string `json:"fieldName"` // 字段名
Operator string `json:"operator"` // 操作符: eq, neq, gt, gte, lt, lte, like, in, between, isNull, isNotNull
ParamName string `json:"paramName"` // 参数名 (对应 CustomMethodParam.Name)
}
// ToPascalCase 将字符串转换为 PascalCase 格式
// 支持 snake_case、kebab-case 和普通字符串
// 例如: "article_status" -> "ArticleStatus", "user-role" -> "UserRole"
func ToPascalCase(s string) string {
if s == "" {
return s
}
// 将下划线和连字符替换为空格,然后使用 Title 转换
s = strings.ReplaceAll(s, "_", " ")
s = strings.ReplaceAll(s, "-", " ")
// 使用 strings.Title 转换每个单词首字母大写
s = strings.Title(s)
// 移除空格
return strings.ReplaceAll(s, " ", "")
}
// GetTemplateFuncMap 返回模板函数映射
func GetTemplateFuncMap() template.FuncMap {
return template.FuncMap{
"title": strings.Title,
"toPascalCase": ToPascalCase,
"GenerateField": GenerateField,
"GenerateModelField": GenerateModelField,
"GenerateSearchField": GenerateSearchField,
"GenerateSearchConditions": GenerateSearchConditions,
"GenerateGormGenSearchConditions": GenerateGormGenSearchConditions,
"GenerateSearchFormItem": GenerateSearchFormItem,
"GenerateTableColumn": GenerateTableColumn,
"GenerateFormItem": GenerateFormItem,
"GenerateDescriptionItem": GenerateDescriptionItem,
"GenerateDefaultFormValue": GenerateDefaultFormValue,
// React/TypeScript 相关函数
"GenerateTSType": GenerateTSType,
"GenerateReactSearchFormItem": GenerateReactSearchFormItem,
"GenerateReactTableColumn": GenerateReactTableColumn,
"GenerateReactProTableColumn": GenerateReactProTableColumn,
"GenerateReactFormItem": GenerateReactFormItem,
"GenerateReactDescriptionItem": GenerateReactDescriptionItem,
"GenerateReactDefaultValue": GenerateReactDefaultValue,
// 自定义查询方法相关函数
"GenerateCustomMethodInterface": GenerateCustomMethodInterface,
"GenerateCustomMethodImpl": GenerateCustomMethodImpl,
}
}
// GenerateField 渲染Model中的字段
func GenerateField(field AutoCodeField) string {
gormTag := ``
if field.FieldIndexType != "" {
gormTag += field.FieldIndexType + ";"
}
if field.PrimaryKey {
gormTag += "primarykey;"
}
if field.DefaultValue != "" {
gormTag += fmt.Sprintf("default:%s;", field.DefaultValue)
}
if field.Comment != "" {
gormTag += fmt.Sprintf("comment:%s;", field.Comment)
}
gormTag += "column:" + field.ColumnName + ";"
if field.DataTypeLong != "" && field.FieldType != "enum" && field.FieldType != "int" {
gormTag += fmt.Sprintf("size:%s;", field.DataTypeLong)
}
requireTag := ` binding:"required"` + "`"
var result string
switch field.FieldType {
case "enum":
result = fmt.Sprintf(`%s string `+"`"+`json:"%s" form:"%s" gorm:"%stype:enum(%s);"`+"`",
field.FieldName, field.FieldJson, field.FieldJson, gormTag, field.DataTypeLong)
case "picture", "video":
tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, field.FieldJson, field.FieldJson, gormTag)
result = fmt.Sprintf(`%s string `+"`"+`%s`+"`"+``, field.FieldName, tagContent)
case "file", "pictures", "array":
tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, field.FieldJson, field.FieldJson, gormTag)
result = fmt.Sprintf(`%s datatypes.JSON `+"`"+`%s swaggertype:"array,object"`+"`"+``, field.FieldName, tagContent)
case "richtext":
tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s`, field.FieldJson, field.FieldJson, gormTag)
result = fmt.Sprintf(`%s *string `+"`"+`%stype:text;"`+"`"+``, field.FieldName, tagContent)
case "json":
tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, field.FieldJson, field.FieldJson, gormTag)
result = fmt.Sprintf(`%s datatypes.JSON `+"`"+`%s swaggertype:"object"`+"`"+``, field.FieldName, tagContent)
default:
tagContent := fmt.Sprintf(`json:"%s" form:"%s" gorm:"%s"`, field.FieldJson, field.FieldJson, gormTag)
var fieldType string
if field.FieldType == "int" {
switch field.DataTypeLong {
case "1", "2", "3":
fieldType = "int8"
case "4", "5":
fieldType = "int16"
case "6", "7", "8", "9", "10":
fieldType = "int32"
default:
fieldType = "int64"
}
} else {
fieldType = field.FieldType
}
result = fmt.Sprintf(`%s *%s `+"`"+`%s`+"`"+``, field.FieldName, fieldType, tagContent)
}
if field.Require {
result = result[0:len(result)-1] + requireTag
}
if field.FieldDesc != "" {
result += fmt.Sprintf(" //%s", field.FieldDesc)
}
return result
}
// GenerateModelField 生成 GORM Gen 风格的模型字段定义
// 用于生成与 GORM Gen 兼容的模型结构体字段
func GenerateModelField(field AutoCodeField) string {
// 构建 GORM 标签
var gormParts []string
// column 标签
gormParts = append(gormParts, fmt.Sprintf("column:%s", field.ColumnName))
// type 标签 - 根据字段类型生成数据库类型
dbType := getDBType(field)
if dbType != "" {
gormParts = append(gormParts, fmt.Sprintf("type:%s", dbType))
}
// index 标签
if field.FieldIndexType != "" {
gormParts = append(gormParts, field.FieldIndexType)
}
// default 标签
if field.DefaultValue != "" {
gormParts = append(gormParts, fmt.Sprintf("default:%s", field.DefaultValue))
}
// comment 标签
comment := field.Comment
if comment == "" {
comment = field.FieldDesc
}
if comment != "" {
gormParts = append(gormParts, fmt.Sprintf("comment:%s", comment))
}
gormTag := strings.Join(gormParts, ";")
// 获取 Go 类型
goType := getModelGoType(field)
// 构建完整的字段定义
var result string
if comment != "" {
result = fmt.Sprintf(`%s %s `+"`"+`gorm:"%s" json:"%s"`+"`"+` // %s`,
field.FieldName, goType, gormTag, field.FieldJson, comment)
} else {
result = fmt.Sprintf(`%s %s `+"`"+`gorm:"%s" json:"%s"`+"`",
field.FieldName, goType, gormTag, field.FieldJson)
}
return result
}
// getDBType 根据字段类型获取数据库类型
func getDBType(field AutoCodeField) string {
switch field.FieldType {
case "string":
if field.DataTypeLong != "" {
return fmt.Sprintf("varchar(%s)", field.DataTypeLong)
}
return "varchar(191)"
case "int":
switch field.DataTypeLong {
case "1", "2", "3":
return "tinyint"
case "4", "5":
return "smallint"
case "6", "7", "8", "9", "10":
return "int"
default:
return "bigint(20)"
}
case "int64":
return "bigint(20)"
case "uint":
return "bigint(20) unsigned"
case "float64":
return "double"
case "bool":
return "tinyint(1)"
case "time.Time":
return "datetime(3)"
case "enum":
if field.DataTypeLong != "" {
return fmt.Sprintf("enum(%s)", field.DataTypeLong)
}
return "varchar(50)"
case "richtext":
return "text"
case "json", "file", "pictures", "array":
return "json"
case "picture", "video":
return "varchar(500)"
default:
return ""
}
}
// getModelGoType 根据字段类型获取 Go 类型(用于 GORM Gen 模型)
func getModelGoType(field AutoCodeField) string {
switch field.FieldType {
case "string", "picture", "video":
return "string"
case "int":
switch field.DataTypeLong {
case "1", "2", "3":
return "int8"
case "4", "5":
return "int16"
case "6", "7", "8", "9", "10":
return "int32"
default:
return "int64"
}
case "int64":
return "int64"
case "uint":
return "int64" // GORM Gen 使用 int64 表示 unsigned
case "float64":
return "float64"
case "bool":
return "bool"
case "time.Time":
return "time.Time"
case "enum":
return "string"
case "richtext":
return "string"
case "json", "file", "pictures", "array":
return "datatypes.JSON"
default:
return "string"
}
}
// GenerateSearchConditions 格式化搜索条件语句
func GenerateSearchConditions(fields []*AutoCodeField) string {
var conditions []string
for _, field := range fields {
if field.FieldSearchType == "" {
continue
}
var condition string
if slices.Contains([]string{"enum", "pictures", "picture", "video", "json", "richtext", "array"}, field.FieldType) {
if field.FieldType == "enum" {
if field.FieldSearchType == "LIKE" {
condition = fmt.Sprintf(`
if info.%s != "" {
db = db.Where("%s LIKE ?", "%%"+ info.%s+"%%")
}`, field.FieldName, field.ColumnName, field.FieldName)
} else {
condition = fmt.Sprintf(`
if info.%s != "" {
db = db.Where("%s %s ?", info.%s)
}`, field.FieldName, field.ColumnName, field.FieldSearchType, field.FieldName)
}
} else {
condition = fmt.Sprintf(`
if info.%s != "" {
// TODO 数据类型为复杂类型,请根据业务需求自行实现复杂类型的查询业务
}`, field.FieldName)
}
} else if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" {
if field.FieldType == "time.Time" {
condition = fmt.Sprintf(`
if len(info.%sRange) == 2 {
db = db.Where("%s %s ? AND ? ", info.%sRange[0], info.%sRange[1])
}`, field.FieldName, field.ColumnName, field.FieldSearchType, field.FieldName, field.FieldName)
} else {
condition = fmt.Sprintf(`
if info.Start%s != nil && info.End%s != nil {
db = db.Where("%s %s ? AND ? ", *info.Start%s, *info.End%s)
}`, field.FieldName, field.FieldName, field.ColumnName, field.FieldSearchType, field.FieldName, field.FieldName)
}
} else if field.FieldSearchType == "IN" || field.FieldSearchType == "NOT IN" {
// IN 和 NOT IN 需要传递数组
condition = fmt.Sprintf(`
if info.%s != nil && len(info.%s) > 0 {
db = db.Where("%s %s ?", info.%s)
}`, field.FieldName, field.FieldName, field.ColumnName, field.FieldSearchType, field.FieldName)
} else {
nullCheck := "info." + field.FieldName + " != nil"
if field.FieldType == "string" {
condition = fmt.Sprintf(`
if %s && *info.%s != "" {`, nullCheck, field.FieldName)
} else {
condition = fmt.Sprintf(`
if %s {`, nullCheck)
}
if field.FieldSearchType == "LIKE" {
condition += fmt.Sprintf(`
db = db.Where("%s LIKE ?", "%%"+ *info.%s+"%%")
}`, field.ColumnName, field.FieldName)
} else {
condition += fmt.Sprintf(`
db = db.Where("%s %s ?", *info.%s)
}`, field.ColumnName, field.FieldSearchType, field.FieldName)
}
}
conditions = append(conditions, condition)
}
return strings.Join(conditions, "")
}
// GenerateGormGenSearchConditions 生成 GORM Gen 风格的搜索条件语句
// 使用类型安全的 GORM Gen 查询语法,如 q.Where(t.FieldName.Like(...))
func GenerateGormGenSearchConditions(fields []*AutoCodeField, structName string) string {
var conditions []string
for _, field := range fields {
if field.FieldSearchType == "" {
continue
}
var condition string
// 复杂类型处理
if slices.Contains([]string{"enum", "pictures", "picture", "video", "json", "richtext", "array"}, field.FieldType) {
if field.FieldType == "enum" {
if field.FieldSearchType == "LIKE" {
condition = fmt.Sprintf(`
if req.%s != "" {
q = q.Where(t.%s.Like("%%" + req.%s + "%%"))
}`, field.FieldName, field.FieldName, field.FieldName)
} else {
condition = fmt.Sprintf(`
if req.%s != "" {
q = q.Where(t.%s.Eq(req.%s))
}`, field.FieldName, field.FieldName, field.FieldName)
}
} else {
condition = fmt.Sprintf(`
if req.%s != "" {
// TODO 数据类型为复杂类型,请根据业务需求自行实现复杂类型的查询业务
}`, field.FieldName)
}
} else if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" {
// BETWEEN 查询
if field.FieldType == "time.Time" {
condition = fmt.Sprintf(`
if len(req.%sRange) == 2 {
q = q.Where(t.%s.Between(req.%sRange[0], req.%sRange[1]))
}`, field.FieldName, field.FieldName, field.FieldName, field.FieldName)
} else {
condition = fmt.Sprintf(`
if req.Start%s != nil && req.End%s != nil {
q = q.Where(t.%s.Between(*req.Start%s, *req.End%s))
}`, field.FieldName, field.FieldName, field.FieldName, field.FieldName, field.FieldName)
}
} else {
// 普通查询
nullCheck := "req." + field.FieldName + " != nil"
if field.FieldType == "string" {
condition = fmt.Sprintf(`
if %s && *req.%s != "" {`, nullCheck, field.FieldName)
} else {
condition = fmt.Sprintf(`
if %s {`, nullCheck)
}
// 根据搜索类型生成不同的查询方法
switch field.FieldSearchType {
case "LIKE":
condition += fmt.Sprintf(`
q = q.Where(t.%s.Like("%%" + *req.%s + "%%"))
}`, field.FieldName, field.FieldName)
case "=", "EQ":
if field.FieldType == "string" {
condition += fmt.Sprintf(`
q = q.Where(t.%s.Eq(*req.%s))
}`, field.FieldName, field.FieldName)
} else {
condition += fmt.Sprintf(`
q = q.Where(t.%s.Eq(*req.%s))
}`, field.FieldName, field.FieldName)
}
case ">", "GT":
condition += fmt.Sprintf(`
q = q.Where(t.%s.Gt(*req.%s))
}`, field.FieldName, field.FieldName)
case ">=", "GTE":
condition += fmt.Sprintf(`
q = q.Where(t.%s.Gte(*req.%s))
}`, field.FieldName, field.FieldName)
case "<", "LT":
condition += fmt.Sprintf(`
q = q.Where(t.%s.Lt(*req.%s))
}`, field.FieldName, field.FieldName)
case "<=", "LTE":
condition += fmt.Sprintf(`
q = q.Where(t.%s.Lte(*req.%s))
}`, field.FieldName, field.FieldName)
case "<>", "!=", "NEQ":
condition += fmt.Sprintf(`
q = q.Where(t.%s.Neq(*req.%s))
}`, field.FieldName, field.FieldName)
case "IN":
condition += fmt.Sprintf(`
q = q.Where(t.%s.In(*req.%s...))
}`, field.FieldName, field.FieldName)
case "NOT IN":
condition += fmt.Sprintf(`
q = q.Where(t.%s.NotIn(*req.%s...))
}`, field.FieldName, field.FieldName)
default:
// 默认使用 Eq
condition += fmt.Sprintf(`
q = q.Where(t.%s.Eq(*req.%s))
}`, field.FieldName, field.FieldName)
}
}
conditions = append(conditions, condition)
}
return strings.Join(conditions, "")
}
// GenerateSearchFormItem 格式化前端搜索条件
func GenerateSearchFormItem(field AutoCodeField) string {
result := fmt.Sprintf(`<el-form-item label="%s" prop="%s">
`, field.FieldDesc, field.FieldJson)
if field.FieldType == "bool" {
result += fmt.Sprintf(` <el-select v-model="searchInfo.%s" clearable placeholder="请选择">
`, field.FieldJson)
result += ` <el-option key="true" label="是" value="true"></el-option>
`
result += ` <el-option key="false" label="否" value="false"></el-option>
`
result += ` </el-select>
`
} else if field.DictType != "" {
multipleAttr := ""
if field.FieldType == "array" {
multipleAttr = "multiple "
}
result += fmt.Sprintf(` <el-tree-select v-model="searchInfo.%s" placeholder="请选择%s" :data="%sOptions" style="width:100%%" filterable :clearable="%v" check-strictly %s></el-tree-select>
`, field.FieldJson, field.FieldDesc, field.DictType, field.Clearable, multipleAttr)
} else if field.CheckDataSource {
multipleAttr := ""
if field.DataSource.Association == 2 {
multipleAttr = "multiple "
}
result += fmt.Sprintf(` <el-select %sv-model="searchInfo.%s" filterable placeholder="请选择%s" :clearable="%v">
`, multipleAttr, field.FieldJson, field.FieldDesc, field.Clearable)
result += fmt.Sprintf(` <el-option v-for="(item,key) in dataSource.%s" :key="key" :label="item.label" :value="item.value" />
`, field.FieldJson)
result += ` </el-select>
`
} else if field.FieldType == "float64" || field.FieldType == "int" {
if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" {
result += fmt.Sprintf(` <el-input class="!w-40" v-model.number="searchInfo.start%s" placeholder="最小值" />
`, field.FieldName)
result += `
`
result += fmt.Sprintf(` <el-input class="!w-40" v-model.number="searchInfo.end%s" placeholder="最大值" />
`, field.FieldName)
} else {
result += fmt.Sprintf(` <el-input v-model.number="searchInfo.%s" placeholder="搜索条件" />
`, field.FieldJson)
}
} else if field.FieldType == "time.Time" {
if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" {
result += ` <template #label>
`
result += ` <span>
`
result += fmt.Sprintf(` %s
`, field.FieldDesc)
result += ` <el-tooltip content="搜索范围是开始日期(包含)至结束日期(不包含)">
`
result += ` <el-icon><QuestionFilled /></el-icon>
`
result += ` </el-tooltip>
`
result += ` </span>
`
result += ` </template>
`
result += fmt.Sprintf(`<el-date-picker class="!w-380px" v-model="searchInfo.%sRange" type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间"></el-date-picker>`, field.FieldJson)
} else {
result += fmt.Sprintf(`<el-date-picker v-model="searchInfo.%s" type="datetime" placeholder="搜索条件"></el-date-picker>`, field.FieldJson)
}
} else {
result += fmt.Sprintf(` <el-input v-model="searchInfo.%s" placeholder="搜索条件" />
`, field.FieldJson)
}
result += `</el-form-item>`
return result
}
// GenerateTableColumn 生成表格列HTML
func GenerateTableColumn(field AutoCodeField) string {
sortAttr := ""
if field.Sort {
sortAttr = " sortable"
}
if field.CheckDataSource {
result := fmt.Sprintf(`<el-table-column%s align="left" label="%s" prop="%s" width="120">
`, sortAttr, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
if field.DataSource.Association == 2 {
result += fmt.Sprintf(` <el-tag v-for="(item,key) in filterDataSource(dataSource.%s,scope.row.%s)" :key="key">
`, field.FieldJson, field.FieldJson)
result += ` {{ item }}
`
result += ` </el-tag>
`
} else {
result += fmt.Sprintf(` <span>{{ filterDataSource(dataSource.%s,scope.row.%s) }}</span>
`, field.FieldJson, field.FieldJson)
}
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.DictType != "" {
result := fmt.Sprintf(`<el-table-column%s align="left" label="%s" prop="%s" width="120">
`, sortAttr, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
if field.FieldType == "array" {
result += fmt.Sprintf(` <el-tag class="mr-1" v-for="item in scope.row.%s" :key="item"> {{ filterDict(item,%sOptions) }}</el-tag>
`, field.FieldJson, field.DictType)
} else {
result += fmt.Sprintf(` {{ filterDict(scope.row.%s,%sOptions) }}
`, field.FieldJson, field.DictType)
}
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.FieldType == "bool" {
result := fmt.Sprintf(`<el-table-column%s align="left" label="%s" prop="%s" width="120">
`, sortAttr, field.FieldDesc, field.FieldJson)
result += fmt.Sprintf(` <template #default="scope">{{ formatBoolean(scope.row.%s) }}</template>
`, field.FieldJson)
result += `</el-table-column>`
return result
} else if field.FieldType == "time.Time" {
result := fmt.Sprintf(`<el-table-column%s align="left" label="%s" prop="%s" width="180">
`, sortAttr, field.FieldDesc, field.FieldJson)
result += fmt.Sprintf(` <template #default="scope">{{ formatDate(scope.row.%s) }}</template>
`, field.FieldJson)
result += `</el-table-column>`
return result
} else if field.FieldType == "picture" {
result := fmt.Sprintf(`<el-table-column label="%s" prop="%s" width="200">
`, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
result += fmt.Sprintf(` <el-image preview-teleported style="width: 100px; height: 100px" :src="getUrl(scope.row.%s)" fit="cover"/>
`, field.FieldJson)
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.FieldType == "pictures" {
result := fmt.Sprintf(`<el-table-column label="%s" prop="%s" width="200">
`, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
result += ` <div class="multiple-img-box">
`
result += fmt.Sprintf(` <el-image preview-teleported v-for="(item,index) in scope.row.%s" :key="index" style="width: 80px; height: 80px" :src="getUrl(item)" fit="cover"/>
`, field.FieldJson)
result += ` </div>
`
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.FieldType == "video" {
result := fmt.Sprintf(`<el-table-column label="%s" prop="%s" width="200">
`, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
result += ` <video style="width: 100px; height: 100px" muted preload="metadata">
`
result += fmt.Sprintf(` <source :src="getUrl(scope.row.%s) + '#t=1'">
`, field.FieldJson)
result += ` </video>
`
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.FieldType == "richtext" {
result := fmt.Sprintf(`<el-table-column label="%s" prop="%s" width="200">
`, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
result += ` [富文本内容]
`
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.FieldType == "file" {
result := fmt.Sprintf(`<el-table-column label="%s" prop="%s" width="200">
`, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
result += ` <div class="file-list">
`
result += fmt.Sprintf(` <el-tag v-for="file in scope.row.%s" :key="file.uid" @click="onDownloadFile(file.url)">{{ file.name }}</el-tag>
`, field.FieldJson)
result += ` </div>
`
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.FieldType == "json" {
result := fmt.Sprintf(`<el-table-column label="%s" prop="%s" width="200">
`, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
result += ` [JSON]
`
result += ` </template>
`
result += `</el-table-column>`
return result
} else if field.FieldType == "array" {
result := fmt.Sprintf(`<el-table-column label="%s" prop="%s" width="200">
`, field.FieldDesc, field.FieldJson)
result += ` <template #default="scope">
`
result += fmt.Sprintf(` <ArrayCtrl v-model="scope.row.%s"/>
`, field.FieldJson)
result += ` </template>
`
result += `</el-table-column>`
return result
}
return fmt.Sprintf(`<el-table-column%s align="left" label="%s" prop="%s" width="120" />
`, sortAttr, field.FieldDesc, field.FieldJson)
}
// GenerateFormItem 生成表单项
func GenerateFormItem(field AutoCodeField) string {
result := fmt.Sprintf(`<el-form-item label="%s:" prop="%s">
`, field.FieldDesc, field.FieldJson)
if field.CheckDataSource {
multipleAttr := ""
if field.DataSource.Association == 2 {
multipleAttr = " multiple"
}
result += fmt.Sprintf(` <el-select%s v-model="formData.%s" placeholder="请选择%s" filterable style="width:100%%" :clearable="%v">
`, multipleAttr, field.FieldJson, field.FieldDesc, field.Clearable)
result += fmt.Sprintf(` <el-option v-for="(item,key) in dataSource.%s" :key="key" :label="item.label" :value="item.value" />
`, field.FieldJson)
result += ` </el-select>
`
} else {
switch field.FieldType {
case "bool":
result += fmt.Sprintf(` <el-switch v-model="formData.%s" active-color="#13ce66" inactive-color="#ff4949" active-text="是" inactive-text="否" clearable ></el-switch>
`, field.FieldJson)
case "string":
if field.DictType != "" {
result += fmt.Sprintf(` <el-tree-select v-model="formData.%s" placeholder="请选择%s" :data="%sOptions" style="width:100%%" filterable :clearable="%v" check-strictly></el-tree-select>
`, field.FieldJson, field.FieldDesc, field.DictType, field.Clearable)
} else {
result += fmt.Sprintf(` <el-input v-model="formData.%s" :clearable="%v" placeholder="请输入%s" />
`, field.FieldJson, field.Clearable, field.FieldDesc)
}
case "richtext":
result += fmt.Sprintf(` <RichEdit v-model="formData.%s"/>
`, field.FieldJson)
case "json":
result += fmt.Sprintf(` // 此字段为json结构可以前端自行控制展示和数据绑定模式 需绑定json的key为 formData.%s 后端会按照json的类型进行存取
`, field.FieldJson)
result += fmt.Sprintf(` {{ formData.%s }}
`, field.FieldJson)
case "array":
if field.DictType != "" {
result += fmt.Sprintf(` <el-select multiple v-model="formData.%s" placeholder="请选择%s" filterable style="width:100%%" :clearable="%v">
`, field.FieldJson, field.FieldDesc, field.Clearable)
result += fmt.Sprintf(` <el-option v-for="(item,key) in %sOptions" :key="key" :label="item.label" :value="item.value" />
`, field.DictType)
result += ` </el-select>
`
} else {
result += fmt.Sprintf(` <ArrayCtrl v-model="formData.%s" editable/>
`, field.FieldJson)
}
case "int":
result += fmt.Sprintf(` <el-input v-model.number="formData.%s" :clearable="%v" placeholder="请输入%s" />
`, field.FieldJson, field.Clearable, field.FieldDesc)
case "time.Time":
result += fmt.Sprintf(` <el-date-picker v-model="formData.%s" type="date" style="width:100%%" placeholder="选择日期" :clearable="%v" />
`, field.FieldJson, field.Clearable)
case "float64":
result += fmt.Sprintf(` <el-input-number v-model="formData.%s" style="width:100%%" :precision="2" :clearable="%v" />
`, field.FieldJson, field.Clearable)
case "enum":
result += fmt.Sprintf(` <el-select v-model="formData.%s" placeholder="请选择%s" style="width:100%%" filterable :clearable="%v">
`, field.FieldJson, field.FieldDesc, field.Clearable)
result += fmt.Sprintf(` <el-option v-for="item in [%s]" :key="item" :label="item" :value="item" />
`, field.DataTypeLong)
result += ` </el-select>
`
case "picture":
result += fmt.Sprintf(` <SelectImage v-model="formData.%s" file-type="image"/>
`, field.FieldJson)
case "pictures":
result += fmt.Sprintf(` <SelectImage multiple v-model="formData.%s" file-type="image"/>
`, field.FieldJson)
case "video":
result += fmt.Sprintf(` <SelectImage v-model="formData.%s" file-type="video"/>
`, field.FieldJson)
case "file":
result += fmt.Sprintf(` <SelectFile v-model="formData.%s" />
`, field.FieldJson)
}
}
result += `</el-form-item>`
return result
}
// GenerateDescriptionItem 生成描述项
func GenerateDescriptionItem(field AutoCodeField) string {
result := fmt.Sprintf(`<el-descriptions-item label="%s">
`, field.FieldDesc)
if field.CheckDataSource {
result += ` <template #default="scope">
`
if field.DataSource.Association == 2 {
result += fmt.Sprintf(` <el-tag v-for="(item,key) in filterDataSource(dataSource.%s,detailForm.%s)" :key="key">
`, field.FieldJson, field.FieldJson)
result += ` {{ item }}
`
result += ` </el-tag>
`
} else {
result += fmt.Sprintf(` <span>{{ filterDataSource(dataSource.%s,detailForm.%s) }}</span>
`, field.FieldJson, field.FieldJson)
}
result += ` </template>
`
} else if field.FieldType != "picture" && field.FieldType != "pictures" &&
field.FieldType != "file" && field.FieldType != "array" &&
field.FieldType != "richtext" {
result += fmt.Sprintf(` {{ detailForm.%s }}
`, field.FieldJson)
} else {
switch field.FieldType {
case "picture":
result += fmt.Sprintf(` <el-image style="width: 50px; height: 50px" :preview-src-list="returnArrImg(detailForm.%s)" :src="getUrl(detailForm.%s)" fit="cover" />
`, field.FieldJson, field.FieldJson)
case "array":
result += fmt.Sprintf(` <ArrayCtrl v-model="detailForm.%s"/>
`, field.FieldJson)
case "pictures":
result += fmt.Sprintf(` <el-image style="width: 50px; height: 50px; margin-right: 10px" :preview-src-list="returnArrImg(detailForm.%s)" :initial-index="index" v-for="(item,index) in detailForm.%s" :key="index" :src="getUrl(item)" fit="cover" />
`, field.FieldJson, field.FieldJson)
case "richtext":
result += fmt.Sprintf(` <RichView v-model="detailForm.%s" />
`, field.FieldJson)
case "file":
result += fmt.Sprintf(` <div class="fileBtn" v-for="(item,index) in detailForm.%s" :key="index">
`, field.FieldJson)
result += ` <el-button type="primary" text bg @click="onDownloadFile(item.url)">
`
result += ` <el-icon style="margin-right: 5px"><Download /></el-icon>
`
result += ` {{ item.name }}
`
result += ` </el-button>
`
result += ` </div>
`
}
}
result += `</el-descriptions-item>`
return result
}
// GenerateDefaultFormValue 生成默认表单值
func GenerateDefaultFormValue(field AutoCodeField) string {
var defaultValue string
switch field.FieldType {
case "bool":
defaultValue = "false"
case "string", "richtext":
defaultValue = "''"
case "int":
if field.DataSource != nil {
defaultValue = "undefined"
} else {
defaultValue = "0"
}
case "time.Time":
defaultValue = "new Date()"
case "float64":
defaultValue = "0"
case "picture", "video":
defaultValue = "\"\""
case "pictures", "file", "array":
defaultValue = "[]"
case "json":
defaultValue = "{}"
default:
defaultValue = "null"
}
return fmt.Sprintf(`%s: %s,`, field.FieldJson, defaultValue)
}
// GenerateSearchField 生成搜索字段
func GenerateSearchField(field AutoCodeField) string {
if field.FieldSearchType == "" {
return ""
}
var result string
if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" {
if field.FieldType == "time.Time" {
result = fmt.Sprintf("%sRange []time.Time `json:\"%sRange\" form:\"%sRange[]\"`",
field.FieldName, field.FieldJson, field.FieldJson)
} else {
startField := fmt.Sprintf("Start%s *%s `json:\"start%s\" form:\"start%s\"`",
field.FieldName, field.FieldType, field.FieldName, field.FieldName)
endField := fmt.Sprintf("End%s *%s `json:\"end%s\" form:\"end%s\"`",
field.FieldName, field.FieldType, field.FieldName, field.FieldName)
result = startField + "\n" + endField
}
} else if field.FieldSearchType == "IN" || field.FieldSearchType == "NOT IN" {
// IN 和 NOT IN 需要数组类型
result = fmt.Sprintf("%s []%s `json:\"%s\" form:\"%s\"`",
field.FieldName, field.FieldType, field.FieldJson, field.FieldJson)
} else {
if field.FieldType == "enum" || field.FieldType == "picture" ||
field.FieldType == "pictures" || field.FieldType == "video" ||
field.FieldType == "json" || field.FieldType == "richtext" ||
field.FieldType == "array" || field.FieldType == "file" {
result = fmt.Sprintf("%s string `json:\"%s\" form:\"%s\"` ",
field.FieldName, field.FieldJson, field.FieldJson)
} else {
result = fmt.Sprintf("%s *%s `json:\"%s\" form:\"%s\"` ",
field.FieldName, field.FieldType, field.FieldJson, field.FieldJson)
}
}
return result
}
// ==================== React/TypeScript 模板函数 ====================
// GenerateTSType Go类型转TypeScript类型
func GenerateTSType(goType string) string {
typeMap := map[string]string{
"int": "number",
"int8": "number",
"int16": "number",
"int32": "number",
"int64": "number",
"uint": "number",
"uint8": "number",
"uint16": "number",
"uint32": "number",
"uint64": "number",
"float32": "number",
"float64": "number",
"string": "string",
"bool": "boolean",
"time.Time": "string",
"datatypes.JSON": "Record<string, any>",
"json": "Record<string, any>",
"picture": "string",
"pictures": "string[]",
"video": "string",
"file": "Array<{ name: string; url: string; uid: string }>",
"array": "any[]",
"richtext": "string",
"enum": "string",
}
if tsType, ok := typeMap[goType]; ok {
return tsType
}
// 处理指针类型
if strings.HasPrefix(goType, "*") {
return GenerateTSType(goType[1:])
}
return "any"
}
// GenerateReactSearchFormItem 生成React搜索表单项
func GenerateReactSearchFormItem(field AutoCodeField) string {
var result string
switch {
case field.FieldType == "bool":
result = fmt.Sprintf(`<Form.Item name="%s" label="%s">
<Select placeholder="请选择" allowClear>
<Select.Option value={true}>是</Select.Option>
<Select.Option value={false}>否</Select.Option>
</Select>
</Form.Item>`, field.FieldJson, field.FieldDesc)
case field.DictType != "":
result = fmt.Sprintf(`<Form.Item name="%s" label="%s">
<Select placeholder="请选择%s" allowClear options={%sOptions} />
</Form.Item>`, field.FieldJson, field.FieldDesc, field.FieldDesc, field.DictType)
case field.CheckDataSource:
result = fmt.Sprintf(`<Form.Item name="%s" label="%s">
<Select placeholder="请选择%s" allowClear options={dataSource['%s']} fieldNames={{ label: 'label', value: 'value' }} />
</Form.Item>`, field.FieldJson, field.FieldDesc, field.FieldDesc, field.FieldJson)
case field.FieldType == "time.Time":
if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" {
result = fmt.Sprintf(`<Form.Item name="%sRange" label="%s">
<DatePicker.RangePicker showTime />
</Form.Item>`, field.FieldJson, field.FieldDesc)
} else {
result = fmt.Sprintf(`<Form.Item name="%s" label="%s">
<DatePicker showTime />
</Form.Item>`, field.FieldJson, field.FieldDesc)
}
case field.FieldType == "int" || field.FieldType == "float64":
if field.FieldSearchType == "BETWEEN" || field.FieldSearchType == "NOT BETWEEN" {
result = fmt.Sprintf(`<Form.Item label="%s">
<Input.Group compact>
<Form.Item name="start%s" noStyle>
<Input style={{ width: 100 }} placeholder="最小值" />
</Form.Item>
<Input style={{ width: 30, borderLeft: 0, borderRight: 0, pointerEvents: 'none' }} placeholder="~" disabled />
<Form.Item name="end%s" noStyle>
<Input style={{ width: 100 }} placeholder="最大值" />
</Form.Item>
</Input.Group>
</Form.Item>`, field.FieldDesc, field.FieldName, field.FieldName)
} else {
result = fmt.Sprintf(`<Form.Item name="%s" label="%s">
<Input placeholder="请输入%s" allowClear />
</Form.Item>`, field.FieldJson, field.FieldDesc, field.FieldDesc)
}
default:
result = fmt.Sprintf(`<Form.Item name="%s" label="%s">
<Input placeholder="请输入%s" allowClear />
</Form.Item>`, field.FieldJson, field.FieldDesc, field.FieldDesc)
}
return result
}
// GenerateReactTableColumn 生成React表格列配置
func GenerateReactTableColumn(field AutoCodeField) string {
sortable := ""
if field.Sort {
sortable = "\n\t\t\tsorter: true,"
}
switch {
case field.CheckDataSource:
if field.DataSource != nil && field.DataSource.Association == 2 {
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 120,%s
render: (val) => val?.map((v: any) => <Tag key={v}>{dataSource['%s']?.find((d: any) => d.value === v)?.label || v}</Tag>),
},`, field.FieldDesc, field.FieldJson, field.FieldJson, sortable, field.FieldJson)
}
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 120,%s
render: (val) => dataSource['%s']?.find((d: any) => d.value === val)?.label || val,
},`, field.FieldDesc, field.FieldJson, field.FieldJson, sortable, field.FieldJson)
case field.DictType != "":
if field.FieldType == "array" {
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 120,%s
render: (val) => val?.map((v: any) => <Tag key={v}>{%sOptions?.find((d: any) => d.value === v)?.label || v}</Tag>),
},`, field.FieldDesc, field.FieldJson, field.FieldJson, sortable, field.DictType)
}
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 120,%s
render: (val) => %sOptions?.find((d: any) => d.value === val)?.label || val,
},`, field.FieldDesc, field.FieldJson, field.FieldJson, sortable, field.DictType)
case field.FieldType == "bool":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 80,%s
render: (val) => val ? '是' : '否',
},`, field.FieldDesc, field.FieldJson, field.FieldJson, sortable)
case field.FieldType == "time.Time":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 180,%s
render: (val) => val ? dayjs(val).format('YYYY-MM-DD HH:mm:ss') : '-',
},`, field.FieldDesc, field.FieldJson, field.FieldJson, sortable)
case field.FieldType == "picture":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 100,
render: (val) => val ? <Image width={50} height={50} src={val} /> : '-',
},`, field.FieldDesc, field.FieldJson, field.FieldJson)
case field.FieldType == "pictures":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 200,
render: (val) => val?.map((url: string, i: number) => <Image key={i} width={50} height={50} src={url} style={{ marginRight: 4 }} />),
},`, field.FieldDesc, field.FieldJson, field.FieldJson)
case field.FieldType == "file":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 200,
render: (val) => val?.map((f: any) => <Tag key={f.uid}>{f.name}</Tag>),
},`, field.FieldDesc, field.FieldJson, field.FieldJson)
case field.FieldType == "richtext":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 100,
render: () => '[富文本]',
},`, field.FieldDesc, field.FieldJson, field.FieldJson)
case field.FieldType == "json":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 100,
render: () => '[JSON]',
},`, field.FieldDesc, field.FieldJson, field.FieldJson)
default:
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
key: '%s',
width: 120,%s
},`, field.FieldDesc, field.FieldJson, field.FieldJson, sortable)
}
}
// GenerateReactFormItem 生成React表单项
func GenerateReactFormItem(field AutoCodeField) string {
rules := ""
if field.Require {
rules = fmt.Sprintf(`
rules={[{ required: true, message: '%s' }]}`, field.ErrorText)
}
switch {
case field.CheckDataSource:
multiple := ""
if field.DataSource != nil && field.DataSource.Association == 2 {
multiple = " mode=\"multiple\""
}
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<Select%s placeholder="请选择%s" allowClear options={dataSource['%s']} fieldNames={{ label: 'label', value: 'value' }} />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, multiple, field.FieldDesc, field.FieldJson)
case field.FieldType == "bool":
return fmt.Sprintf(`<Form.Item name="%s" label="%s" valuePropName="checked"%s>
<Switch checkedChildren="是" unCheckedChildren="否" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules)
case field.DictType != "":
multiple := ""
if field.FieldType == "array" {
multiple = " mode=\"multiple\""
}
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<Select%s placeholder="请选择%s" allowClear options={%sOptions} />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, multiple, field.FieldDesc, field.DictType)
case field.FieldType == "richtext":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
{/* TODO: 使用富文本编辑器组件 */}
<Input.TextArea rows={4} placeholder="请输入%s" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, field.FieldDesc)
case field.FieldType == "json":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
{/* TODO: JSON编辑器 */}
<Input.TextArea rows={4} placeholder="请输入JSON" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules)
case field.FieldType == "array":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<Select mode="tags" placeholder="请输入%s" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, field.FieldDesc)
case field.FieldType == "int":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<InputNumber style={{ width: '100%%' }} placeholder="请输入%s" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, field.FieldDesc)
case field.FieldType == "float64":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<InputNumber style={{ width: '100%%' }} precision={2} placeholder="请输入%s" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, field.FieldDesc)
case field.FieldType == "time.Time":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<DatePicker style={{ width: '100%%' }} showTime placeholder="请选择%s" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, field.FieldDesc)
case field.FieldType == "enum":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<Select placeholder="请选择%s" allowClear>
{/* TODO: 根据枚举值生成选项 */}
</Select>
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, field.FieldDesc)
case field.FieldType == "picture":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
{/* TODO: 图片上传组件 */}
<Input placeholder="请输入图片URL" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules)
case field.FieldType == "pictures":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
{/* TODO: 多图片上传组件 */}
<Input placeholder="请输入图片URL" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules)
case field.FieldType == "video":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
{/* TODO: 视频上传组件 */}
<Input placeholder="请输入视频URL" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules)
case field.FieldType == "file":
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
{/* TODO: 文件上传组件 */}
<Input placeholder="请上传文件" />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules)
default:
return fmt.Sprintf(`<Form.Item name="%s" label="%s"%s>
<Input placeholder="请输入%s" allowClear />
</Form.Item>`, field.FieldJson, field.FieldDesc, rules, field.FieldDesc)
}
}
// GenerateReactDescriptionItem 生成React描述项
// 根据字段配置生成对应的详情展示项,支持多种字段类型
func GenerateReactDescriptionItem(field AutoCodeField) string {
switch {
// 数据源字段 - 从外部数据源获取显示值
case field.CheckDataSource:
if field.DataSource != nil && field.DataSource.Association == 2 {
// 多选数据源 - 显示为多个标签
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s?.map((v: any) => <Tag key={v}>{dataSource['%s']?.find((d: any) => d.value === v)?.label || v}</Tag>)}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson, field.FieldJson)
}
// 单选数据源 - 显示标签文本
return fmt.Sprintf(`<Descriptions.Item label="%s">
{dataSource['%s']?.find((d: any) => d.value === currentRecord?.%s)?.label || currentRecord?.%s}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson, field.FieldJson, field.FieldJson)
// 字典类型字段 - 从字典获取显示值
case field.DictType != "":
if field.FieldType == "array" {
// 数组类型字典 - 显示为多个标签
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s?.map((v: any) => <Tag key={v}>{get%sLabel(v)}</Tag>)}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson, ToPascalCase(field.DictType))
}
// 单值字典 - 显示字典标签
return fmt.Sprintf(`<Descriptions.Item label="%s">
{get%sLabel(currentRecord?.%s)}
</Descriptions.Item>`, field.FieldDesc, ToPascalCase(field.DictType), field.FieldJson)
// 布尔类型 - 显示是/否
case field.FieldType == "bool":
return fmt.Sprintf(`<Descriptions.Item label="%s">{currentRecord?.%s ? '是' : '否'}</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// 时间类型 - 格式化显示
case field.FieldType == "time.Time":
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s ? dayjs(currentRecord.%s).format('YYYY-MM-DD HH:mm:ss') : '-'}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson, field.FieldJson)
// 单图片类型 - 显示图片预览
case field.FieldType == "picture":
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s && <Image width={100} src={currentRecord.%s} />}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson, field.FieldJson)
// 多图片类型 - 显示图片列表
case field.FieldType == "pictures":
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s?.map((url: string, i: number) => <Image key={i} width={50} src={url} style={{ marginRight: 4 }} />)}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// 视频类型 - 显示视频播放器
case field.FieldType == "video":
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s && (
<video width={200} controls src={currentRecord.%s}>
您的浏览器不支持视频播放
</video>
)}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson, field.FieldJson)
// 文件类型 - 显示文件列表带下载
case field.FieldType == "file":
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s?.map((f: any, i: number) => (
<Tag key={f.uid || i}>
<a href={f.url} target="_blank" rel="noopener noreferrer">{f.name}</a>
</Tag>
))}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// 富文本类型 - 渲染HTML内容
case field.FieldType == "richtext":
return fmt.Sprintf(`<Descriptions.Item label="%s">
<div dangerouslySetInnerHTML={{ __html: currentRecord?.%s || '' }} />
</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// JSON类型 - 格式化显示JSON
case field.FieldType == "json":
return fmt.Sprintf(`<Descriptions.Item label="%s">
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>{JSON.stringify(currentRecord?.%s, null, 2)}</pre>
</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// 数组类型(无字典) - 显示为标签列表
case field.FieldType == "array":
return fmt.Sprintf(`<Descriptions.Item label="%s">
{currentRecord?.%s?.map((v: any, i: number) => <Tag key={i}>{v}</Tag>)}
</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// 枚举类型 - 直接显示值
case field.FieldType == "enum":
return fmt.Sprintf(`<Descriptions.Item label="%s">{currentRecord?.%s}</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// 整数类型 - 显示数字
case field.FieldType == "int":
return fmt.Sprintf(`<Descriptions.Item label="%s">{currentRecord?.%s ?? '-'}</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
// 浮点数类型 - 保留两位小数
case field.FieldType == "float64":
return fmt.Sprintf(`<Descriptions.Item label="%s">{currentRecord?.%s !== undefined ? currentRecord.%s.toFixed(2) : '-'}</Descriptions.Item>`, field.FieldDesc, field.FieldJson, field.FieldJson)
// 默认类型 - 直接显示值
default:
return fmt.Sprintf(`<Descriptions.Item label="%s">{currentRecord?.%s ?? '-'}</Descriptions.Item>`, field.FieldDesc, field.FieldJson)
}
}
// GenerateReactDefaultValue 生成React表单默认值
func GenerateReactDefaultValue(field AutoCodeField) string {
switch field.FieldType {
case "bool":
return fmt.Sprintf(`%s: false,`, field.FieldJson)
case "string", "richtext", "picture", "video":
return fmt.Sprintf(`%s: '',`, field.FieldJson)
case "int", "float64":
if field.DataSource != nil {
return fmt.Sprintf(`%s: undefined,`, field.FieldJson)
}
return fmt.Sprintf(`%s: 0,`, field.FieldJson)
case "time.Time":
return fmt.Sprintf(`%s: undefined,`, field.FieldJson)
case "pictures", "file", "array":
return fmt.Sprintf(`%s: [],`, field.FieldJson)
case "json":
return fmt.Sprintf(`%s: {},`, field.FieldJson)
default:
return fmt.Sprintf(`%s: undefined,`, field.FieldJson)
}
}
// GenerateReactProTableColumn 生成 Ant Design Pro Table 列配置
func GenerateReactProTableColumn(field AutoCodeField) string {
sorter := ""
if field.Sort {
sorter = "\n\t\t\tsorter: true,"
}
// 搜索配置
hideInSearch := ""
if field.FieldSearchType == "" {
hideInSearch = "\n\t\t\thideInSearch: true,"
}
switch {
case field.CheckDataSource:
valueType := "select"
if field.DataSource != nil && field.DataSource.Association == 2 {
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 150,%s%s
valueType: '%s',
fieldProps: {
mode: 'multiple',
options: dataSource['%s'],
},
render: (_, record) => record.%s?.map((v: any) => (
<Tag key={v}>{dataSource['%s']?.find((d: any) => d.value === v)?.label || v}</Tag>
)),
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch, valueType, field.FieldJson, field.FieldJson, field.FieldJson)
}
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 150,%s%s
valueType: '%s',
fieldProps: {
options: dataSource['%s'],
},
render: (_, record) => dataSource['%s']?.find((d: any) => d.value === record.%s)?.label || record.%s,
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch, valueType, field.FieldJson, field.FieldJson, field.FieldJson, field.FieldJson)
case field.DictType != "":
valueType := "select"
if field.FieldType == "array" {
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 150,%s%s
valueType: '%s',
fieldProps: {
mode: 'multiple',
options: %sOptions,
},
render: (_, record) => record.%s?.map((v: any) => (
<Tag key={v}>{get%sLabel(v)}</Tag>
)),
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch, valueType, field.DictType, field.FieldJson, ToPascalCase(field.DictType))
}
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 150,%s%s
valueType: '%s',
fieldProps: {
options: %sOptions,
},
render: (_, record) => get%sLabel(record.%s),
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch, valueType, field.DictType, ToPascalCase(field.DictType), field.FieldJson)
case field.FieldType == "bool":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 80,%s%s
valueType: 'select',
valueEnum: {
true: { text: '是', status: 'Success' },
false: { text: '否', status: 'Default' },
},
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch)
case field.FieldType == "time.Time":
searchType := ""
if field.FieldSearchType == "BETWEEN" {
searchType = "\n\t\t\tvalueType: 'dateTimeRange',"
} else {
searchType = "\n\t\t\tvalueType: 'dateTime',"
}
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 180,%s%s%s
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch, searchType)
case field.FieldType == "picture":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 100,
hideInSearch: true,
render: (_, record) => record.%s ? <Image width={50} height={50} src={record.%s} /> : '-',
},`, field.FieldDesc, field.FieldJson, field.FieldJson, field.FieldJson)
case field.FieldType == "pictures":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 200,
hideInSearch: true,
render: (_, record) => record.%s?.map((url: string, i: number) => (
<Image key={i} width={50} height={50} src={url} style={{ marginRight: 4 }} />
)),
},`, field.FieldDesc, field.FieldJson, field.FieldJson)
case field.FieldType == "file":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 200,
hideInSearch: true,
render: (_, record) => record.%s?.map((f: any) => <Tag key={f.uid}>{f.name}</Tag>),
},`, field.FieldDesc, field.FieldJson, field.FieldJson)
case field.FieldType == "richtext":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 100,
hideInSearch: true,
render: () => <Typography.Text type="secondary">[富文本]</Typography.Text>,
},`, field.FieldDesc, field.FieldJson)
case field.FieldType == "json":
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 100,
hideInSearch: true,
render: () => <Typography.Text type="secondary">[JSON]</Typography.Text>,
},`, field.FieldDesc, field.FieldJson)
case field.FieldType == "int", field.FieldType == "float64":
valueType := "digit"
if field.FieldSearchType == "BETWEEN" {
valueType = "digitRange"
}
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 120,%s%s
valueType: '%s',
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch, valueType)
default:
return fmt.Sprintf(`{
title: '%s',
dataIndex: '%s',
width: 150,%s%s
ellipsis: true,
},`, field.FieldDesc, field.FieldJson, sorter, hideInSearch)
}
}
// ==================== 自定义查询方法模板函数 ====================
// GenerateCustomMethodInterface 生成自定义方法的接口定义
// 用于 biz 层 Repository 接口
func GenerateCustomMethodInterface(methods []*CustomMethod, structName, pkgName string) string {
if len(methods) == 0 {
return ""
}
var builder strings.Builder
builder.WriteString("\n\t// 自定义查询方法\n")
for _, method := range methods {
// 生成方法签名
params := generateMethodParams(method.Params)
returnType := generateReturnType(method.ReturnType, structName, pkgName)
comment := method.Description
if comment == "" {
comment = method.Name
}
builder.WriteString(fmt.Sprintf("\t// %s %s\n", method.Name, comment))
builder.WriteString(fmt.Sprintf("\t%s(ctx context.Context%s) %s\n", method.Name, params, returnType))
}
return builder.String()
}
// GenerateCustomMethodImpl 生成自定义方法的实现
// 用于 data 层 Repository 实现
func GenerateCustomMethodImpl(methods []*CustomMethod, structName, pkgName, abbreviation string) string {
if len(methods) == 0 {
return ""
}
var builder strings.Builder
for _, method := range methods {
// 生成方法签名
params := generateMethodParams(method.Params)
returnType := generateReturnType(method.ReturnType, structName, pkgName)
comment := method.Description
if comment == "" {
comment = method.Name
}
builder.WriteString(fmt.Sprintf("\n// %s %s\n", method.Name, comment))
builder.WriteString(fmt.Sprintf("func (r *%sRepo) %s(ctx context.Context%s) %s {\n", abbreviation, method.Name, params, returnType))
// 生成方法体
builder.WriteString(fmt.Sprintf("\tt := query.%s\n", structName))
builder.WriteString("\tq := t.WithContext(ctx)\n\n")
// 生成查询条件
conditions := generateConditions(method.Conditions, method.Params)
if conditions != "" {
builder.WriteString(conditions)
}
// 生成排序
if method.OrderBy != "" {
builder.WriteString(generateOrderBy(method.OrderBy))
}
// 生成限制
if method.Limit > 0 {
builder.WriteString(fmt.Sprintf("\tq = q.Limit(%d)\n", method.Limit))
}
// 生成返回语句
builder.WriteString(generateReturnStatement(method.ReturnType, structName, pkgName))
builder.WriteString("}\n")
}
return builder.String()
}
// generateMethodParams 生成方法参数列表
func generateMethodParams(params []*CustomMethodParam) string {
if len(params) == 0 {
return ""
}
var parts []string
for _, p := range params {
parts = append(parts, fmt.Sprintf("%s %s", p.Name, p.Type))
}
return ", " + strings.Join(parts, ", ")
}
// generateReturnType 生成返回类型
func generateReturnType(returnType, structName, pkgName string) string {
switch returnType {
case "single":
return fmt.Sprintf("(*%s.%s, error)", pkgName, structName)
case "list":
return fmt.Sprintf("([]*%s.%s, error)", pkgName, structName)
case "count":
return "(int64, error)"
case "exists":
return "(bool, error)"
default:
return fmt.Sprintf("(*%s.%s, error)", pkgName, structName)
}
}
// generateConditions 生成查询条件
func generateConditions(conditions []*CustomCondition, params []*CustomMethodParam) string {
if len(conditions) == 0 {
return ""
}
var builder strings.Builder
builder.WriteString("\t// 查询条件\n")
for _, cond := range conditions {
// 查找对应的参数类型
var paramType string
for _, p := range params {
if p.Name == cond.ParamName {
paramType = p.Type
break
}
}
condStr := generateSingleCondition(cond, paramType)
if condStr != "" {
builder.WriteString(condStr)
}
}
return builder.String()
}
// generateSingleCondition 生成单个查询条件
func generateSingleCondition(cond *CustomCondition, paramType string) string {
fieldName := cond.FieldName
paramName := cond.ParamName
switch cond.Operator {
case "eq", "=":
return fmt.Sprintf("\tq = q.Where(t.%s.Eq(%s))\n", fieldName, paramName)
case "neq", "!=", "<>":
return fmt.Sprintf("\tq = q.Where(t.%s.Neq(%s))\n", fieldName, paramName)
case "gt", ">":
return fmt.Sprintf("\tq = q.Where(t.%s.Gt(%s))\n", fieldName, paramName)
case "gte", ">=":
return fmt.Sprintf("\tq = q.Where(t.%s.Gte(%s))\n", fieldName, paramName)
case "lt", "<":
return fmt.Sprintf("\tq = q.Where(t.%s.Lt(%s))\n", fieldName, paramName)
case "lte", "<=":
return fmt.Sprintf("\tq = q.Where(t.%s.Lte(%s))\n", fieldName, paramName)
case "like":
return fmt.Sprintf("\tq = q.Where(t.%s.Like(\"%%"+"\" + %s + \""+"%%\"))\n", fieldName, paramName)
case "in":
return fmt.Sprintf("\tq = q.Where(t.%s.In(%s...))\n", fieldName, paramName)
case "notIn":
return fmt.Sprintf("\tq = q.Where(t.%s.NotIn(%s...))\n", fieldName, paramName)
case "between":
// between 需要两个参数,假设参数名为 paramName + "Start" 和 paramName + "End"
return fmt.Sprintf("\tq = q.Where(t.%s.Between(%sStart, %sEnd))\n", fieldName, paramName, paramName)
case "isNull":
return fmt.Sprintf("\tq = q.Where(t.%s.IsNull())\n", fieldName)
case "isNotNull":
return fmt.Sprintf("\tq = q.Where(t.%s.IsNotNull())\n", fieldName)
default:
return fmt.Sprintf("\tq = q.Where(t.%s.Eq(%s))\n", fieldName, paramName)
}
}
// generateOrderBy 生成排序语句
func generateOrderBy(orderBy string) string {
if orderBy == "" {
return ""
}
// 解析排序字段,格式: "field_name DESC" 或 "field_name ASC" 或 "FieldName"
parts := strings.Fields(orderBy)
if len(parts) == 0 {
return ""
}
fieldName := ToPascalCase(parts[0])
direction := "Asc"
if len(parts) > 1 && strings.ToUpper(parts[1]) == "DESC" {
direction = "Desc"
}
return fmt.Sprintf("\tq = q.Order(t.%s.%s())\n", fieldName, direction)
}
// generateReturnStatement 生成返回语句
func generateReturnStatement(returnType, structName, pkgName string) string {
switch returnType {
case "single":
return fmt.Sprintf(`
m, err := q.First()
if err != nil {
return nil, err
}
return toBiz%s(m), nil
`, structName)
case "list":
return fmt.Sprintf(`
models, err := q.Find()
if err != nil {
return nil, err
}
result := make([]*%s.%s, len(models))
for i, m := range models {
result[i] = toBiz%s(m)
}
return result, nil
`, pkgName, structName, structName)
case "count":
return `
count, err := q.Count()
return count, err
`
case "exists":
return `
count, err := q.Count()
if err != nil {
return false, err
}
return count > 0, nil
`
default:
return fmt.Sprintf(`
m, err := q.First()
if err != nil {
return nil, err
}
return toBiz%s(m), nil
`, structName)
}
}