Skip to content

自定义表单 - Easy4Form 主代理 API

返回: 主 API 文档

自定义表单是 Easy4Form 中最强大和灵活的表单类型。它们允许您从用户那里收集各种类型的输入,包括文本、数字、开关、下拉菜单和滑块。非常适合配置屏幕、用户注册、设置面板以及任何需要结构化数据输入的场景。

目录

概述

什么是自定义表单?

自定义表单提供:

  • 多种输入类型:文本、数字、开关、下拉菜单、滑块
  • 结构化数据收集:带有标签和描述的有组织表单
  • 灵活布局:可以按任何顺序添加元素
  • 丰富响应:返回与每个表单元素对应的值列表
  • 验证支持:内置和自定义验证选项

何时使用自定义表单

适用于:

  • 用户注册和个人资料
  • 配置和设置面板
  • 数据输入表单
  • 调查和问卷
  • 复杂输入收集
  • 多步骤向导

不适用于:

基本用法

简单示例

java
import cn.enderrealm.easy4form.api.Easy4FormAPI;
import cn.enderrealm.easy4form.api.CustomFormBuilder;

public void showPlayerRegistration(Player player) {
    CustomFormBuilder form = new CustomFormBuilder()
        .title("玩家注册")
        .addInput("username", "用户名:", "输入您的显示名称")
        .addInput("email", "邮箱:", "your@email.com")
        .addToggle("newsletter", "订阅新闻通讯", false)
        .addDropdown("region", "选择地区:", 
                    Arrays.asList("北美", "欧洲", "亚洲", "其他"), 0);
    
    Easy4FormAPI.sendCustomForm(player, form, response -> {
        if (response != null) {
            String username = response.getInput("username");
            String email = response.getInput("email");
            boolean newsletter = response.getToggle("newsletter");
            String region = response.getDropdown("region");
            
            // 处理注册
            registerPlayer(player, username, email, newsletter, region);
            player.sendMessage("注册成功完成!");
        } else {
            player.sendMessage("注册已取消。");
        }
    });
}

响应处理

自定义表单响应提供对表单数据的类型化访问:

java
response -> {
    if (response == null) {
        // 表单被关闭而没有提交
        player.sendMessage("表单已取消。");
        return;
    }
    
    // 通过元素 ID 访问表单数据
    String textValue = response.getInput("elementId");
    boolean toggleValue = response.getToggle("toggleId");
    String dropdownValue = response.getDropdown("dropdownId");
    double sliderValue = response.getSlider("sliderId");
    
    // 处理收集的数据
    processFormData(player, textValue, toggleValue, dropdownValue, sliderValue);
}

表单元素

1. 文本输入

从用户收集文本输入:

java
// 基本文本输入
form.addInput("name", "玩家姓名:", "输入您的姓名");

// 带验证
form.addInput("username", "用户名:", "3-16个字符", input -> {
    if (input.length() < 3 || input.length() > 16) {
        return "用户名必须是3-16个字符长";
    }
    if (!input.matches("[a-zA-Z0-9_]+")) {
        return "用户名只能包含字母、数字和下划线";
    }
    return null; // 有效
});

// 多行文本输入
form.addTextArea("description", "描述:", "输入详细描述...");

2. 开关(布尔值)

提供开/关开关:

java
// 基本开关
form.addToggle("pvp", "启用 PvP", false); // 默认:false

// 带描述
form.addToggle("notifications", "接收通知", true, 
              "获取重要服务器事件的通知");

3. 下拉菜单(选择)

提供多选选择:

java
// 基本下拉菜单
List<String> options = Arrays.asList("简单", "普通", "困难", "专家");
form.addDropdown("difficulty", "难度等级:", options, 1); // 默认:"普通"

// 动态下拉菜单
List<String> availableWorlds = getAvailableWorlds();
form.addDropdown("world", "选择世界:", availableWorlds, 0);

// 带自定义显示值
Map<String, String> regionMap = new HashMap<>();
regionMap.put("na", "北美");
regionMap.put("eu", "欧洲");
regionMap.put("asia", "亚太地区");
form.addDropdown("region", "地区:", regionMap, "na");

4. 滑块(数值范围)

允许在范围内进行数值输入:

java
// 基本滑块
form.addSlider("volume", "音量等级:", 0.0, 100.0, 1.0, 50.0); // 最小值、最大值、步长、默认值

// 带自定义格式
form.addSlider("speed", "移动速度:", 0.1, 2.0, 0.1, 1.0, 
              value -> String.format("%.1fx", value));

// 整数滑块
form.addIntSlider("players", "最大玩家数:", 1, 20, 1, 10);

5. 数字输入

直接数值输入:

java
// 基本数字输入
form.addNumber("amount", "数量:", "输入数量", 0.0);

// 带范围验证
form.addNumber("price", "价格:", "$0.00", 0.0, number -> {
    if (number < 0) {
        return "价格不能为负数";
    }
    if (number > 10000) {
        return "价格不能超过 $10,000";
    }
    return null; // 有效
});

// 整数输入
form.addInteger("quantity", "数量:", "输入数量", 1);

高级功能

条件元素

根据其他输入显示/隐藏元素:

java
public void showConditionalForm(Player player) {
    CustomFormBuilder form = new CustomFormBuilder()
        .title("服务器设置")
        .addToggle("enablePvP", "启用 PvP", false)
        .addConditionalGroup("pvpSettings", "enablePvP", true, group -> {
            group.addSlider("pvpDamage", "PvP 伤害倍数:", 0.1, 3.0, 0.1, 1.0);
            group.addToggle("friendlyFire", "友军伤害", false);
            group.addDropdown("pvpZones", "PvP 区域:", 
                            Arrays.asList("任何地方", "指定区域", "仅竞技场"), 1);
        })
        .addToggle("enableEconomy", "启用经济", true)
        .addConditionalGroup("economySettings", "enableEconomy", true, group -> {
            group.addNumber("startingMoney", "起始金钱:", "$100.00", 100.0);
            group.addSlider("taxRate", "税率 (%):", 0.0, 25.0, 0.5, 5.0);
        });
    
    Easy4FormAPI.sendCustomForm(player, form, response -> {
        if (response != null) {
            processServerSettings(player, response);
        }
    });
}

表单验证

在提交前验证整个表单:

java
CustomFormBuilder form = new CustomFormBuilder()
    .title("用户账户")
    .addInput("username", "用户名:", "输入用户名")
    .addInput("password", "密码:", "输入密码")
    .addInput("confirmPassword", "确认密码:", "确认密码")
    .addFormValidator(response -> {
        String password = response.getInput("password");
        String confirmPassword = response.getInput("confirmPassword");
        
        if (!password.equals(confirmPassword)) {
            return "密码不匹配";
        }
        
        if (password.length() < 8) {
            return "密码必须至少8个字符长";
        }
        
        return null; // 有效
    });

多页表单

创建向导式多步骤表单:

java
public void showRegistrationWizard(Player player) {
    showRegistrationStep1(player, new HashMap<>());
}

private void showRegistrationStep1(Player player, Map<String, Object> data) {
    CustomFormBuilder form = new CustomFormBuilder()
        .title("注册 - 第 1 步,共 3 步")
        .addLabel("个人信息")
        .addInput("firstName", "名:", "输入名")
        .addInput("lastName", "姓:", "输入姓")
        .addInput("email", "邮箱:", "your@email.com");
    
    Easy4FormAPI.sendCustomForm(player, form, response -> {
        if (response != null) {
            data.put("firstName", response.getInput("firstName"));
            data.put("lastName", response.getInput("lastName"));
            data.put("email", response.getInput("email"));
            
            showRegistrationStep2(player, data);
        } else {
            player.sendMessage("注册已取消。");
        }
    });
}

private void showRegistrationStep2(Player player, Map<String, Object> data) {
    CustomFormBuilder form = new CustomFormBuilder()
        .title("注册 - 第 2 步,共 3 步")
        .addLabel("偏好设置")
        .addDropdown("region", "地区:", 
                    Arrays.asList("北美", "欧洲", "亚洲", "其他"), 0)
        .addToggle("newsletter", "订阅新闻通讯", false)
        .addSlider("experience", "游戏经验(年):", 0, 20, 1, 5);
    
    Easy4FormAPI.sendCustomForm(player, form, response -> {
        if (response != null) {
            data.put("region", response.getDropdown("region"));
            data.put("newsletter", response.getToggle("newsletter"));
            data.put("experience", response.getSlider("experience"));
            
            showRegistrationStep3(player, data);
        } else {
            // 返回第 1 步
            showRegistrationStep1(player, data);
        }
    });
}

private void showRegistrationStep3(Player player, Map<String, Object> data) {
    // 最终确认步骤
    String summary = String.format(
        "请确认您的注册信息:\n\n" +
        "姓名:%s %s\n" +
        "邮箱:%s\n" +
        "地区:%s\n" +
        "新闻通讯:%s\n" +
        "经验:%.0f 年",
        data.get("firstName"), data.get("lastName"),
        data.get("email"), data.get("region"),
        data.get("newsletter"), data.get("experience")
    );
    
    CustomFormBuilder form = new CustomFormBuilder()
        .title("注册 - 第 3 步,共 3 步")
        .addLabel(summary)
        .addToggle("confirm", "我确认此信息正确", false);
    
    Easy4FormAPI.sendCustomForm(player, form, response -> {
        if (response != null && response.getToggle("confirm")) {
            // 完成注册
            completeRegistration(player, data);
            player.sendMessage("注册成功完成!");
        } else if (response != null) {
            player.sendMessage("请确认您的信息以完成注册。");
            showRegistrationStep3(player, data);
        } else {
            // 返回第 2 步
            showRegistrationStep2(player, data);
        }
    });
}

构建器模式

CustomFormBuilder 为表单创建提供流畅的 API:

java
CustomFormBuilder form = new CustomFormBuilder()
    .title("表单标题")                                      // 设置表单标题
    .addLabel("节标题")                                     // 添加描述性文本
    .addInput("id", "标签:", "占位符")                      // 添加文本输入
    .addToggle("id", "标签", defaultValue)                 // 添加开关
    .addDropdown("id", "标签:", options, defaultIndex)     // 添加下拉菜单
    .addSlider("id", "标签:", min, max, step, defaultValue) // 添加滑块
    .addNumber("id", "标签:", "占位符", defaultValue)       // 添加数字输入
    .addSeparator()                                        // 添加视觉分隔符
    .setResponseHandler(response -> { ... })               // 设置响应处理器
    .setErrorHandler(error -> { ... });                   // 设置错误处理器

// 发送表单
Easy4FormAPI.sendCustomForm(player, form, responseHandler);

最佳实践

1. 使用标签和分隔符组织

java
// ✅ 好 - 组织良好
CustomFormBuilder form = new CustomFormBuilder()
    .title("玩家设置")
    .addLabel("🎮 游戏偏好")
    .addDropdown("difficulty", "难度:", difficulties, 1)
    .addToggle("autoSave", "自动保存", true)
    .addSeparator()
    .addLabel("🔊 音频设置")
    .addSlider("volume", "主音量:", 0, 100, 5, 75)
    .addToggle("soundEffects", "音效", true)
    .addSeparator()
    .addLabel("📱 通知")
    .addToggle("chatNotifications", "聊天通知", true);

// ❌ 避免 - 没有组织
CustomFormBuilder form = new CustomFormBuilder()
    .title("设置")
    .addDropdown("difficulty", "难度:", difficulties, 1)
    .addSlider("volume", "音量:", 0, 100, 5, 75)
    .addToggle("autoSave", "自动保存", true)
    .addToggle("soundEffects", "音效", true);

2. 使用描述性标签和占位符

java
// ✅ 好 - 清晰且描述性
form.addInput("serverName", "服务器名称:", "输入唯一的服务器名称(3-32个字符)");
form.addNumber("maxPlayers", "最大玩家数:", "1-100个玩家", 20.0);
form.addSlider("difficulty", "难度倍数:", 0.5, 3.0, 0.1, 1.0, 
              value -> String.format("%.1fx (%.0f%% 伤害)", value, value * 100));

// ❌ 避免 - 模糊标签
form.addInput("name", "名称:", "名称");
form.addNumber("number", "数字:", "数字", 0.0);
form.addSlider("slider", "值:", 0, 100, 1, 50);

3. 提供合理的默认值

java
// ✅ 好 - 深思熟虑的默认值
form.addToggle("enablePvP", "启用 PvP", false);           // 安全默认值
form.addSlider("spawnRate", "怪物生成率:", 0.1, 3.0, 0.1, 1.0); // 正常速率
form.addDropdown("gamemode", "默认游戏模式:", 
                Arrays.asList("生存", "创造", "冒险"), 0); // 生存

// ❌ 避免 - 不良默认值
form.addToggle("deleteAllData", "删除所有数据", true);    // 危险默认值
form.addSlider("difficulty", "难度:", 0, 10, 1, 10);   // 最大难度

4. 适当验证输入

java
// ✅ 好 - 全面验证
form.addInput("username", "用户名:", "3-16个字符,仅字母/数字", input -> {
    if (input.trim().isEmpty()) {
        return "用户名不能为空";
    }
    if (input.length() < 3 || input.length() > 16) {
        return "用户名必须是3-16个字符长";
    }
    if (!input.matches("[a-zA-Z0-9_]+")) {
        return "用户名只能包含字母、数字和下划线";
    }
    if (isUsernameTaken(input)) {
        return "用户名已被占用";
    }
    return null;
});

// ❌ 避免 - 没有验证
form.addInput("username", "用户名:", "输入用户名"); // 没有验证

5. 优雅地处理错误

java
Easy4FormAPI.sendCustomForm(player, form, response -> {
    if (response == null) {
        player.sendMessage("表单已取消。");
        return;
    }
    
    try {
        // 处理表单数据
        processFormData(player, response);
        player.sendMessage("设置保存成功!");
    } catch (ValidationException e) {
        player.sendMessage("输入无效:" + e.getMessage());
        // 可选择重新显示带错误消息的表单
        showFormWithError(player, e.getMessage());
    } catch (Exception e) {
        player.sendMessage("保存设置时发生错误。请重试。");
        getLogger().severe("表单处理错误:" + e.getMessage());
    }
});

示例

示例 1:服务器配置面板

java
public void showServerConfig(Player admin) {
    if (!admin.hasPermission("server.admin")) {
        admin.sendMessage("您没有访问服务器配置的权限。");
        return;
    }
    
    // 获取当前服务器设置
    ServerConfig config = getServerConfig();
    
    CustomFormBuilder form = new CustomFormBuilder()
        .title("🔧 服务器配置")
        
        // 基本设置
        .addLabel("📋 基本设置")
        .addInput("serverName", "服务器名称:", "输入服务器名称", config.getServerName())
        .addInput("motd", "每日消息:", "欢迎消息", config.getMotd())
        .addIntSlider("maxPlayers", "最大玩家数:", 1, 100, 1, config.getMaxPlayers())
        .addSeparator()
        
        // 游戏设置
        .addLabel("🎮 游戏设置")
        .addDropdown("difficulty", "难度:", 
                    Arrays.asList("和平", "简单", "普通", "困难"), 
                    config.getDifficultyIndex())
        .addToggle("pvp", "启用 PvP", config.isPvpEnabled())
        .addToggle("mobSpawning", "怪物生成", config.isMobSpawningEnabled())
        .addSlider("mobSpawnRate", "怪物生成率:", 0.1, 3.0, 0.1, config.getMobSpawnRate(),
                  value -> String.format("%.1fx", value))
        .addSeparator()
        
        // 世界设置
        .addLabel("世界设置")
        .addToggle("generateStructures", "生成结构", config.isGenerateStructures())
        .addToggle("allowNether", "允许下界", config.isAllowNether())
        .addToggle("allowEnd", "允许末地", config.isAllowEnd())
        .addIntSlider("viewDistance", "视距:", 3, 32, 1, config.getViewDistance())
        .addSeparator()
        
        // 经济设置(如果启用了经济插件)
        .addConditionalGroup("economyGroup", () -> isEconomyEnabled(), group -> {
            group.addLabel("经济设置");
            group.addNumber("startingMoney", "起始金钱:", "$0.00", config.getStartingMoney());
            group.addSlider("shopTaxRate", "商店税率 (%):", 0.0, 25.0, 0.5, config.getShopTaxRate());
            group.addToggle("allowPlayerShops", "允许玩家商店", config.isAllowPlayerShops());
        })
        
        // 验证
        .addFormValidator(response -> {
            String serverName = response.getInput("serverName");
            if (serverName.trim().isEmpty()) {
                return "服务器名称不能为空";
            }
            if (serverName.length() > 50) {
                return "服务器名称不能超过50个字符";
            }
            return null;
        });
    
    Easy4FormAPI.sendCustomForm(admin, form, response -> {
        if (response != null) {
            try {
                // 应用配置更改
                applyServerConfig(response);
                admin.sendMessage("服务器配置更新成功!");
                
                // 记录更改
                getLogger().info(String.format("服务器配置由 %s 更新", admin.getName()));
                
                // 通知其他管理员
                notifyAdmins(String.format("%s 更新了服务器配置", admin.getName()));
                
            } catch (Exception e) {
                admin.sendMessage("更新服务器配置时出错:" + e.getMessage());
                getLogger().severe("服务器配置更新失败:" + e.getMessage());
            }
        } else {
            admin.sendMessage("服务器配置更新已取消。");
        }
    });
}

private void applyServerConfig(CustomFormResponse response) {
    ServerConfig config = getServerConfig();
    
    config.setServerName(response.getInput("serverName"));
    config.setMotd(response.getInput("motd"));
    config.setMaxPlayers((int) response.getSlider("maxPlayers"));
    config.setDifficulty(response.getDropdownIndex("difficulty"));
    config.setPvpEnabled(response.getToggle("pvp"));
    config.setMobSpawningEnabled(response.getToggle("mobSpawning"));
    config.setMobSpawnRate(response.getSlider("mobSpawnRate"));
    config.setGenerateStructures(response.getToggle("generateStructures"));
    config.setAllowNether(response.getToggle("allowNether"));
    config.setAllowEnd(response.getToggle("allowEnd"));
    config.setViewDistance((int) response.getSlider("viewDistance"));
    
    if (isEconomyEnabled()) {
        config.setStartingMoney(response.getNumber("startingMoney"));
        config.setShopTaxRate(response.getSlider("shopTaxRate"));
        config.setAllowPlayerShops(response.getToggle("allowPlayerShops"));
    }
    
    config.save();
    config.apply();
}

示例 2:玩家个人资料创建

java
public void showProfileCreation(Player player) {
    CustomFormBuilder form = new CustomFormBuilder()
        .title("创建您的个人资料")
        
        // 个人信息
        .addLabel("个人信息")
        .addInput("displayName", "显示名称:", "其他人将如何看到您", player.getName())
        .addTextArea("bio", "个人简介:", "告诉其他人关于您的信息(可选)")
        .addDropdown("timezone", "时区:", getTimezones(), getDefaultTimezoneIndex())
        .addSeparator()
        
        // 游戏偏好
        .addLabel("游戏偏好")
        .addDropdown("favoriteGamemode", "最喜欢的游戏模式:", 
                    Arrays.asList("生存", "创造", "冒险", "观察者"), 0)
        .addSlider("skillLevel", "技能等级:", 1, 10, 1, 5,
                  value -> {
                      String[] levels = {"初学者", "新手", "中级", 
                                       "高级", "专家", "大师"};
                      int index = Math.min((int)((value - 1) / 2), levels.length - 1);
                      return String.format("%.0f - %s", value, levels[index]);
                  })
        .addMultiSelect("interests", "兴趣:", 
                       Arrays.asList("建筑", "红石", "PvP", "探索", 
                                   "农业", "交易", "小游戏"))
        .addSeparator()
        
        // 隐私设置
        .addLabel("隐私设置")
        .addToggle("showOnlineStatus", "向其他人显示在线状态", true)
        .addToggle("allowFriendRequests", "允许好友请求", true)
        .addToggle("showStats", "公开显示统计信息", false)
        .addDropdown("profileVisibility", "个人资料可见性:", 
                    Arrays.asList("公开", "仅好友", "私人"), 0)
        .addSeparator()
        
        // 通知偏好
        .addLabel("通知")
        .addToggle("emailNotifications", "邮件通知", false)
        .addToggle("friendOnlineNotifications", "好友上线通知", true)
        .addToggle("eventNotifications", "服务器事件通知", true)
        
        // 验证
        .addFormValidator(response -> {
            String displayName = response.getInput("displayName").trim();
            if (displayName.isEmpty()) {
                return "显示名称不能为空";
            }
            if (displayName.length() < 3 || displayName.length() > 20) {
                return "显示名称必须是3-20个字符长";
            }
            if (!displayName.matches("[a-zA-Z0-9_\\s]+")) {
                return "显示名称只能包含字母、数字、空格和下划线";
            }
            if (isDisplayNameTaken(displayName, player)) {
                return "显示名称已被占用";
            }
            return null;
        });
    
    Easy4FormAPI.sendCustomForm(player, form, response -> {
        if (response != null) {
            try {
                // 创建玩家个人资料
                PlayerProfile profile = createPlayerProfile(player, response);
                savePlayerProfile(profile);
                
                player.sendMessage("个人资料创建成功!");
                player.sendMessage("欢迎来到服务器," + response.getInput("displayName") + "!");
                
                // 显示欢迎导览或下一步
                showWelcomeTour(player);
                
            } catch (Exception e) {
                player.sendMessage("创建个人资料时出错:" + e.getMessage());
                getLogger().severe("为 " + player.getName() + " 创建个人资料失败:" + e.getMessage());
            }
        } else {
            player.sendMessage("个人资料创建已取消。");
            // 也许显示简化的快速设置
            showQuickSetup(player);
        }
    });
}

private PlayerProfile createPlayerProfile(Player player, CustomFormResponse response) {
    PlayerProfile profile = new PlayerProfile(player.getUniqueId());
    
    profile.setDisplayName(response.getInput("displayName"));
    profile.setBio(response.getInput("bio"));
    profile.setTimezone(response.getDropdown("timezone"));
    profile.setFavoriteGamemode(response.getDropdown("favoriteGamemode"));
    profile.setSkillLevel((int) response.getSlider("skillLevel"));
    profile.setInterests(response.getMultiSelect("interests"));
    profile.setShowOnlineStatus(response.getToggle("showOnlineStatus"));
    profile.setAllowFriendRequests(response.getToggle("allowFriendRequests"));
    profile.setShowStats(response.getToggle("showStats"));
    profile.setProfileVisibility(response.getDropdown("profileVisibility"));
    profile.setEmailNotifications(response.getToggle("emailNotifications"));
    profile.setFriendOnlineNotifications(response.getToggle("friendOnlineNotifications"));
    profile.setEventNotifications(response.getToggle("eventNotifications"));
    
    return profile;
}

示例 3:商店创建表单

java
public void showShopCreation(Player player) {
    if (!player.hasPermission("shops.create")) {
        player.sendMessage("您没有创建商店的权限。");
        return;
    }
    
    // 检查玩家是否达到商店限制
    int currentShops = getPlayerShopCount(player);
    int maxShops = getMaxShopsForPlayer(player);
    
    if (currentShops >= maxShops) {
        player.sendMessage(String.format("您已达到商店限制 (%d/%d)", currentShops, maxShops));
        return;
    }
    
    CustomFormBuilder form = new CustomFormBuilder()
        .title("创建新商店")
        
        // 基本商店信息
        .addLabel("商店信息")
        .addInput("shopName", "商店名称:", "输入唯一的商店名称", "")
        .addTextArea("description", "描述:", "描述您的商店销售什么(可选)")
        .addDropdown("category", "商店类别:", 
                    Arrays.asList("综合", "食物", "工具", "武器", "盔甲", 
                                "建筑", "红石", "装饰", "其他"), 0)
        .addSeparator()
        
        // 位置设置
        .addLabel("位置设置")
        .addToggle("useCurrentLocation", "使用当前位置", true)
        .addConditionalGroup("customLocation", "useCurrentLocation", false, group -> {
            group.addNumber("locationX", "X 坐标:", "输入 X 坐标", 0.0);
            group.addNumber("locationY", "Y 坐标:", "输入 Y 坐标", 64.0);
            group.addNumber("locationZ", "Z 坐标:", "输入 Z 坐标", 0.0);
            group.addDropdown("world", "世界:", getWorldNames(), 0);
        })
        .addSeparator()
        
        // 商店设置
        .addLabel("商店设置")
        .addToggle("isPublic", "公开商店", true, "允许其他玩家找到并访问您的商店")
        .addToggle("allowVisitors", "允许访客", true, "让玩家无需购买即可浏览")
        .addSlider("shopSize", "商店大小:", 1, 5, 1, 3,
                  value -> {
                      String[] sizes = {"微型 (9 物品)", "小型 (18 物品)", "中型 (27 物品)", 
                                      "大型 (36 物品)", "巨型 (45 物品)"};
                      return sizes[(int)value - 1];
                  })
        .addSeparator()
        
        // 经济设置
        .addLabel("经济设置")
        .addNumber("setupCost", "设置费用:", "创建此商店的费用", getShopSetupCost(player))
        .addSlider("taxRate", "税率 (%):", 0.0, 10.0, 0.5, 2.0,
                  value -> String.format("%.1f%% (您保留 %.1f%%)", value, 100 - value))
        .addToggle("autoRestock", "从库存自动补货", false)
        .addConditionalGroup("restockSettings", "autoRestock", true, group -> {
            group.addIntSlider("restockInterval", "补货间隔(分钟):", 5, 60, 5, 15);
            group.addToggle("notifyLowStock", "库存不足时通知", true);
        })
        
        // 验证
        .addFormValidator(response -> {
            String shopName = response.getInput("shopName").trim();
            if (shopName.isEmpty()) {
                return "商店名称不能为空";
            }
            if (shopName.length() < 3 || shopName.length() > 30) {
                return "商店名称必须是3-30个字符长";
            }
            if (isShopNameTaken(shopName)) {
                return "商店名称已被占用";
            }
            
            double setupCost = response.getNumber("setupCost");
            if (getPlayerBalance(player) < setupCost) {
                return String.format("资金不足。您需要 $%.2f 但只有 $%.2f", 
                                   setupCost, getPlayerBalance(player));
            }
            
            return null;
        });
    
    Easy4FormAPI.sendCustomForm(player, form, response -> {
        if (response != null) {
            try {
                // 创建商店
                Shop shop = createShop(player, response);
                
                // 收取设置费用
                double setupCost = response.getNumber("setupCost");
                removePlayerBalance(player, setupCost);
                
                player.sendMessage(String.format("商店 '%s' 创建成功!", shop.getName()));
                player.sendMessage(String.format("设置费用:$%.2f", setupCost));
                player.sendMessage("使用 /shop manage 向您的商店添加物品。");
                
                // 如果请求,传送到商店位置
                if (response.getToggle("useCurrentLocation")) {
                    player.sendMessage("您的商店已在您当前位置创建。");
                } else {
                    player.sendMessage("输入 /shop visit " + shop.getName() + " 访问您的新商店。");
                }
                
                // 记录商店创建
                getLogger().info(String.format("玩家 %s 在 %s 创建了商店 '%s'", 
                                              player.getName(), shop.getLocation(), shop.getName()));
                
            } catch (Exception e) {
                player.sendMessage("创建商店时出错:" + e.getMessage());
                getLogger().severe("商店创建失败:" + e.getMessage());
            }
        } else {
            player.sendMessage("商店创建已取消。");
        }
    });
}

private Shop createShop(Player player, CustomFormResponse response) {
    Shop shop = new Shop();
    
    shop.setOwner(player.getUniqueId());
    shop.setName(response.getInput("shopName"));
    shop.setDescription(response.getInput("description"));
    shop.setCategory(response.getDropdown("category"));
    
    // 设置位置
    if (response.getToggle("useCurrentLocation")) {
        shop.setLocation(player.getLocation());
    } else {
        double x = response.getNumber("locationX");
        double y = response.getNumber("locationY");
        double z = response.getNumber("locationZ");
        String worldName = response.getDropdown("world");
        World world = Bukkit.getWorld(worldName);
        shop.setLocation(new Location(world, x, y, z));
    }
    
    shop.setPublic(response.getToggle("isPublic"));
    shop.setAllowVisitors(response.getToggle("allowVisitors"));
    shop.setSize((int) response.getSlider("shopSize"));
    shop.setTaxRate(response.getSlider("taxRate"));
    shop.setAutoRestock(response.getToggle("autoRestock"));
    
    if (response.getToggle("autoRestock")) {
        shop.setRestockInterval((int) response.getSlider("restockInterval"));
        shop.setNotifyLowStock(response.getToggle("notifyLowStock"));
    }
    
    shop.save();
    return shop;
}

故障排除

常见问题

表单不显示:

  • 确保玩家是基岩版玩家
  • 检查 Floodgate 是否正确安装
  • 验证 Easy4Form 是否已启用
  • 检查表单大小限制(元素过多)

响应值为 null 或不正确:

  • 验证表单创建和响应处理之间的元素 ID 匹配
  • 检查表单是否已提交或关闭
  • 使用调试来记录响应值
  • 确保数值的正确类型转换

验证不工作:

  • 检查验证逻辑的边界情况
  • 确保验证函数对有效输入返回 null
  • 测试各种输入组合
  • 记录验证结果以进行调试

大型表单的性能问题:

  • 考虑将大型表单分解为多个步骤
  • 使用条件组隐藏不相关的部分
  • 限制下拉菜单选项的数量
  • 优化验证逻辑

调试示例

java
CustomFormBuilder form = new CustomFormBuilder()
    .title("调试表单")
    .addInput("testInput", "测试输入:", "输入文本")
    .addToggle("testToggle", "测试开关", false)
    .addDropdown("testDropdown", "测试下拉菜单:", 
                Arrays.asList("选项 1", "选项 2", "选项 3"), 0)
    .addSlider("testSlider", "测试滑块:", 0, 100, 10, 50);

Easy4FormAPI.sendCustomForm(player, form, response -> {
    if (response == null) {
        getLogger().info("表单被 " + player.getName() + " 关闭");
        return;
    }
    
    // 记录所有响应值以进行调试
    getLogger().info("来自 " + player.getName() + " 的表单响应:");
    getLogger().info("  输入:'" + response.getInput("testInput") + "'");
    getLogger().info("  开关:" + response.getToggle("testToggle"));
    getLogger().info("  下拉菜单:'" + response.getDropdown("testDropdown") + "' (索引:" + 
                    response.getDropdownIndex("testDropdown") + ")");
    getLogger().info("  滑块:" + response.getSlider("testSlider"));
    
    player.sendMessage("表单提交成功!请查看控制台了解详情。");
});

相关文档

相关文档:

Released under the MIT License.