diff --git a/server/internal/library/hggen/views/column.go b/server/internal/library/hggen/views/column.go index d3128e4..761f848 100644 --- a/server/internal/library/hggen/views/column.go +++ b/server/internal/library/hggen/views/column.go @@ -10,14 +10,15 @@ import ( "fmt" "strings" + "hotgo/internal/consts" + "hotgo/internal/library/hggen/internal/cmd/gendao" + "hotgo/internal/model/input/sysin" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" - "hotgo/internal/consts" - "hotgo/internal/library/hggen/internal/cmd/gendao" - "hotgo/internal/model/input/sysin" ) // DoTableColumns 获取指定表生成字段列表 @@ -63,11 +64,10 @@ func DoTableColumns(ctx context.Context, in *sysin.GenCodesColumnListInp, config LEFT JOIN pg_constraint pk ON pk.conrelid = c.oid AND pk.contype = 'p' AND a.attnum = ANY(pk.conkey) LEFT JOIN pg_constraint uk ON uk.conrelid = c.oid AND uk.contype = 'u' AND a.attnum = ANY(uk.conkey) WHERE n.nspname = 'public' - AND c.relname = '%s' + AND c.relname = '` + in.Table + `' AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum` - sql = fmt.Sprintf(sql, in.Table) } else { // MySQL: 使用information_schema.COLUMNS sql = fmt.Sprintf("SELECT ORDINAL_POSITION as id, COLUMN_NAME as name, COLUMN_COMMENT as dc, DATA_TYPE as dataType, COLUMN_TYPE as sqlType, CHARACTER_MAXIMUM_LENGTH as length, IS_NULLABLE as isAllowNull, COLUMN_DEFAULT as defaultValue, COLUMN_KEY as `index`, EXTRA as extra FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s' ORDER BY id ASC", conf.Name, in.Table) diff --git a/server/internal/library/hggen/views/curd.go b/server/internal/library/hggen/views/curd.go index 2b19c57..e49d4b2 100644 --- a/server/internal/library/hggen/views/curd.go +++ b/server/internal/library/hggen/views/curd.go @@ -896,9 +896,15 @@ func (l *gCurd) generateSqlContent(ctx context.Context, in *CurdPreviewInput) (e "menuTable": config.Prefix + "admin_menu", "mainComponent": "LAYOUT", } - genFile = new(sysin.GenFile) + genFile = new(sysin.GenFile) + templateName = "source.sql.template" ) + // 根据数据库类型选择不同的模板 + if config.Type == consts.DBPgsql { + templateName = "source_pgsql.sql.template" + } + menus, err := service.AdminMenu().GetFastList(ctx) if err != nil { return err @@ -993,7 +999,7 @@ func (l *gCurd) generateSqlContent(ctx context.Context, in *CurdPreviewInput) (e } tplData["generatePath"] = genFile.Path - genFile.Content, err = in.view.Parse(ctx, name+".template", tplData) + genFile.Content, err = in.view.Parse(ctx, templateName, tplData) if err != nil { return err } diff --git a/server/internal/library/hggen/views/curd_generate_logic.go b/server/internal/library/hggen/views/curd_generate_logic.go index 4bc2028..78bc993 100644 --- a/server/internal/library/hggen/views/curd_generate_logic.go +++ b/server/internal/library/hggen/views/curd_generate_logic.go @@ -99,10 +99,10 @@ func (l *gCurd) generateLogicSwitchFields(ctx context.Context, in *CurdPreviewIn func (l *gCurd) generateLogicEdit(ctx context.Context, in *CurdPreviewInput) g.Map { var ( - data = make(g.Map) - updateBuffer = bytes.NewBuffer(nil) - insertBuffer = bytes.NewBuffer(nil) - uniqueBuffer = bytes.NewBuffer(nil) + data = make(g.Map) + updateBuffer = bytes.NewBuffer(nil) + insertBuffer = bytes.NewBuffer(nil) + uniqueBuffer = bytes.NewBuffer(nil) validationBuffer = bytes.NewBuffer(nil) ) @@ -116,7 +116,7 @@ func (l *gCurd) generateLogicEdit(ctx context.Context, in *CurdPreviewInput) g.M } if field.Unique { - uniqueBuffer.WriteString(fmt.Sprintf(LogicEditUnique, field.GoName, in.In.DaoName, in.In.DaoName, field.GoName, field.GoName, field.Dc,in.pk.GoName)) + uniqueBuffer.WriteString(fmt.Sprintf(LogicEditUnique, field.GoName, in.In.DaoName, in.In.DaoName, field.GoName, field.GoName, field.Dc, in.pk.GoName)) } // 添加 YAML 格式验证 @@ -246,6 +246,8 @@ func (l *gCurd) generateLogicListWhereEach(buffer *bytes.Buffer, in *CurdPreview if IsNumberType(field.GoType) { linkMode = `in.` + field.GoName + ` > 0` + } else if field.GoType == GoTypeBool { + linkMode = `true` // bool 类型始终可以查询 } else if field.GoType == GoTypeGTime { linkMode = `in.` + field.GoName + ` != nil` } else if field.GoType == GoTypeJson { @@ -286,12 +288,23 @@ func (l *gCurd) generateLogicListWhereEach(buffer *bytes.Buffer, in *CurdPreview case WhereModeNotBetween: whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "NotBetween(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + "[0], in." + field.GoName + "[1])\n\t}" case WhereModeLike: - whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "Like(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}" + fieldValue := "in." + field.GoName + if field.GoType != GoTypeString && field.GoType != GoTypeBytes { + fieldValue = "gconv.String(in." + field.GoName + ")" + } + whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "Like(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", " + fieldValue + ")\n\t}" case WhereModeLikeAll: val := `"%"+in.` + field.GoName + `+"%"` + if field.GoType != GoTypeString && field.GoType != GoTypeBytes { + val = `"%"+gconv.String(in.` + field.GoName + `)+"%"` + } whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "Like(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", " + val + ")\n\t}" case WhereModeNotLike: - whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "NotLike(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", in." + field.GoName + ")\n\t}" + fieldValue := "in." + field.GoName + if field.GoType != GoTypeString && field.GoType != GoTypeBytes { + fieldValue = "gconv.String(in." + field.GoName + ")" + } + whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "NotLike(" + tablePrefix + "dao." + daoName + ".Columns()." + columnName + ", " + fieldValue + ")\n\t}" case WhereModeJsonContains: val := tablePrefix + `"JSON_CONTAINS("+dao.` + daoName + `.Columns().` + columnName + `+",?)", in.` + field.GoName whereTag = "\tif " + linkMode + " {\n\t\tmod = mod." + wherePrefix + "(" + val + ")\n\t}" diff --git a/server/internal/library/hggen/views/curd_generate_web_edit.go b/server/internal/library/hggen/views/curd_generate_web_edit.go index 970796b..25aeb10 100644 --- a/server/internal/library/hggen/views/curd_generate_web_edit.go +++ b/server/internal/library/hggen/views/curd_generate_web_edit.go @@ -9,6 +9,7 @@ import ( "bytes" "context" "fmt" + "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/text/gstr" ) @@ -32,7 +33,7 @@ func (l *gCurd) generateWebEditFormItem(ctx context.Context, in *CurdPreviewInpu } var ( - defaultComponent = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.Dc, field.TsName) + defaultComponent = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.Dc, field.TsName) component string ) @@ -45,105 +46,109 @@ func (l *gCurd) generateWebEditFormItem(ctx context.Context, in *CurdPreviewInpu component = defaultComponent case FormModeInputNumber: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.Dc, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.Dc, field.TsName) case FormModeInputTextarea: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.Dc, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.Dc, field.TsName) case FormModeInputEditor: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName, field.TsName) case FormModeInputYaml: - component = fmt.Sprintf("\n handleYamlValidationChange(isValid, '%s')\" />\n ", field.Dc, field.TsName, field.TsName, field.TsName, field.Dc, field.TsName) + component = fmt.Sprintf(" \n handleYamlValidationChange(isValid, '%s')\"\n />\n ", field.Dc, field.TsName, field.TsName, field.TsName, field.Dc, field.TsName) case FormModeInputDynamic: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) case FormModeDate: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) // case FormModeDateRange: // 必须要有两个字段,后面优化下 case FormModeTime: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) // case FormModeTimeRange: // 必须要有两个字段,后面优化下 case FormModeRadio: - component = fmt.Sprintf("\n \n \n \n ", field.Dc, field.TsName, field.TsName, field.TsName, field.TsName, in.options.dictMap[field.TsName], field.TsName, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n \n \n ", field.Dc, field.TsName, field.TsName, field.TsName, field.TsName, in.options.dictMap[field.TsName], field.TsName, field.TsName, field.TsName) case FormModeCheckbox: - component = fmt.Sprintf("\n \n \n \n \n \n ", field.Dc, field.TsName, field.TsName, in.options.dictMap[field.TsName]) + component = fmt.Sprintf(" \n \n \n \n \n \n ", field.Dc, field.TsName, field.TsName, in.options.dictMap[field.TsName]) case FormModeSelect: if in.options.dictMap[field.TsName] != nil { - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName, in.options.dictMap[field.TsName]) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName, in.options.dictMap[field.TsName]) } else { - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) } case FormModeSelectMultiple: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName, in.options.dictMap[field.TsName]) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName, in.options.dictMap[field.TsName]) case FormModeUploadImage: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) case FormModeUploadImages: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) case FormModeUploadFile: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) case FormModeUploadFiles: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) case FormModeSwitch: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) case FormModeRate: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName, field.GoName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName, field.GoName) case FormModeCitySelector: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, field.TsName) + component = fmt.Sprintf(" \n \n ", field.Dc, field.TsName, field.TsName) + case FormModePidTreeSelect: - component = fmt.Sprintf(` - - `, field.Dc, in.pk.TsName, in.options.Tree.TitleField.TsName) + component = fmt.Sprintf(` + + `, field.Dc, in.pk.TsName, in.options.Tree.TitleField.TsName) + case FormModeTreeSelect: - component = fmt.Sprintf(` - - `, field.Dc, field.TsName, field.Dc, field.TsName) + component = fmt.Sprintf(` + + `, field.Dc, field.TsName, field.Dc, field.TsName) + case FormModeCascader: - component = fmt.Sprintf(` - - `, field.Dc, field.TsName, field.Dc, field.TsName) + component = fmt.Sprintf(` + + `, field.Dc, field.TsName, field.Dc, field.TsName) + default: component = defaultComponent } - buffer.WriteString(fmt.Sprintf("%v\n\n", field.FormGridSpan, component)) + buffer.WriteString(fmt.Sprintf("\n%v\n \n\n", field.FormGridSpan, component)) } return buffer.String() } @@ -156,8 +161,6 @@ func (l *gCurd) generateWebEditScript(ctx context.Context, in *CurdPreviewInput) hasYamlField = false ) - importBuffer.WriteString(" import { ref, computed } from 'vue';\n") - // 导入字典 if in.options.DictOps.Has { importBuffer.WriteString(" import { useDictStore } from '@/store/modules/dict';\n") diff --git a/server/internal/library/hggen/views/curd_generate_web_index.go b/server/internal/library/hggen/views/curd_generate_web_index.go index 4ec37d8..2193b21 100644 --- a/server/internal/library/hggen/views/curd_generate_web_index.go +++ b/server/internal/library/hggen/views/curd_generate_web_index.go @@ -8,6 +8,7 @@ package views import ( "bytes" "context" + "github.com/gogf/gf/v2/frame/g" ) @@ -17,7 +18,7 @@ func (l *gCurd) webIndexTplData(ctx context.Context, in *CurdPreviewInput) (g.Ma importBuffer = bytes.NewBuffer(nil) importVueMethod = []string{"h", "reactive", "ref", "computed"} importApiMethod = []string{"List"} - importModelMethod = []string{"columns", "schemas"} + importModelMethod = []string{"columns", "schemas", "State"} importUtilsMethod = []string{"adaTableScrollX"} importIcons []string actionWidth int64 = 72 @@ -100,7 +101,6 @@ func (l *gCurd) webIndexTplData(ctx context.Context, in *CurdPreviewInput) (g.Ma } // 导入基础包 - importBuffer.WriteString(" import " + ImportWebMethod(importVueMethod) + " from 'vue';\n") importBuffer.WriteString(" import { useDialog, useMessage } from 'naive-ui';\n") importBuffer.WriteString(" import { BasicTable, TableAction } from '@/components/Table';\n") importBuffer.WriteString(" import { BasicForm, useForm } from '@/components/Form/index';\n") diff --git a/server/internal/library/hggen/views/curd_generate_web_model.go b/server/internal/library/hggen/views/curd_generate_web_model.go index 8f04616..4953dbf 100644 --- a/server/internal/library/hggen/views/curd_generate_web_model.go +++ b/server/internal/library/hggen/views/curd_generate_web_model.go @@ -46,8 +46,6 @@ func (l *gCurd) generateWebModelImport(ctx context.Context, in *CurdPreviewInput 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") @@ -120,19 +118,25 @@ func (l *gCurd) generateWebModelStateItems(ctx context.Context, in *CurdPreviewI dataType = parts[0] } } + + isStr := isStringType(field.TsType, dataType) + isArray := strings.HasPrefix(dataType, "_") || strings.Contains(field.SqlType, "[]") + var value = field.DefaultValue if value == nil { - value = "null" - } - if value == "" { - // 修复字符串字段为空时的处理 - if isStringType(field.TsType, dataType) { - value = "" // 模板会自动加引号变成 '' + if isArray { + value = "null" + } else if isStr { + value = "" } else { value = "null" } - } else if valueStr, ok := value.(string); ok && isStringType(field.TsType, dataType) { - // 对于字符串类型,直接使用原值,模板会自动加引号 + } + if value == "" { + if isArray || !isStr { + value = "null" + } + } else if valueStr, ok := value.(string); ok && isStr { value = valueStr } @@ -470,9 +474,14 @@ func isStringType(tsType, dataType string) bool { // 根据数据库数据类型判断 stringTypes := []string{ + // mysql "varchar", "char", "text", "longtext", "mediumtext", "tinytext", "nvarchar", "nchar", "ntext", "string", "enum", "set", + // pgsql + "bpchar", "date", "time", "timetz", "timestamp", "timestamptz", "interval", + "uuid", "bytea", "inet", "cidr", "macaddr", "macaddr8", "bit", "varbit", "json", "jsonb", + "point", "line", "lseg", "box", "path", "polygon", "circle", "money", } for _, t := range stringTypes { @@ -480,6 +489,5 @@ func isStringType(tsType, dataType string) bool { return true } } - return false } diff --git a/server/internal/library/hggen/views/curd_generate_web_view.go b/server/internal/library/hggen/views/curd_generate_web_view.go index 7e27fdc..dc932fd 100644 --- a/server/internal/library/hggen/views/curd_generate_web_view.go +++ b/server/internal/library/hggen/views/curd_generate_web_view.go @@ -9,33 +9,43 @@ import ( "bytes" "context" "fmt" + "github.com/gogf/gf/v2/frame/g" ) func (l *gCurd) webViewTplData(ctx context.Context, in *CurdPreviewInput) (data g.Map, err error) { data = make(g.Map) - data["item"] = l.generateWebViewItem(ctx, in) + item, hasUpload := l.generateWebViewItem(ctx, in) + data["item"] = item + data["hasUploadFile"] = hasUpload return } -func (l *gCurd) generateWebViewItem(ctx context.Context, in *CurdPreviewInput) string { +func (l *gCurd) generateWebViewItem(ctx context.Context, in *CurdPreviewInput) (string, bool) { buffer := bytes.NewBuffer(nil) + hasUploadFile := false + for _, field := range in.masterFields { if !field.IsEdit { continue } + // 检测是否使用了文件上传相关的组件 + if field.FormMode == FormModeUploadFile || field.FormMode == FormModeUploadFiles { + hasUploadFile = true + } + var ( - defaultComponent = fmt.Sprintf("\n \n {{ formValue.%s }}\n ", field.Dc, field.TsName) + defaultComponent = fmt.Sprintf("\n \n {{ formValue.%s }}\n ", field.Dc, field.TsName) component string ) switch field.FormMode { case FormModeInputTextarea, FormModeInputEditor: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName) + component = fmt.Sprintf("\n \n \n ", field.Dc, field.TsName) case FormModeInputYaml: - component = fmt.Sprintf("\n \n
{{ formValue.%s }}
", field.Dc, field.TsName) + component = fmt.Sprintf("\n \n
{{ formValue.%s }}
\n
", field.Dc, field.TsName) case FormModeInputDynamic: component = defaultComponent @@ -47,28 +57,28 @@ func (l *gCurd) generateWebViewItem(ctx context.Context, in *CurdPreviewInput) s component = defaultComponent case FormModeRadio, FormModeSelect: - component = fmt.Sprintf("\n {{ dict.getLabel('%s', formValue.%s) }}\n ", field.Dc, in.options.dictMap[field.TsName], field.TsName, in.options.dictMap[field.TsName], field.TsName) + component = fmt.Sprintf("\n \n {{ dict.getLabel('%s', formValue.%s) }}\n \n ", field.Dc, in.options.dictMap[field.TsName], field.TsName, in.options.dictMap[field.TsName], field.TsName) case FormModeCheckbox, FormModeSelectMultiple: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, in.options.dictMap[field.TsName], in.options.dictMap[field.TsName]) + component = fmt.Sprintf("\n \n ", field.Dc, field.TsName, in.options.dictMap[field.TsName], in.options.dictMap[field.TsName]) case FormModeUploadImage: - component = fmt.Sprintf("\n \n ", field.Dc, field.TsName) + component = fmt.Sprintf("\n \n \n ", field.Dc, field.TsName) case FormModeUploadImages: - component = fmt.Sprintf("\n \n \n \n \n \n \n \n \n ", field.Dc, field.TsName) + component = fmt.Sprintf("\n \n \n \n \n \n \n \n \n ", field.Dc, field.TsName) case FormModeUploadFile: - component = fmt.Sprintf("\n \n
\n
\n
\n
\n {{ getFileExt(formValue.%s) }}\n
\n
\n
\n
\n
", field.Dc, field.TsName, field.TsName, field.TsName) + component = fmt.Sprintf("\n \n
\n
\n
\n
\n {{ getFileExt(formValue.%s) }}\n
\n
\n
\n
\n
", field.Dc, field.TsName, field.TsName, field.TsName) case FormModeUploadFiles: - component = fmt.Sprintf("\n \n
\n \n \n
\n
\n {{\n getFileExt(item)\n }}\n
\n
\n
\n \n \n
", field.Dc, field.TsName) + component = fmt.Sprintf("\n \n
\n \n \n
\n
\n \n {{ getFileExt(item) }}\n \n
\n
\n
\n \n \n
", field.Dc, field.TsName) case FormModeSwitch: - component = fmt.Sprintf("\n ", field.Dc, field.TsName) + component = fmt.Sprintf("\n \n ", field.Dc, field.TsName) case FormModeRate: - component = fmt.Sprintf("", field.Dc, field.TsName) + component = fmt.Sprintf("\n \n ", field.Dc, field.TsName) default: component = defaultComponent @@ -76,5 +86,5 @@ func (l *gCurd) generateWebViewItem(ctx context.Context, in *CurdPreviewInput) s buffer.WriteString(" " + component + "\n\n") } - return buffer.String() + return buffer.String(), hasUploadFile } diff --git a/server/internal/library/hggen/views/utils.go b/server/internal/library/hggen/views/utils.go index e159d0e..8a2d58b 100644 --- a/server/internal/library/hggen/views/utils.go +++ b/server/internal/library/hggen/views/utils.go @@ -8,7 +8,6 @@ package views import ( "context" "fmt" - "github.com/gogf/gf/v2/util/gutil" "hotgo/internal/consts" "hotgo/internal/library/hggen/views/gohtml" "hotgo/internal/model" @@ -21,10 +20,14 @@ import ( "strings" "unicode" + "github.com/gogf/gf/v2/util/gutil" + "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gfile" + "github.com/gogf/gf/v2/os/gproc" + "github.com/gogf/gf/v2/os/gtime" "github.com/gogf/gf/v2/text/gregex" "github.com/gogf/gf/v2/text/gstr" "github.com/gogf/gf/v2/util/gconv" @@ -79,8 +82,17 @@ func ImportSql(ctx context.Context, path string) error { if err != nil { return err } + sqlContent := string(rows) + config := g.DB("default").GetConfig() + if config.Type == consts.DBPgsql { + return importSqlPgsql(ctx, sqlContent) + } + return importSqlMysql(ctx, sqlContent) +} - sqlArr := strings.Split(string(rows), "\n") +// importSqlMysql 导出mysql文件 +func importSqlMysql(ctx context.Context, sqlContent string) error { + sqlArr := strings.Split(sqlContent, "\n") for _, sql := range sqlArr { sql = strings.TrimSpace(sql) if sql == "" || strings.HasPrefix(sql, "--") { @@ -95,6 +107,53 @@ func ImportSql(ctx context.Context, path string) error { return nil } +// importSqlPgsql 导出pgsql文件 +func importSqlPgsql(ctx context.Context, sqlContent string) error { + lines := strings.Split(sqlContent, "\n") + var currentStmt strings.Builder + inDoBlock := false + for _, line := range lines { + trimmedLine := strings.TrimSpace(line) + + if trimmedLine == "" || strings.HasPrefix(trimmedLine, "--") { + continue + } + + currentStmt.WriteString(line) + currentStmt.WriteString("\n") + + if strings.HasPrefix(trimmedLine, "DO $$") || strings.HasPrefix(trimmedLine, "DO $") { + inDoBlock = true + continue + } + + if inDoBlock && (strings.HasPrefix(trimmedLine, "END $$;") || strings.HasPrefix(trimmedLine, "END $")) { + inDoBlock = false + stmt := currentStmt.String() + exec, err := g.DB().Exec(ctx, stmt) + g.Log().Infof(ctx, "importSqlPgsql DO block executed, exec:%+v, err:%+v", exec, err) + if err != nil { + g.Log().Errorf(ctx, "importSqlPgsql error: %v, sql: %s", err, stmt) + return err + } + currentStmt.Reset() + continue + } + + if !inDoBlock && strings.HasSuffix(trimmedLine, ";") { + stmt := currentStmt.String() + exec, err := g.DB().Exec(ctx, stmt) + g.Log().Infof(ctx, "importSqlPgsql sql executed, exec:%+v, err:%+v", exec, err) + if err != nil { + g.Log().Errorf(ctx, "importSqlPgsql error: %v, sql: %s", err, stmt) + return err + } + currentStmt.Reset() + } + } + return nil +} + func checkCurdPath(temp *model.GenerateAppCrudTemplate, addonName string) (err error) { if temp == nil { return gerror.New("生成模板配置不能为空") @@ -299,22 +358,182 @@ func FormatGo(ctx context.Context, name, code string) (string, error) { } func FormatVue(code string) string { + if formatted, ok := tryPrettierFormat(code, "vue"); ok { + return formatted + } + endTag := `` vueLen := gstr.PosR(code, endTag) vueCode := code[:vueLen+len(endTag)] tsCode := code[vueLen+len(endTag):] + vueCode = gohtml.Format(vueCode) + vueCode = formatVueTemplate(vueCode) tsCode = FormatTs(tsCode) return vueCode + tsCode } func FormatTs(code string) string { + if formatted, ok := tryPrettierFormat(code, "typescript"); ok { + return formatted + } code = replaceEmptyLinesWithSpace(code) + code = formatTypeScript(code) return code + "\n" } +// tryPrettierFormat 尝试使用 Prettier 格式化代码 +func tryPrettierFormat(code string, parser string) (string, bool) { + webDir := gfile.Abs("./web") + prettierBin := gfile.Join(webDir, "node_modules", ".bin", "prettier") + + if !gfile.Exists(prettierBin) { + return "", false + } + + tmpFile := gfile.Temp(gtime.TimestampNanoStr()) + "." + getFileExt(parser) + defer gfile.Remove(tmpFile) + + if err := gfile.PutContents(tmpFile, code); err != nil { + return "", false + } + + cmd := fmt.Sprintf("cd %s && %s --write %s 2>/dev/null", webDir, prettierBin, tmpFile) + _, err := gproc.ShellExec(context.Background(), cmd) + if err != nil { + return "", false + } + + formatted := gfile.GetContents(tmpFile) + if formatted == "" { + return "", false + } + return formatted, true +} + +// getFileExt 根据 parser 类型返回文件扩展名 +func getFileExt(parser string) string { + switch parser { + case "vue": + return "vue" + case "typescript": + return "ts" + default: + return "txt" + } +} + +// formatVueTemplate 格式化 Vue 模板代码 +func formatVueTemplate(code string) string { + lines := strings.Split(code, "\n") + var result []string + lastLineWasOpenTag := false + + for _, line := range lines { + trimmed := strings.TrimSpace(line) + if lastLineWasOpenTag && trimmed == "" { + continue + } + + if strings.HasPrefix(trimmed, "<") && !strings.HasPrefix(trimmed, "") && !strings.HasSuffix(trimmed, ">") && + !strings.HasPrefix(trimmed, "