动态调查表示例

动态调查表系统概览

构建智能化问卷调查系统,实现个性化问题分支和数据收集

此示例演示如何使用kinlink API创建动态调查表,根据用户回答自动调整后续问题。通过智能分支逻辑条件显示数据分析,提供个性化的调查体验。

动态调查表核心功能

  • 🔀 条件分支:根据答案动态显示相关问题
  • 🎯 智能筛选:自动过滤不相关的问题项
  • 📊 实时统计:即时分析和展示调查数据
  • 🔄 逻辑跳转:基于回答结果的智能跳转
  • 📝 自适应问题:动态生成个性化问题
  • 💡 提示引导:智能提供填写建议和帮助

关键功能

  • 基于条件的字段显示隐藏
  • 动态问题分支和逻辑跳转
  • 实时数据验证和分析
  • 个性化用户体验

代码示例

/**
* 动态调查表示例
* 功能:创建智能化问卷调查系统,根据用户回答动态调整问题
*/
(function () {
// 调查表配置
const SURVEY_CONFIG = {
  // 用户类型分支
  USER_TYPE: {
    fieldCode: '单选框_0', // 用户类型选择
    options: {
      'individual': {
        label: '个人用户',
        nextFields: ['文字列__1行__0', '文字列__1行__1', '日期_1'],
        hiddenFields: ['单行文本框_10', '下拉菜单']
      },
      'enterprise': {
        label: '企业用户',
        nextFields: ['单行文本框_10', '下拉菜单', '文字列__1行__2'],
        hiddenFields: ['文字列__1行__1', '日期_1']
      }
    }
  },
  
  // 体验评价分支
  EXPERIENCE_RATING: {
    fieldCode: '单选框_1', // 体验评分
    options: {
      'excellent': {
        label: '非常满意',
        nextFields: ['多行文本框_0'], // 满意原因
        hiddenFields: ['多行文本框_1', '多选_0']
      },
      'good': {
        label: '比较满意',
        nextFields: ['多行文本框_0'],
        hiddenFields: ['多行文本框_1', '多选_0']
      },
      'poor': {
        label: '不满意',
        nextFields: ['多行文本框_1', '多选_0'], // 不满意原因和改进建议
        hiddenFields: ['多行文本框_0']
      }
    }
  }
};

// 调查统计数据
let surveyStats = {
  totalResponses: 0,
  userTypeDistribution: {},
  experienceRatings: {},
  completionRate: 0
};

kinlink.events.on(kinlink.FormEvents.FORM_LOADED, () => {
  try {
    console.log('初始化动态调查表系统...');
    
    // 创建调查管理面板
    createSurveyDashboard();
    
    // 初始化调查逻辑
    initializeSurveyLogic();
    
    // 设置初始状态
    setupInitialState();
    
    console.log('动态调查表系统初始化完成');
    
  } catch (error) {
    console.error('动态调查表初始化失败:', error);
  }
});

// 创建调查管理面板
function createSurveyDashboard() {
  const dashboardHTML = `
    <div id="surveyDashboard" style="
      background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
      padding: 25px;
      margin: 20px 0;
      border-radius: 15px;
      color: white;
      box-shadow: 0 6px 25px rgba(231, 76, 60, 0.3);
    ">
      <div style="text-align: center; margin-bottom: 25px;">
        <h2 style="margin: 0 0 10px 0; font-size: 26px; font-weight: 700;">
          📊 智能调查系统
        </h2>
        <div style="font-size: 15px; opacity: 0.95;">
          根据您的回答,系统将智能调整后续问题
        </div>
      </div>
      
      <div id="surveyStats" style="
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
        gap: 15px;
        margin-bottom: 20px;
      ">
        <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;">
          <div style="font-size: 24px; font-weight: 600; margin-bottom: 5px;">0</div>
          <div style="font-size: 13px; opacity: 0.9;">总回答数</div>
        </div>
        <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;">
          <div style="font-size: 24px; font-weight: 600; margin-bottom: 5px;">0%</div>
          <div style="font-size: 13px; opacity: 0.9;">完成率</div>
        </div>
        <div style="background: rgba(255,255,255,0.15); padding: 15px; border-radius: 10px; text-align: center;">
          <div style="font-size: 24px; font-weight: 600; margin-bottom: 5px;">-</div>
          <div style="font-size: 13px; opacity: 0.9;">当前分支</div>
        </div>
      </div>
      
      <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px;">
        <button id="resetSurvey" style="padding: 12px; background: rgba(255,255,255,0.2); color: white; border: 2px solid rgba(255,255,255,0.3); border-radius: 8px; cursor: pointer; transition: all 0.3s ease; font-weight: 500;">
          🔄 重置调查
        </button>
        <button id="showBranches" style="padding: 12px; background: rgba(255,255,255,0.2); color: white; border: 2px solid rgba(255,255,255,0.3); border-radius: 8px; cursor: pointer; transition: all 0.3s ease; font-weight: 500;">
          🌿 显示分支
        </button>
        <button id="exportData" style="padding: 12px; background: rgba(255,255,255,0.2); color: white; border: 2px solid rgba(255,255,255,0.3); border-radius: 8px; cursor: pointer; transition: all 0.3s ease; font-weight: 500;">
          📤 导出数据
        </button>
        <button id="previewMode" style="padding: 12px; background: rgba(255,255,255,0.2); color: white; border: 2px solid rgba(255,255,255,0.3); border-radius: 8px; cursor: pointer; transition: all 0.3s ease; font-weight: 500;">
          👁️ 预览模式
        </button>
      </div>
      
      <div id="currentBranch" style="
        background: rgba(0,0,0,0.2);
        padding: 12px;
        border-radius: 8px;
        margin-top: 15px;
        font-size: 13px;
        font-family: monospace;
      ">
        当前状态:等待用户选择...
      </div>
    </div>
  `;

  // 插入面板到表单顶部
  const formElement = document.querySelector('.ant-form') || document.body;
  const dashboardElement = document.createElement('div');
  dashboardElement.innerHTML = dashboardHTML;
  formElement.insertBefore(dashboardElement, formElement.firstChild);

  // 绑定面板事件
  bindDashboardEvents();
}

// 绑定面板事件
function bindDashboardEvents() {
  const dashboard = document.getElementById('surveyDashboard');
  if (!dashboard) return;

  dashboard.addEventListener('click', (e) => {
    const target = e.target;
    
    if (target.id === 'resetSurvey') {
      resetSurveyState();
    } else if (target.id === 'showBranches') {
      showBranchVisualization();
    } else if (target.id === 'exportData') {
      exportSurveyData();
    } else if (target.id === 'previewMode') {
      togglePreviewMode();
    }
  });
}

// 初始化调查逻辑
function initializeSurveyLogic() {
  // 监听字段值变化
  kinlink.events.on(kinlink.FormEvents.FIELD_VALUE_CHANGED, handleFieldChange);
  
  // 设置初始字段状态
  hideAllConditionalFields();
}

// 处理字段值变化
function handleFieldChange(data) {
  const { fieldCode, value } = data;
  
  console.log(`字段变化: ${fieldCode} = ${value}`);
  
  // 处理用户类型选择
  if (fieldCode === SURVEY_CONFIG.USER_TYPE.fieldCode) {
    handleUserTypeChange(value);
  }
  
  // 处理体验评价选择
  if (fieldCode === SURVEY_CONFIG.EXPERIENCE_RATING.fieldCode) {
    handleExperienceRatingChange(value);
  }
  
  // 更新统计数据
  updateSurveyStats();
  
  // 更新分支状态显示
  updateBranchStatus();
}

// 处理用户类型变化
function handleUserTypeChange(userType) {
  const config = SURVEY_CONFIG.USER_TYPE.options[userType];
  if (!config) return;

  console.log(`用户类型切换到: ${config.label}`);
  
  // 显示相关字段
  config.nextFields.forEach(fieldCode => {
    kinlink.formApi.showField(fieldCode);
    
    // 添加视觉效果
    setTimeout(() => {
      const element = kinlink.formApi.getFieldElement(fieldCode);
      if (element) {
        element.style.transition = 'all 0.5s ease';
        element.style.transform = 'translateX(-10px)';
        setTimeout(() => {
          element.style.transform = 'translateX(0)';
        }, 200);
      }
    }, 100);
  });
  
  // 隐藏不相关字段
  config.hiddenFields.forEach(fieldCode => {
    kinlink.formApi.hideField(fieldCode);
  });
  
  kinlink.formApi.showMessage('info', `已切换到${config.label}相关问题`, 2);
}

// 处理体验评价变化
function handleExperienceRatingChange(rating) {
  const config = SURVEY_CONFIG.EXPERIENCE_RATING.options[rating];
  if (!config) return;

  console.log(`体验评价: ${config.label}`);
  
  // 显示相关字段
  config.nextFields.forEach(fieldCode => {
    kinlink.formApi.showField(fieldCode);
    
    // 为不同评价添加不同的提示
    const element = kinlink.formApi.getFieldElement(fieldCode);
    if (element) {
      const input = element.querySelector('textarea, input');
      if (input) {
        if (rating === 'poor') {
          input.placeholder = '请详细说明您遇到的问题,我们会认真改进';
          input.style.borderColor = '#e74c3c';
        } else {
          input.placeholder = '感谢您的肯定,请分享更多想法';
          input.style.borderColor = '#27ae60';
        }
      }
    }
  });
  
  // 隐藏不相关字段
  config.hiddenFields.forEach(fieldCode => {
    kinlink.formApi.hideField(fieldCode);
  });
  
  const messageType = rating === 'poor' ? 'warning' : 'success';
  kinlink.formApi.showMessage(messageType, `感谢您的${config.label}评价`, 2);
}

// 设置初始状态
function setupInitialState() {
  // 隐藏所有条件字段
  hideAllConditionalFields();
  
  // 显示欢迎消息
  setTimeout(() => {
    kinlink.formApi.showMessage('info', '欢迎参与调查!请选择您的用户类型开始', 3);
  }, 1000);
}

// 隐藏所有条件字段
function hideAllConditionalFields() {
  const allConditionalFields = [
    ...Object.values(SURVEY_CONFIG.USER_TYPE.options).flatMap(option => [...option.nextFields, ...option.hiddenFields]),
    ...Object.values(SURVEY_CONFIG.EXPERIENCE_RATING.options).flatMap(option => [...option.nextFields, ...option.hiddenFields])
  ];
  
  // 去重
  const uniqueFields = [...new Set(allConditionalFields)];
  
  uniqueFields.forEach(fieldCode => {
    kinlink.formApi.hideField(fieldCode);
  });
}

// 重置调查状态
function resetSurveyState() {
  if (confirm('确定要重置调查吗?这将清除所有已填写的数据。')) {
    // 清空所有字段值
    Object.values(SURVEY_CONFIG).forEach(config => {
      kinlink.formApi.setFieldValue(config.fieldCode, '');
      
      Object.values(config.options).forEach(option => {
        [...option.nextFields, ...option.hiddenFields].forEach(fieldCode => {
          kinlink.formApi.setFieldValue(fieldCode, '');
        });
      });
    });
    
    // 重置显示状态
    setupInitialState();
    
    // 重置统计数据
    surveyStats = {
      totalResponses: 0,
      userTypeDistribution: {},
      experienceRatings: {},
      completionRate: 0
    };
    
    updateSurveyStats();
    kinlink.formApi.showMessage('success', '调查已重置', 2);
  }
}

// 显示分支可视化
function showBranchVisualization() {
  let branchInfo = '调查分支结构:\n\n';
  
  branchInfo += '1. 用户类型分支:\n';
  Object.entries(SURVEY_CONFIG.USER_TYPE.options).forEach(([key, option]) => {
    branchInfo += `   • ${option.label}: 显示字段 ${option.nextFields.length} 个\n`;
  });
  
  branchInfo += '\n2. 体验评价分支:\n';
  Object.entries(SURVEY_CONFIG.EXPERIENCE_RATING.options).forEach(([key, option]) => {
    branchInfo += `   • ${option.label}: 显示字段 ${option.nextFields.length} 个\n`;
  });
  
  alert(branchInfo);
}

// 导出调查数据
function exportSurveyData() {
  const exportData = {
    timestamp: new Date().toISOString(),
    stats: surveyStats,
    currentResponse: getCurrentFormData()
  };
  
  const dataStr = JSON.stringify(exportData, null, 2);
  const dataBlob = new Blob([dataStr], { type: 'application/json' });
  
  const link = document.createElement('a');
  link.href = URL.createObjectURL(dataBlob);
  link.download = `survey-data-${new Date().getTime()}.json`;
  link.click();
  
  kinlink.formApi.showMessage('success', '数据已导出', 2);
}

// 获取当前表单数据
function getCurrentFormData() {
  const formData = {};
  
  Object.values(SURVEY_CONFIG).forEach(config => {
    formData[config.fieldCode] = kinlink.formApi.getFieldValue(config.fieldCode);
    
    Object.values(config.options).forEach(option => {
      [...option.nextFields, ...option.hiddenFields].forEach(fieldCode => {
        formData[fieldCode] = kinlink.formApi.getFieldValue(fieldCode);
      });
    });
  });
  
  return formData;
}

// 更新调查统计
function updateSurveyStats() {
  // 这里可以实现统计逻辑
  // 实际项目中可能需要从服务器获取数据
  console.log('更新调查统计数据...');
}

// 更新分支状态
function updateBranchStatus() {
  const branchStatus = document.getElementById('currentBranch');
  if (!branchStatus) return;

  const userType = kinlink.formApi.getFieldValue(SURVEY_CONFIG.USER_TYPE.fieldCode);
  const experience = kinlink.formApi.getFieldValue(SURVEY_CONFIG.EXPERIENCE_RATING.fieldCode);
  
  let status = '当前状态:';
  if (userType) {
    const userConfig = SURVEY_CONFIG.USER_TYPE.options[userType];
    status += `${userConfig ? userConfig.label : userType}`;
  }
  
  if (experience) {
    const expConfig = SURVEY_CONFIG.EXPERIENCE_RATING.options[experience];
    status += ` → ${expConfig ? expConfig.label : experience}`;
  }
  
  if (!userType && !experience) {
    status += '等待用户选择...';
  }
  
  branchStatus.textContent = status;
}

// 切换预览模式
let previewMode = false;
function togglePreviewMode() {
  previewMode = !previewMode;
  
  if (previewMode) {
    // 在预览模式下显示所有字段
    Object.values(SURVEY_CONFIG).forEach(config => {
      Object.values(config.options).forEach(option => {
        [...option.nextFields, ...option.hiddenFields].forEach(fieldCode => {
          kinlink.formApi.showField(fieldCode);
        });
      });
    });
    
    kinlink.formApi.showMessage('info', '预览模式:显示所有字段', 2);
  } else {
    // 退出预览模式,恢复正常逻辑
    setupInitialState();
    kinlink.formApi.showMessage('info', '已退出预览模式', 2);
  }
  
  const button = document.getElementById('previewMode');
  if (button) {
    button.textContent = previewMode ? '🔙 退出预览' : '👁️ 预览模式';
  }
}
})();

使用说明

要使用此功能,只需将代码复制到您的kinlink自定义JavaScript中。系统会根据用户的选择自动显示相关问题,隐藏不相关的内容,创造个性化的调查体验。

注意事项

动态调查表需要根据您的表单字段配置调整分支逻辑。请确保字段代码正确,并测试所有分支路径是否按预期工作。

API参考

此示例使用了以下kinlink API:
  • kinlink.events.on(eventName, callback) - 注册事件监听器

  • kinlink.FormEvents.FORM_LOADED - 表单加载完成事件

  • kinlink.FormEvents.FIELD_VALUE_CHANGED - 字段值变化事件

  • kinlink.formApi.hideField(fieldCode) - 隐藏指定字段

  • kinlink.formApi.showField(fieldCode) - 显示指定字段

  • kinlink.formApi.getFieldValue(fieldCode) - 获取字段值

  • kinlink.formApi.setFieldValue(fieldCode, value) - 设置字段值

  • kinlink.formApi.getFieldElement(fieldCode) - 获取字段DOM元素

  • kinlink.formApi.showMessage(type, message, duration) - 显示消息提示