动态调查表系统概览
构建智能化问卷调查系统,实现个性化问题分支和数据收集
此示例演示如何使用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)- 显示消息提示