事件系统API

强大的表单生命周期事件系统,实现动态交互和响应式表单逻辑。

事件系统(kinlink.events)是kinlink的核心组件,提供了完整的表单生命周期事件管理。通过监听各种表单事件,您可以创建高度交互和动态的表单应用,实现字段联动、实时验证、数据预处理等高级功能。

事件监听器管理

注册和管理表单事件的核心功能

事件监听器管理提供了注册、移除事件监听器的完整解决方案,支持灵活的事件处理机制。

事件注册

on(eventName, callback)

注册事件监听器,在指定事件发生时自动调用回调函数。

参数:

  • eventName (string): 事件名称,通过kinlink.FormEvents获取
  • callback (function): 事件触发时执行的回调函数

返回值: 监听器ID,用于后续移除监听器

适用场景: 表单初始化、字段联动、数据验证、用户交互响应

// 注册表单加载事件
const loadListenerId = kinlink.events.on(kinlink.FormEvents.FORM_LOADED, () => { 
console.log('表单已完全加载,可以开始初始化');
// 执行表单初始化逻辑
initializeFormDefaults();
});

// 注册字段变化事件
const changeListenerId = kinlink.events.on(kinlink.FormEvents.FIELD_CHANGE, (data) => {
console.log('字段发生变化:', data.fieldName, '新值:', data.value);
// 实现动态响应逻辑
handleFieldInteraction(data);
});

// 注册提交前事件
const beforeSubmitId = kinlink.events.on(kinlink.FormEvents.BEFORE_SUBMIT, (data) => {
return validateBeforeSubmit(data.values);
});

off(eventName, listenerId)

移除之前注册的事件监听器。

参数:

  • eventName (string): 事件名称
  • listenerId (string): 注册时返回的监听器ID

适用场景: 组件卸载、条件性监听器、防止内存泄漏

// 移除特定监听器
kinlink.events.off(kinlink.FormEvents.FORM_LOADED, loadListenerId);

// 条件性移除监听器
if (isAdvancedMode) {
kinlink.events.off(kinlink.FormEvents.FIELD_CHANGE, basicChangeListenerId);
// 注册高级监听器
const advancedListenerId = kinlink.events.on(kinlink.FormEvents.FIELD_CHANGE, advancedHandler);
}

// 组件清理时移除所有监听器
function cleanup() {
kinlink.events.off(kinlink.FormEvents.FORM_LOADED, loadListenerId);
kinlink.events.off(kinlink.FormEvents.FIELD_CHANGE, changeListenerId);
kinlink.events.off(kinlink.FormEvents.BEFORE_SUBMIT, beforeSubmitId);
}

表单生命周期事件

表单各个生命周期阶段的事件处理

表单生命周期事件覆盖了从加载到提交的完整流程,让您能够在关键时刻介入表单逻辑。

表单初始化事件

FORM_LOADED

表单完全加载并准备好交互时触发,是执行初始化逻辑的最佳时机。

事件数据: 简单对象,标识表单已加载

适用场景:

  • 设置默认值和初始状态
  • 配置字段验证规则
  • 初始化UI组件
  • 建立字段间的依赖关系
kinlink.events.on(kinlink.FormEvents.FORM_LOADED, (data) => {
const form = kinlink.formApi;

// 表单初始化配置
console.log('开始表单初始化');

// 设置默认值
form.setFieldsValue({
  createDate: new Date().toISOString().split('T')[0],
  status: 'draft',
  priority: 'medium'
});

// 配置字段可见性
form.hideField('advancedOptions');
form.visuallyHideField('internalId');

// 设置字段状态
form.disableField('calculatedField');

// 添加验证规则
form.addFieldValidator('email', (value) => {
  if (!value) return '邮箱地址必填';
  const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
  return emailRegex.test(value) ? undefined : '请输入有效的邮箱地址';
});

// 设置样式主题
form.setFieldsLabelStyles({
  email: { color: '#007bff', fontWeight: '600' },
  name: { color: '#007bff', fontWeight: '600' }
});

// 初始化完成提示
form.showInfo('表单已加载,请填写必要信息');
console.log('表单初始化完成');
});

用户交互事件

响应用户操作和字段变化的事件处理

用户交互事件让您能够实时响应用户的表单操作,创建动态和智能的表单体验。

字段变化事件

FIELD_CHANGE

当任何字段值发生变化时触发,是实现字段联动和实时处理的核心事件。

事件数据:

  • fieldName (string): 发生变化的字段名称
  • value (any): 字段的新值

适用场景:

  • 字段间联动逻辑
  • 实时数据计算
  • 条件式字段显示/隐藏
  • 动态验证和提示
kinlink.events.on(kinlink.FormEvents.FIELD_CHANGE, (data) => {
const { fieldName, value } = data;
const form = kinlink.formApi;

console.log(`字段 ${fieldName} 值变为: ${value}`);

// 产品类型联动逻辑
if (fieldName === 'productType') {
  handleProductTypeChange(value, form);
}

// 数量和价格计算
if (fieldName === 'quantity' || fieldName === 'price') {
  calculateTotal(form);
}

// 地区选择联动
if (fieldName === 'country') {
  updateStateOptions(value, form);
}

// 用户类型权限控制
if (fieldName === 'userType') {
  adjustFieldPermissions(value, form);
}

// 实时数据验证
if (fieldName === 'username') {
  debounceValidateUsername(value);
}
});

// 产品类型变化处理
function handleProductTypeChange(productType, form) {
switch (productType) {
  case 'premium':
    form.showField('premiumFeatures');
    form.showField('supportLevel');
    form.setFieldValue('supportLevel', 'priority');
    break;
  case 'basic':
    form.hideField('premiumFeatures');
    form.hideField('supportLevel');
    form.setFieldValue('supportLevel', 'standard');
    break;
  case 'enterprise':
    form.showField('premiumFeatures');
    form.showField('supportLevel');
    form.showField('customRequirements');
    break;
  default:
    form.hideField('premiumFeatures');
    form.hideField('supportLevel');
    form.hideField('customRequirements');
}
}

// 总价计算
function calculateTotal(form) {
const quantity = Number(form.getFieldValue('quantity')) || 0;
const price = Number(form.getFieldValue('price')) || 0;
const discount = Number(form.getFieldValue('discount')) || 0;

const subtotal = quantity * price;
const total = subtotal - (subtotal * discount / 100);

form.setFieldValue('subtotal', subtotal.toFixed(2));
form.setFieldValue('total', total.toFixed(2));

// 动态样式提示
if (total > 1000) {
  form.setFieldLabelStyle('total', { color: '#28a745', fontWeight: 'bold' });
  form.showInfo('您获得了大额订单优惠资格!');
}
}

// 防抖用户名验证
const debounceValidateUsername = debounce(async (username) => {
if (!username) return;

try {
  const isAvailable = await checkUsernameAvailability(username);
  const form = kinlink.formApi;
  
  if (isAvailable) {
    form.clearFieldError('username');
    form.setFieldComponentStyle('username', {
      borderColor: '#28a745',
      backgroundColor: '#f8fff9'
    });
  } else {
    form.setFieldError('username', '用户名已被使用');
    form.setFieldComponentStyle('username', {
      borderColor: '#dc3545',
      backgroundColor: '#fff5f5'
    });
  }
} catch (error) {
  console.error('用户名验证失败:', error);
}
}, 500);

表单提交事件

表单提交流程中的事件处理和数据控制

表单提交事件让您能够在提交的各个阶段介入处理逻辑,确保数据的正确性和完整性。

提交前事件

BEFORE_SUBMIT

在表单提交前触发,提供最后的数据验证和修改机会。

事件数据:

  • values (object): 将要提交的表单数据(可修改)

返回值:

  • true: 允许提交
  • false: 阻止提交

适用场景:

  • 最终数据验证
  • 数据预处理和格式化
  • 添加元数据
  • 条件性提交控制
kinlink.events.on(kinlink.FormEvents.BEFORE_SUBMIT, (data) => {
const { values } = data;
const form = kinlink.formApi;

console.log('开始提交前处理,原始数据:', values);

// 执行最终验证
const validationResult = form.validateForm();
if (!validationResult.isValid) {
  form.showError('请修正所有表单错误后再提交');
  
  // 聚焦到第一个错误字段
  const firstErrorField = Object.keys(validationResult.errors)[0];
  const fieldElement = form.getFieldElement(firstErrorField);
  fieldElement.focus();
  
  return false; // 阻止提交
}

// 业务逻辑验证
if (values.userType === 'enterprise' && !values.companyName) {
  form.setFieldError('companyName', '企业用户必须填写公司名称');
  form.showError('企业用户信息不完整');
  return false;
}

// 数据预处理和格式化
processSubmissionData(values);

// 添加提交元数据
addSubmissionMetadata(values);

// 显示提交中状态
form.showInfo('正在提交表单,请稍候...');

console.log('提交前处理完成,最终数据:', values);
return true; // 允许提交
});

// 数据预处理
function processSubmissionData(values) {
// 格式化日期字段
if (values.birthDate) {
  values.birthDate = new Date(values.birthDate).toISOString();
}

// 清理和格式化电话号码
if (values.phone) {
  values.phone = values.phone.replace(/[^d]/g, '');
}

// 格式化货币金额
if (values.amount) {
  values.amount = parseFloat(values.amount).toFixed(2);
}

// 处理多选字段
if (Array.isArray(values.interests)) {
  values.interests = values.interests.join(',');
}

// 移除空字段
Object.keys(values).forEach(key => {
  if (values[key] === '' || values[key] === null || values[key] === undefined) {
    delete values[key];
  }
});
}

// 添加提交元数据
function addSubmissionMetadata(values) {
values.submissionMetadata = {
  timestamp: new Date().toISOString(),
  userAgent: navigator.userAgent,
  referrer: document.referrer,
  platform: navigator.platform,
  language: navigator.language,
  screenResolution: `${screen.width}x${screen.height}`,
  formVersion: '2.1.0',
  sessionId: generateSessionId()
};
}

提交后事件

AFTER_SUBMIT

表单提交完成后触发,用于处理提交结果和后续操作。

事件数据:

  • result (any): 服务器返回的响应数据
  • success (boolean): 提交是否成功

适用场景:

  • 显示提交结果消息
  • 页面跳转或重定向
  • 数据清理和重置
  • 错误处理和重试机制
kinlink.events.on(kinlink.FormEvents.AFTER_SUBMIT, (data) => {
const { result, success } = data;
const form = kinlink.formApi;

console.log('提交完成,结果:', { result, success });

if (success) {
  handleSubmitSuccess(result, form);
} else {
  handleSubmitFailure(result, form);
}
});

// 成功提交处理
function handleSubmitSuccess(result, form) {
// 显示成功消息
form.showSuccess('表单提交成功!数据已保存。');

// 根据业务逻辑执行后续操作
if (result.recordId) {
  console.log('新记录ID:', result.recordId);
  
  // 显示记录详情
  form.showInfo(`记录已创建,ID: ${result.recordId}`);
  
  // 可选:设置只读模式
  setFormReadonlyMode(form);
  
  // 可选:延迟跳转
  setTimeout(() => {
    if (result.redirectUrl) {
      window.location.href = result.redirectUrl;
    } else {
      window.location.href = `/records/${result.recordId}`;
    }
  }, 2000);
}

// 发送成功事件到分析系统
trackEvent('form_submit_success', {
  formType: 'contact',
  recordId: result.recordId
});
}

// 失败提交处理
function handleSubmitFailure(result, form) {
console.error('提交失败:', result);

// 解析错误信息
if (result.validationErrors) {
  // 显示服务器验证错误
  form.setFieldsErrors(result.validationErrors);
  form.showError('请修正标注的错误后重新提交');
  
  // 聚焦到第一个错误字段
  const firstErrorField = Object.keys(result.validationErrors)[0];
  if (firstErrorField) {
    const fieldElement = form.getFieldElement(firstErrorField);
    fieldElement.focus();
  }
} else if (result.message) {
  // 显示业务错误消息
  form.showError(`提交失败: ${result.message}`);
} else {
  // 显示通用错误消息
  form.showError('提交失败,请检查网络连接后重试');
}

// 提供重试选项
setTimeout(() => {
  form.showWarning('如果问题持续存在,请联系技术支持');
}, 3000);

// 记录错误日志
trackEvent('form_submit_error', {
  error: result.message || 'Unknown error',
  formData: form.getAllValues()
});
}

// 设置表单为只读模式
function setFormReadonlyMode(form) {
const allValues = form.getAllValues();
Object.keys(allValues).forEach(fieldCode => {
  form.disableField(fieldCode);
});

// 添加只读提示样式
form.setFieldsComponentStyles(
  Object.keys(allValues).reduce((styles, fieldCode) => {
    styles[fieldCode] = {
      backgroundColor: '#f8f9fa',
      borderColor: '#e9ecef',
      color: '#6c757d'
    };
    return styles;
  }, {})
);
}

事件系统最佳实践

完整的事件驱动表单示例

// 高级表单事件管理系统
class AdvancedFormEventManager {
constructor() {
  this.listeners = new Map();
  this.formState = {
    isInitialized: false,
    isSubmitting: false,
    hasUnsavedChanges: false
  };
  
  this.setupEventListeners();
  this.setupBeforeUnloadProtection();
}

// 设置所有事件监听器
setupEventListeners() {
  // 表单初始化
  this.addListener('FORM_LOADED', kinlink.FormEvents.FORM_LOADED, () => {
    this.handleFormLoaded();
  });
  
  // 字段变化监听
  this.addListener('FIELD_CHANGE', kinlink.FormEvents.FIELD_CHANGE, (data) => {
    this.handleFieldChange(data);
  });
  
  // 提交前处理
  this.addListener('BEFORE_SUBMIT', kinlink.FormEvents.BEFORE_SUBMIT, (data) => {
    return this.handleBeforeSubmit(data);
  });
  
  // 提交后处理
  this.addListener('AFTER_SUBMIT', kinlink.FormEvents.AFTER_SUBMIT, (data) => {
    this.handleAfterSubmit(data);
  });
}

// 统一监听器注册
addListener(key, eventName, callback) {
  const listenerId = kinlink.events.on(eventName, callback);
  this.listeners.set(key, { eventName, listenerId });
}

// 表单加载处理
handleFormLoaded() {
  console.log('🚀 表单加载完成,开始初始化');
  const form = kinlink.formApi;
  
  // 初始化表单状态
  this.initializeFormState(form);
  
  // 设置验证规则
  this.setupValidationRules(form);
  
  // 配置UI样式
  this.setupUITheme(form);
  
  // 标记初始化完成
  this.formState.isInitialized = true;
  form.showSuccess('表单初始化完成');
  
  console.log('✅ 表单初始化成功');
}

// 字段变化处理
handleFieldChange(data) {
  const { fieldName, value } = data;
  
  // 标记有未保存的更改
  this.formState.hasUnsavedChanges = true;
  
  // 字段特定处理逻辑
  this.processFieldLogic(fieldName, value);
  
  // 实时保存草稿
  this.saveDraft();
  
  console.log(`📝 字段 ${fieldName} 更新: ${value}`);
}

// 提交前处理
handleBeforeSubmit(data) {
  console.log('📤 准备提交表单');
  
  if (this.formState.isSubmitting) {
    kinlink.formApi.showWarning('表单正在提交中,请勿重复提交');
    return false;
  }
  
  // 执行提交前验证和处理
  const isValid = this.validateSubmission(data.values);
  if (!isValid) {
    return false;
  }
  
  // 预处理提交数据
  this.preprocessSubmissionData(data.values);
  
  // 设置提交状态
  this.formState.isSubmitting = true;
  this.formState.hasUnsavedChanges = false;
  
  return true;
}

// 提交后处理
handleAfterSubmit(data) {
  console.log('📨 表单提交完成');
  
  // 重置提交状态
  this.formState.isSubmitting = false;
  
  // 处理提交结果
  if (data.success) {
    this.handleSubmissionSuccess(data.result);
  } else {
    this.handleSubmissionError(data.result);
    this.formState.hasUnsavedChanges = true; // 恢复未保存状态
  }
}

// 字段逻辑处理
processFieldLogic(fieldName, value) {
  const form = kinlink.formApi;
  const processors = {
    userType: (val) => this.handleUserTypeChange(val, form),
    country: (val) => this.handleCountryChange(val, form),
    productType: (val) => this.handleProductTypeChange(val, form),
    quantity: () => this.calculateTotals(form),
    price: () => this.calculateTotals(form)
  };
  
  const processor = processors[fieldName];
  if (processor) {
    processor(value);
  }
}

// 自动保存草稿
saveDraft() {
  if (!this.formState.isInitialized) return;
  
  clearTimeout(this.draftTimer);
  this.draftTimer = setTimeout(() => {
    const formData = kinlink.formApi.getAllValues();
    localStorage.setItem('formDraft', JSON.stringify({
      data: formData,
      timestamp: Date.now()
    }));
    console.log('💾 草稿已自动保存');
  }, 2000);
}

// 页面离开保护
setupBeforeUnloadProtection() {
  window.addEventListener('beforeunload', (e) => {
    if (this.formState.hasUnsavedChanges) {
      e.preventDefault();
      e.returnValue = '您有未保存的更改,确定要离开吗?';
      return e.returnValue;
    }
  });
}

// 清理资源
destroy() {
  // 移除所有事件监听器
  this.listeners.forEach(({ eventName, listenerId }) => {
    kinlink.events.off(eventName, listenerId);
  });
  this.listeners.clear();
  
  // 清理定时器
  if (this.draftTimer) {
    clearTimeout(this.draftTimer);
  }
  
  console.log('🧹 事件管理器已清理');
}
}

// 使用示例
document.addEventListener('DOMContentLoaded', () => {
const eventManager = new AdvancedFormEventManager();

// 在页面卸载时清理
window.addEventListener('unload', () => {
  eventManager.destroy();
});
});

事件数据规范

事件回调参数详解

每种事件类型都有特定的数据结构,了解这些结构有助于更好地处理事件:

FORM_LOADED 事件数据

// 事件数据结构
{
  // 简单对象,标识表单已加载
  // 无特定属性
}

FIELD_CHANGE 事件数据

// 事件数据结构
{
  fieldName: string,    // 发生变化的字段名称
  value: any,          // 字段的新值
  previousValue: any,  // 字段的旧值(如果可用)
  timestamp: number    // 变化发生的时间戳
}

BEFORE_SUBMIT 事件数据

// 事件数据结构  
{
  values: object,      // 表单数据对象(可修改)
  formElement: HTMLFormElement,  // 表单DOM元素
  submitButton: HTMLElement      // 触发提交的按钮元素
}

AFTER_SUBMIT 事件数据

// 事件数据结构
{
  result: any,         // 服务器响应数据
  success: boolean,    // 提交是否成功
  statusCode: number,  // HTTP状态码
  duration: number,    // 提交耗时(毫秒)
  submittedData: object // 已提交的数据
}

参数说明

通用参数类型

  • eventName {string} - 事件名称,通过kinlink.FormEvents常量获取
  • callback {function} - 事件回调函数,接收事件数据作为参数
  • listenerId {string} - 监听器唯一标识符,用于移除监听器
  • data {object} - 事件数据对象,包含事件相关信息

返回值类型

  • string - 监听器ID(on方法返回)
  • boolean - 操作成功状态(BEFORE_SUBMIT事件回调返回)
  • undefined - 无返回值的操作