要实现一个通用的业务列表字段可以动态调整的功能,主要的目标是:让不同用户可以根据自己的需求,定制展示的字段以及字段的顺序。这可以通过以下步骤来实现:
功能要求:
- 字段选择与定制:用户可以选择在列表中显示哪些字段。
- 字段排序:用户可以拖动或调整字段的顺序。
- 保存设置:每个用户的字段选择和顺序需要保存,方便下次加载时使用。
- 灵活配置:支持不同类型的业务列表,字段可以是固定的(如客户名称、手机号等),也可以是动态的。
总体设计思路:
- 字段管理:每个业务列表(例如:客户列表)有一组预定义的字段,用户可以选择显示哪些字段,并调整这些字段的显示顺序。
- 用户个性化设置:每个用户的字段设置(显示与否、排序顺序)需要进行保存,这样在用户下次访问时,可以加载个性化的配置。
- 前端交互:用户通过前端界面选择需要显示的字段和调整字段顺序,操作通过API请求传递到后端。
- 后端存储:后端保存每个用户的字段设置,可以使用数据库保存字段配置或使用文件存储配置。
关键模块:
- 字段管理模块:负责定义所有字段。
- 用户设置模块:保存每个用户的字段显示设置。
- 接口模块:提供API给前端来获取、更新用户的字段显示设置。
1. 数据库设计
假设我们有一个"客户"列表,字段包括:客户名称、手机号、邮箱等。
- 字段定义表(用于定义字段)
fields:定义所有可能的字段。
CREATE TABLE fields (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL, -- 字段名称(如:客户名称、手机号等)
display_name VARCHAR(255) NOT NULL, -- 用户展示的字段名称
type VARCHAR(50) NOT NULL -- 字段类型,如:string、number、date等
);
- 用户字段设置表(保存用户个性化设置)
user_field_settings:保存每个用户选择的字段和排序顺序。
CREATE TABLE user_field_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL, -- 用户ID
field_id INT NOT NULL, -- 字段ID(与fields表关联)
display_order INT NOT NULL, -- 字段展示顺序
is_visible BOOLEAN NOT NULL DEFAULT TRUE, -- 是否显示该字段
FOREIGN KEY (field_id) REFERENCES fields(id),
UNIQUE(user_id, field_id) -- 保证每个用户每个字段只有一条记录
);
2. 后端设计
后端主要实现以下几个功能:
- 获取字段配置
- 获取用户个性化字段设置
- 更新用户个性化字段设置
2.1 结构体定义
我们需要一些结构体来表示字段信息和用户的设置。
package model
// Field 定义了一个字段
type Field struct {
ID int `json:"id"`
Name string `json:"name"` // 字段名称,例如 "customer_name"
DisplayName string `json:"display_name"` // 展示的字段名称,例如 "客户名称"
Type string `json:"type"` // 字段类型,例如 "string"
}
// UserFieldSetting 用户的字段设置
type UserFieldSetting struct {
UserID int `json:"user_id"`
FieldID int `json:"field_id"`
DisplayOrder int `json:"display_order"`
IsVisible bool `json:"is_visible"`
}
2.2 控制器与服务
我们需要编写API来获取和更新字段设置。
获取字段设置
// controller/field_controller.go
package controller
import (
"net/http"
"handover/service"
"github.com/tal-tech/go-zero/rest"
"github.com/tal-tech/go-zero/core/logx"
)
type FieldController struct {
FieldService *service.FieldService
}
func (fc *FieldController) GetFieldsByUser(w http.ResponseWriter, r *http.Request) {
// 获取当前用户ID
userID := r.Header.Get("User-ID") // 假设用户ID从header中获取
fields, err := fc.FieldService.GetUserFields(userID)
if err != nil {
logx.Error("Error getting user fields: ", err)
http.Error(w, "Failed to get fields", http.StatusInternalServerError)
return
}
// 返回字段数据
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(fields)
}
更新用户字段设置
// controller/field_controller.go
func (fc *FieldController) UpdateUserFields(w http.ResponseWriter, r *http.Request) {
// 解析请求体中的字段设置
var settings []model.UserFieldSetting
err := json.NewDecoder(r.Body).Decode(&settings)
if err != nil {
logx.Error("Error parsing request body: ", err)
http.Error(w, "Invalid input", http.StatusBadRequest)
return
}
// 获取当前用户ID
userID := r.Header.Get("User-ID")
// 更新字段设置
err = fc.FieldService.UpdateUserFields(userID, settings)
if err != nil {
logx.Error("Error updating user fields: ", err)
http.Error(w, "Failed to update fields", http.StatusInternalServerError)
return
}
// 返回成功响应
w.WriteHeader(http.StatusOK)
w.Write([]byte("Field settings updated successfully"))
}
2.3 服务层
// service/field_service.go
package service
import (
"handover/store"
"handover/model"
)
type FieldService struct {
Store *store.Store
}
// 获取用户字段设置
func (fs *FieldService) GetUserFields(userID string) ([]model.Field, error) {
// 查询数据库,获取用户的字段设置
fields, err := fs.Store.GetFieldsByUserID(userID)
if err != nil {
return nil, err
}
return fields, nil
}
// 更新用户字段设置
func (fs *FieldService) UpdateUserFields(userID string, settings []model.UserFieldSetting) error {
// 更新数据库,保存用户的字段设置
err := fs.Store.SaveUserFieldSettings(userID, settings)
if err != nil {
return err
}
return nil
}
2.4 数据访问层
// store/store.go
package store
import (
"handover/model"
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
type Store struct {
Conn *sql.DB
}
// 获取用户的字段配置
func (s *Store) GetFieldsByUserID(userID string) ([]model.Field, error) {
// SQL查询获取用户字段设置
rows, err := s.Conn.Query(`
SELECT f.id, f.name, f.display_name, f.type
FROM fields f
JOIN user_field_settings ufs ON f.id = ufs.field_id
WHERE ufs.user_id = ? AND ufs.is_visible = TRUE
ORDER BY ufs.display_order`, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var fields []model.Field
for rows.Next() {
var field model.Field
if err := rows.Scan(&field.ID, &field.Name, &field.DisplayName, &field.Type); err != nil {
return nil, err
}
fields = append(fields, field)
}
return fields, nil
}
// 保存用户的字段设置
func (s *Store) SaveUserFieldSettings(userID string, settings []model.UserFieldSetting) error {
tx, err := s.Conn.Begin()
if err != nil {
return err
}
// 删除当前用户的字段设置
_, err = tx.Exec("DELETE FROM user_field_settings WHERE user_id = ?", userID)
if err != nil {
tx.Rollback()
return err
}
// 插入新的字段设置
for _, setting := range settings {
_, err = tx.Exec("INSERT INTO user_field_settings (user_id, field_id, display_order, is_visible) VALUES (?, ?, ?, ?)",
userID, setting.FieldID, setting.DisplayOrder, setting.IsVisible)
if err != nil {
tx.Rollback()
return err
}
}
return tx.Commit()
}
3. 前端设计
前端需要提供以下功能:
- 字段选择:用户可以选择显示哪些字段,可以通过多选框或者拖拽排序的方式来实现。
- 保存设置:用户选择完成后,可以点击保存按钮,调用API保存设置。
- 显示设置:当用户打开列表时,调用API加载保存的字段设置,并根据字段顺序和是否可见来动态展示列表。
前端的具体实现可能需要使用一些UI组件库来支持拖拽排序和字段选择功能。常用的库有:React DnD、Ant Design的Table组件等