Golang+VUE不定结构显示

一、需求背景

需要实现一个在Web页面对数据库进行查询的功能,但由于查询语句不确定,查询结果字段也不确定。后端为Golang开发,前端使用VUE,Golang为强类型语言,并且会对数据库查出的结果进行二次数据结构类型调整,导致最终结果展示和数据库内部实际存储结果不同。

二、问题

  • 数据库类型:MySQL
  • Go版本:1.13.8
  • Go操作数据库包:Gorm
  • 前端ui:element ui
1
2
3
4
5
6
7
8
9
10
11
12
13
// 默认根据获得到的类型,直接返回结果
var result []map[string]interface{}

for rows.Next() {
values := make([]interface{}, len(columns))
object := map[string]interface{}{}
for i, column := range columns {
object[column.Name()] = reflect.New(column.ScanType()).Interface()
values[i] = object[column.Name()]
}
err = rows.Scan(values...)
}
result = append(result, object)

返回结果会出现时间类型、甚至int、varchar类型和数据库存储信息不一致情况,例如:

  • 数据库实际存储为:

  • 但调用显示结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
"data": [
{
"col": null,
"col100": null,
"col2": null,
"col27": null,
"col3": "",
"col5": null,
"col6": "",
"emp_no": 10101,
"from_date": {
"Time": "1998-10-14T00:00:00+08:00",
"Valid": true
},
"iState": "",
"id": 1000,
"salary": 66591,
"to_date": {
"Time": "1999-10-14T00:00:00+08:00",
"Valid": true
},
"type": ""
},
{
"col": null,
"col100": null,
"col2": null,
"col27": null,
"col3": "",
"col5": null,
"col6": "",
"emp_no": 10101,
"from_date": {
"Time": "1999-10-14T00:00:00+08:00",
"Valid": true
},
"iState": "",
"id": 1001,
"salary": 66715,
"to_date": {
"Time": "2000-09-23T00:00:00+08:00",
"Valid": true
},
"type": ""
}
]

三、实现

1、后端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

func ExecuteSelectSQL(sql string) (int, []string, []map[string]interface{}, error) {
var total int
var header []string
var result []map[string]interface{}
rows, err := db.Raw(sql).Rows()
if err != nil {
return 0, nil, nil, errors.New("select error")
}

defer rows.Close()
cols, err := rows.Columns()
header = cols

for rows.Next() {
columns, err := rows.ColumnTypes()
if err != nil {
return 0, header, nil, err
}

dbtypes := make(map[string]string)

values := make([]interface{}, len(columns))
object := map[string]interface{}{}
for i, column := range columns {
v := reflect.New(column.ScanType()).Interface()
switch v.(type) {
case *[]uint8:
v = new(string)
case *sql.RawBytes:
v = new(*string)
case *mysql.NullTime:
v = new(time.Time)
default:
// fmt.Printf("%v: %T", column.Name(), v)
}
object[column.Name()] = v

values[i] = object[column.Name()]
dbtypes[column.Name()] = column.DatabaseTypeName()
}
err = rows.Scan(values...)
//fmt.Println(dbtypes)

for i,v := range cols {
vs, ok := values[i].(*time.Time)
if !ok {
continue
}
if dbtypes[v] == "DATE" {
object[v] = vs.Format("2006-01-02")
} else {
object[v] = vs.Format("2006-01-02 15:04:05")
}
}

if err != nil {
return 0, header, nil, err
}
result = append(result, object)
}

total = len(result)
return total, header, result, nil
}

后端返回结果示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"result": [
{
"col": null,
"col100": null,
"col2": null,
"col27": null,
"col3": "",
"col5": null,
"col6": "",
"emp_no": 10101,
"from_date": "1998-10-14",
"iState": "",
"id": 1000,
"salary": 66591,
"to_date": "1999-10-14",
"type": ""
},
{
"col": null,
"col100": null,
"col2": null,
"col27": null,
"col3": "",
"col5": null,
"col6": "",
"emp_no": 10101,
"from_date": "1999-10-14",
"iState": "",
"id": 1001,
"salary": 66715,
"to_date": "2000-09-23",
"type": ""
}
],
"header": [
"emp_no",
"salary",
"from_date",
"to_date",
"id",
"type",
"iState",
"col",
"col2",
"col27",
"col100",
"col3",
"col5",
"col6"
],
"total": 5

2、前端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
// 结果展示:header为字段信息,selectResult为查询结果列表
<el-form-item label="查询结果:" prop="sqlSelect" :style=this.sqlSelectResult>
<template>
<el-table :data="selectResult" style="width: 100%" >
<el-table-column v-for="(date, index) in header" :key="index" :label="date" :prop="date" :width="setColumnWidth(date)">
<template slot-scope="scope">
{{selectResult[scope.$index][date]}}
</template>
</el-table-column>
</el-table>
</template>
</el-form-item>
</template>

// methods中可以添加setColumnWidth,让显示更友好一些,字段名不会折行

setColumnWidth(str) {
let columnWidth = 0;
for (let char of str) {
if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
// 如果是英文字符,为字符分配10个单位宽度,单位宽度可根据字体大小调整
columnWidth += 10
} else if (char >= '\u4e00' && char <= '\u9fa5') {
// 如果是中文字符,为字符分配14个单位宽度,单位宽度可根据字体大小调整
columnWidth += 14
} else {
// 其他种类字符,为字符分配10个单位宽度,单位宽度可根据字体大小调整
columnWidth += 10
}
}
if (columnWidth < 120) {
// 设置最小宽度
columnWidth = 160
}
return columnWidth + 'px'
},

3、最终页面显示效果