概述
FsYxt Web SDK 是一款面向营销官网的 JavaScript SDK,提供访客追踪、事件埋点、表单管理、会员体系等核心功能,帮助企业实现精准的营销数据采集与用户行为分析。
快速开始
方式一:推荐方式
步骤1: 引入SDK
<script>
(function () {
var s = document.createElement("script");
s.type = "text/javascript";
s.charset = "utf-8";
s.src = "https://www.fxiaoke.com/ec/kemai/release/static/marketing-website-access.js?id=" + Math.random();
document.getElementsByTagName("head")[0].appendChild(s);
})();
</script>步骤2: 初始化配置
window.addEventListener('load', function() {
FsYxt.configure({
ea: "your-enterprise-ea", // 企业ea(必填)
websiteId: "your-website-id", // 网站ID(必填)
host: "your-host.com", // API HOST地址(必填)
enableSpaTracking: false // 是否启用SPA单页应用埋点(可选)
});
});方式二:旧方式(不推荐)
注意: 此方式已过时,仅为兼容旧项目保留,新项目请使用方式一。
引入SDK并初始化配置(一步完成)
<script>
(function () {
var config = {
ea: 'your-enterprise-ea',
websiteId: 'your-website-id',
host: 'your-host.com'
};
var s = document.createElement("script");
s.type = "text/javascript";
s.charset = "utf-8";
s.src = "https://www.fxiaoke.com/ec/kemai/release/static/marketing-website-access.js?id=" + Math.random();
s.id = "fsMarketingWebsiteScript";
s.innerHTML = [
"FSscriptArgs.WebsiteEa='" + config.ea + "'",
"FSscriptArgs.WebsiteId='" + config.websiteId + "'",
"FSscriptArgs.host='" + config.host + "'"
].join(';');
window.FsYxtWebsiteEa = config.ea;
window.FsYxtWebsiteId = config.websiteId;
window.FsYxtHost = config.host;
document.getElementsByTagName("head")[0].appendChild(s);
})();
</script>使用旧方式时,配置已在脚本加载时完成,无需再调用
FsYxt.configure()。监听初始化状态
window.addEventListener('load', function() {
// SDK初始化成功
FsYxt.on('onReady', function() {
console.log('SDK已准备就绪');
});
// SDK初始化失败
FsYxt.on('onError', function(error) {
console.error('SDK初始化失败:', error);
});
});核心API
1. configure(config)
初始化SDK配置,必须在使用其他功能前调用。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| ea | String | 是 | 应用源ID |
| websiteId | String | 是 | 网站ID |
| host | String | 是 | 主机地址 |
| enableSpaTracking | Boolean | 否 | 是否启用单页应用(SPA)埋点,默认false |
使用示例
FsYxt.configure({
ea: "88146",
websiteId: "8b28bec67afc42c281f078ff9bea7fec",
host: "crm.example.com"
});2. track(params)
上报自定义事件埋点,用于追踪用户行为。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| eventId | String | 是 | 事件ID |
| attributesId | String/Array | 否 | 事件属性ID(传数组时取第一个) |
| marketingEventId | String | 否 | 市场活动ID |
| eventDescription | String | 否 | 事件描述 |
| success | Function | 否 | 成功回调函数 |
| fail | Function | 否 | 失败回调函数 |
使用示例
场景一:单一事件单一按钮上报
适用于单个按钮的埋点上报场景,直接在代码中调用
track 方法。示例代码:
// 点击下载按钮时上报
document.getElementById('downloadBtn').addEventListener('click', function() {
FsYxt.track({
eventId: 'button_click',
attributesId: 'header_download_btn',
eventDescription: '点击下载按钮',
marketingEventId: 'xxx',
success: function(data) {
console.log('埋点上报成功', data);
},
fail: function(error) {
console.error('埋点上报失败', error);
}
});
});场景二:多个按钮统一上报(推荐)
适用于页面上有多个按钮需要埋点的场景,通过 HTML 自定义属性配置埋点参数,统一绑定事件处理,减少重复代码。
实现思路:
- 页面上多个按钮使用统一的 class(如
.js-track-btn)标识需要埋点的按钮 - 每个按钮通过
data-*属性配置埋点参数(eventId、attributesId、eventDescription等) - 统一绑定点击事件,从按钮属性中读取埋点参数并上报
- 所有埋点只需维护一段 JS 逻辑,易于维护和扩展
HTML 示例:
<!-- 下载按钮 -->
<button
class="js-track-btn"
data-event-id="button_click"
data-attributes-id="header_download_btn"
data-event-desc="头部下载按钮"
>
下载
</button>
<!-- 查看更多按钮 -->
<button
class="js-track-btn"
data-event-id="button_click"
data-attributes-id="banner_more_btn"
data-event-desc="Banner 查看更多按钮"
>
查看更多
</button>
<!-- 咨询按钮 -->
<button
class="js-track-btn"
data-event-id="button_click"
data-attributes-id="footer_consult_btn"
data-event-desc="底部联系咨询按钮"
>
咨询我们
</button>JavaScript 统一绑定:
// 统一绑定所有按钮的埋点事件
document.addEventListener("click", function (e) {
const btn = e.target.closest(".js-track-btn");
if (!btn) return;
// 从按钮的 data 属性中读取埋点参数
const eventId = btn.dataset.eventId;
const attributesId = btn.dataset.attributesId;
const eventDescription = btn.dataset.eventDesc;
const marketingEventId = btn.dataset.marketingEventId; // 可选
// 调用埋点上报
FsYxt.track({
eventId: eventId,
attributesId: attributesId,
eventDescription: eventDescription,
marketingEventId: marketingEventId,
success(data) {
console.log("埋点上报成功", data);
},
fail(error) {
console.error("埋点上报失败", error);
},
});
});优势说明:
- 易于维护:所有埋点只需维护一段 JS 逻辑
- 灵活扩展:新增按钮只需添加 HTML 属性,无需修改 JS 代码
- 自动区分:后台会自动根据
attributesId区分不同按钮来源 - 统一管理:所有埋点参数集中在 HTML 中,便于查看和管理
3. createForm(params)
动态创建表单iframe,用于在页面中嵌入营销表单。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| id | String | 是 | 表单容器元素的ID |
| src | String | 是 | 表单页面URL |
| style | String | 否 | iframe样式(CSS字符串) |
| autoheight | Boolean | 否 | 是否自动适应高度 |
使用示例
FsYxt.createForm({
id: 'form-container',
src: 'https://crm.example.com/proj/page/marketing-page?id=xxx',
style: 'width:100%;height:600px;border:none;',
autoheight: true
});<!-- 在页面中预留表单容器 -->
<div id="form-container"></div>4. generateQrcode(type, fanQrCodeId, options)
生成营销推广二维码(支持公众号/企业微信)。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| type | String | 是 | 二维码类型: "gzh"(公众号) 或 "qw"(企业微信) |
| fanQrCodeId | String | 是 | 场景ID或粉丝码ID |
| options | Object | 否 | 配置选项 |
| options.renderType | String | 否 | 渲染类型: "auto"(自动渲染) 或 "manual"(手动渲染),默认"auto" |
| options.success | Function | 否 | 成功回调函数 |
| options.fail | Function | 否 | 失败回调函数 |
使用示例
// 自动渲染模式
FsYxt.generateQrcode('gzh', 'xxx', {
renderType: 'auto',
success: function(result) {
console.log('二维码容器:', result.container);
console.log('二维码URL:', result.qrUrl);
},
fail: function(error) {
console.error('生成失败:', error);
}
});
// 手动渲染模式
FsYxt.generateQrcode('qw', 'xxx', {
renderType: 'manual',
success: function(result) {
// 自行处理二维码URL
document.getElementById('my-qrcode').src = result.qrUrl;
}
});5. memberOperationIntercept(params)
会员操作拦截,限制非会员访问特定功能。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| className | String | 是 | 需要拦截的元素类名 |
| onFormSubmit | Function | 否 | 表单提交回调函数 |
| expires | Number | 否 | 会员登录态过期时间(毫秒) |
使用示例
FsYxt.memberOperationIntercept({
className: 'premium-download-btn',
expires: 24 * 60 * 60 * 1000, // 24小时
onFormSubmit: function(result) {
if (result.status === 'ok') {
console.log('会员登录成功');
}
}
});<!-- 需要拦截的元素 -->
<a href="download.zip" class="premium-download-btn">下载资源</a>6. memberSignIn(params)
嵌入式会员登录表单(仅PC端)。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| className | String | 是 | 登录表单容器元素类名 |
| onFormSubmit | Function | 否 | 表单提交回调函数 |
| expires | Number | 否 | 会员登录态过期时间(毫秒) |
使用示例
FsYxt.memberSignIn({
className: 'member-login-container',
expires: 7 * 24 * 60 * 60 * 1000, // 7天
onFormSubmit: function(result) {
if (result.status === 'ok') {
console.log('登录成功');
// 跳转或刷新页面
location.reload();
}
}
});<div class="member-login-container"></div>7. trackSinglePage()
为单页应用(SPA)启用路由变化埋点追踪。
注意: 使用
configure() 初始化时,推荐通过 enableSpaTracking: true 参数启用,无需单独调用此方法。使用示例
// 方式一: 推荐
FsYxt.configure({
ea: "88146",
websiteId: "xxx",
host: "crm.example.com",
enableSpaTracking: true // 启用SPA埋点
});
// 方式二: 手动调用
FsYxt.trackSinglePage();8. getConfig()
获取SDK当前配置信息,包含访客ID、UTM参数等。
返回值
返回一个包含以下字段的对象:
| 字段名 | 类型 | 说明 |
| visitorId | String | 访客唯一标识 |
| fsWebsiteUid | String | 网站访客ID |
| websiteId | String | 网站ID |
| host | String | 主机地址 |
| ea | String | 应用源ID |
| utmSource | String | UTM来源 |
| utmMedium | String | UTM媒介 |
| utmCampaign | String | UTM活动 |
| utmContent | String | UTM内容 |
| utmTerm | String | UTM关键词 |
| ... | ... | 其他扩展参数 |
使用示例
var config = FsYxt.getConfig();
console.log('访客ID:', config.visitorId);
console.log('UTM来源:', config.utmSource);
console.log('完整配置:', config);9. on(eventName, handler)
绑定SDK事件回调。
支持的事件
| 事件名 | 触发时机 | 回调参数 |
| onReady | SDK初始化完成 | 无 |
| onError | SDK初始化失败 | (errorMessage: String) |
| onFormSubmit | 表单提交完成 | (result: Object) |
使用示例
// SDK就绪事件
FsYxt.on('onReady', function() {
console.log('SDK已准备就绪');
});
// 错误事件
FsYxt.on('onError', function(error) {
console.error('初始化错误:', error);
});
// 表单提交事件
FsYxt.on('onFormSubmit', function(result) {
console.log('提交状态:', result.status); // 'ok' 或 'fail'
console.log('表单数据:', result.formData);
console.log('服务器响应:', result.response);
});FormSDK API
FsYxt.FormSDK 提供表单相关的高级功能,适用于自定义表单场景。1. FormSDK.getFieldDescriptions(params)
获取表单字段配置信息,包括字段类型、选项值、验证规则等。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| formId | String | 是 | 表单ID |
| extendParams | Object | 否 | 扩展参数 |
| success | Function | 否 | 成功回调函数 |
| fail | Function | 否 | 失败回调函数 |
返回数据结构
{
info: {
name: "表单名称",
title: "表单标题"
},
fields: [
{
apiName: "email", // 字段API名称,提交时使用此字段名
type: "email", // 字段类型
defineType: "custom", // 定义类型: "custom"(自定义) 或 "system"(系统字段)
label: "Email", // 字段标签
isRequired: true, // 是否必填
isVerify: false, // 是否需要验证
helpText: "Business Email", // 帮助文本
dateFormat: "yyyy-MM-dd HH:mm", // 日期格式(仅日期类型字段)
options: [ // 选项列表(仅单选/多选字段)
{
label: "选项显示文本",
value: "选项值" // 提交时使用此值
}
]
}
]
}字段类型说明
| type 值 | 字段类型 | 说明 |
| text | 单行文本 | 普通文本输入 |
| multi_text | 多行文本 | 多行文本输入 |
| 邮箱 | 邮箱格式验证 | |
| phone_number | 手机号 | 手机号格式验证 |
| select_one | 单选 | 需要从 options 中获取选项值 |
| select_manny | 多选 | 需要从 options 中获取选项值,提交时传数组 |
| number | 数字 | 数字类型 |
| date_time | 日期时间 | 日期时间类型,传值为时间戳(毫秒) |
| image | 图片 | 图片上传 |
| files | 文件附件 | 文件上传 |
使用示例
// 获取表单字段配置
FsYxt.FormSDK.getFieldDescriptions({
formId: 'xxx',
success: function(data) {
console.log('表单名称:', data.info.name);
console.log('表单标题:', data.info.title);
// 遍历字段配置
data.fields.forEach(function(field) {
console.log('字段名:', field.apiName);
console.log('字段类型:', field.type);
console.log('是否必填:', field.isRequired);
// 如果是单选或多选,获取选项值
if (field.type === 'select_one' || field.type === 'select_manny') {
console.log('选项列表:', field.options);
// 构建下拉选项
field.options.forEach(function(option) {
console.log('选项:', option.label, '值:', option.value);
});
}
});
},
fail: function(error) {
console.error('获取失败:', error);
}
});2. FormSDK.submitForm(params)
提交自定义表单数据。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| formId | String | 是 | 表单ID |
| enrollId | String | 否 | 报名ID(用于更新已有报名) |
| sceneType | Number | 否 | 场景类型: 0-当前页为转化页, 2-上一页为转化页 |
| data | Object | 是 | 表单数据 |
| extendParams | Object | 否 | 扩展参数 |
| success | Function | 否 | 成功回调函数 |
| fail | Function | 否 | 失败回调函数 |
表单数据字段说明
重要提示:字段名称请使用
getFieldDescriptions 接口返回的 apiName 字段值对于单选(select_one)、多选(select_manny)字段,必须使用 getFieldDescriptions 接口返回的 options 中的 value 值系统字段(如 name、phone、email 等)和自定义字段(如 text5_xxx、text7_xxx 等)的传值方式相同系统字段(常用)
| apiName | 字段类型 | 传值类型 | 示例 | 说明 |
| name | 姓名 | String | name: "张三" | 文本字符串 |
| phone | 手机号 | String | phone: "13800138000" | 手机号格式字符串 |
| 邮箱 | String | email: "test@example.com" | 邮箱格式字符串 | |
| companyName | 公司 | String | company: "XX科技有限公司" | 公司名称 |
| position | 职位 | String | position: "产品经理" | 职位名称 |
| country | 国家 | String | country: "CN" | 国家编码,从 getAreaData 获取 |
| province | 省 | String | province: "110000" | 省份编码,从 getAreaData 获取 |
| city | 市 | String | city: "110100" | 城市编码,从 getAreaData 获取 |
| district | 区 | String | district: "110101" | 区县编码,从 getAreaData 获取 |
| address | 详细地址 | String | address: "XX街道XX号" | 详细地址文本 |
自定义字段类型
| 字段类型 | type 值 | 传值类型 | 传值说明 | 示例 |
| 单行文本 | text | String | 直接传文本值 | text5_xxx: "网站地址" |
| 多行文本 | multi_text | String | 直接传文本值 | text6_xxx: "这是一段描述\n换行内容" |
| 邮箱 | String | 邮箱格式字符串 | email: "user@example.com" | |
| 手机号 | phone_number | String | 手机号格式字符串 | phone: "13800138000" |
| 单选 | select_one | String | 必须使用 options 中的 value 值 | text7_xxx: "ae3748076b822385" |
| 多选 | select_manny | Array | 必须使用 options 中的 value 值数组 | texts1_xxx: ["value1", "value2"] |
| 数字 | number | Number | 数字类型 | num1_xxx: 100 |
| 日期时间 | date_time | Number | 时间戳(毫秒) | num2_xxx: 1737360000000 |
| 图片 | image | String | 图片URL地址 | picMap_xxx: "https://example.com/image.jpg" |
| 文件附件 | files | String | 文件URL地址 | fileAttachmentMap_xxx: "https://example.com/file.pdf" |
字段传值注意事项
- 单选字段传值:
// ❌ 错误:使用选项的 label
text7_xxx: "0-4"
// ✅ 正确:使用选项的 value(从 getFieldDescriptions 获取)
text7_xxx: "1"- 多选字段传值(type 为
select_manny):
// ❌ 错误:使用选项的 label 数组
texts1_xxx: ["选项A", "选项B"]
// ✅ 正确:使用选项的 value 数组
texts1_xxx: ["ae3748076b822385", "445dad1a25b6f5ff"]- 日期时间字段传值(type 为
date_time):
// ❌ 错误:使用日期字符串
num2_xxx: "2025-01-20 14:30"
// ✅ 正确:使用时间戳(毫秒)
// 将日期转换为时间戳:new Date('2025-01-20 14:30').getTime()
num2_xxx: 1737360000000
// 或者使用当前时间戳
num2_xxx: new Date().getTime()- 地址字段传值(country、province、city、district):
// 地址字段需要传地址编码(字符串),可通过 getAreaData 获取
// getAreaData 返回的数据结构:{ country: { options: [...] }, province: { options: [...] }, ... }
// 步骤1: 获取地址数据
FsYxt.FormSDK.getAreaData({
success: function(data) {
// 步骤2: 从 options 中根据 label 查找对应的 value(编码)
var provinceOption = data.province.options.find(function(p) {
return p.label === "北京市";
});
var provinceCode = provinceOption ? provinceOption.value : "";
// 步骤3: 提交表单时使用编码
var formData = {
country: "248", // 国家编码(从 data.country.options 获取)
province: provinceCode, // 省份编码(使用 value,不是 label)
city: "110100", // 城市编码(从选中省份的 child_options 获取)
district: "110101", // 区县编码(从选中城市的 child_options 获取)
address: "XX街道XX号" // 详细地址使用文本
};
}
});
// ❌ 错误:使用地址名称
province: "北京市"
// ✅ 正确:使用地址编码(从 options 中的 value 获取)
province: "110000"- 必填字段:
- 通过
getFieldDescriptions获取字段配置,检查isRequired属性 - 所有
isRequired: true的字段必须传值
使用示例
示例一:基础提交(已知字段配置)
FsYxt.FormSDK.submitForm({
formId: 'xxx',
sceneType: 0,
data: {
name: '张三',
phone: '13800138000',
email: 'zhangsan@example.com',
companyName: 'XX科技有限公司',
position: '产品经理',
},
success: function(result) {
console.log('提交成功:', result);
alert('提交成功!');
},
fail: function(error) {
console.error('提交失败:', error);
alert('提交失败,请重试');
}
});示例二:先获取字段配置再提交(推荐)
var formId = 'xxx';
var formFields = {}; // 存储字段配置
// 步骤1: 获取表单字段配置
FsYxt.FormSDK.getFieldDescriptions({
formId: formId,
success: function(data) {
// 保存字段配置,便于后续使用
formFields = data.fields;
// 构建表单数据
var formData = {
name: '张三',
phone: '13800138000',
email: 'zhangsan@example.com',
companyName: 'XX科技有限公司',
position: '产品经理'
};
// 处理单选字段(必须使用 options 中的 value)
var teamSizeField = formFields.find(function(f) {
return f.apiName === 'text7_53804f9c6c384b3f';
});
if (teamSizeField && teamSizeField.options) {
// 假设用户选择了 "10-19" 选项
var selectedOption = teamSizeField.options.find(function(opt) {
return opt.label === '10-19';
});
if (selectedOption) {
formData[teamSizeField.apiName] = selectedOption.value; // 使用 value
}
}
// 处理多选字段(必须使用 options 中的 value 数组)
var sourceField = formFields.find(function(f) {
return f.type === 'select_manny';
});
if (sourceField && sourceField.options) {
// 假设用户选择了多个选项
var selectedValues = sourceField.options
.filter(function(opt) {
return ['选项A', '选项B'].includes(opt.label);
})
.map(function(opt) {
return opt.value; // 使用 value
});
if (selectedValues.length > 0) {
formData[sourceField.apiName] = selectedValues;
}
}
// 步骤2: 提交表单
FsYxt.FormSDK.submitForm({
formId: formId,
sceneType: 0,
data: formData,
success: function(result) {
console.log('提交成功:', result);
alert('提交成功!');
},
fail: function(error) {
console.error('提交失败:', error);
alert('提交失败: ' + error.errMsg);
}
});
},
fail: function(error) {
console.error('获取字段配置失败:', error);
}
});3. FormSDK.sendSMCode(params)
发送短信验证码。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| formId | String | 是 | 表单ID |
| mobile | String | 是 | 手机号码 |
| extendParams | Object | 否 | 扩展参数 |
| success | Function | 否 | 成功回调函数 |
| fail | Function | 否 | 失败回调函数 |
使用示例
FsYxt.FormSDK.sendSMCode({
formId: 'xxx',
mobile: '13800138000',
success: function(data) {
console.log('验证码发送成功');
alert('验证码已发送');
},
fail: function(error) {
console.error('验证码发送失败:', error);
alert('发送失败,请稍后重试');
}
});4. FormSDK.getAreaData(params)
获取省市区三级联动数据,用于地址字段(country、province、city、district)的编码获取。
参数说明
| 参数名 | 类型 | 必填 | 说明 |
| extendParams | Object | 否 | 扩展参数 |
| success | Function | 否 | 成功回调函数 |
| fail | Function | 否 | 失败回调函数 |
返回数据结构
返回一个对象(Map),包含
country、province、city、district 四个字段,每个字段包含对应的选项数据:{
country: {
api_name: "country",
define_type: "package",
is_required: false,
is_index: false,
label: "国家",
options: [
{
label: "中国", // 国家名称
value: "248", // 国家编码(提交时使用此值)
child_options: [...], // 子选项(省份数据)
resource_bundle_key: "",
standard_code: ""
},
{
label: "阿尔巴尼亚",
value: "0",
child_options: [...]
},
// ... 更多国家(共325个)
]
},
province: {
api_name: "province",
options: [
{
label: "北京市",
value: "110000", // 省份编码(提交时使用此值)
child_options: [...] // 子选项(城市数据)
},
// ... 更多省份
]
},
city: {
api_name: "city",
options: [
{
label: "北京市",
value: "110100", // 城市编码(提交时使用此值)
child_options: [...] // 子选项(区县数据)
},
// ... 更多城市(共427个)
]
},
district: {
api_name: "district",
options: [
{
label: "东城区",
value: "110101" // 区县编码(提交时使用此值)
},
// ... 更多区县
]
}
}选项对象说明:
label: 显示文本,用于展示给用户value: 选项值,提交表单时必须使用此值child_options: 子选项数组(可选),用于级联选择
使用示例
基础用法
FsYxt.FormSDK.getAreaData({
success: function(data) {
console.log('省市区数据:', data);
// 访问国家选项
if (data.country && data.country.options) {
data.country.options.forEach(function(country) {
console.log('国家:', country.label, '编码:', country.value);
});
}
// 访问省份选项
if (data.province && data.province.options) {
data.province.options.forEach(function(province) {
console.log('省份:', province.label, '编码:', province.value);
});
}
// 访问城市选项
if (data.city && data.city.options) {
console.log('城市总数:', data.city.options.length);
}
// 访问区县选项
if (data.district && data.district.options) {
console.log('区县总数:', data.district.options.length);
}
},
fail: function(error) {
console.error('获取失败:', error);
}
});三级联动选择器实现
var areaData = null;
var selectedCountry = null;
var selectedProvince = null;
var selectedCity = null;
var selectedDistrict = null;
// 获取省市区数据
FsYxt.FormSDK.getAreaData({
success: function(data) {
areaData = data;
// 初始化国家下拉框(可选)
var countrySelect = document.getElementById('country');
if (data.country && data.country.options) {
data.country.options.forEach(function(country) {
var option = document.createElement('option');
option.value = country.value; // 使用 value
option.textContent = country.label; // 显示 label
countrySelect.appendChild(option);
});
}
// 初始化省份下拉框
var provinceSelect = document.getElementById('province');
if (data.province && data.province.options) {
data.province.options.forEach(function(province) {
var option = document.createElement('option');
option.value = province.value; // 使用 value
option.textContent = province.label; // 显示 label
provinceSelect.appendChild(option);
});
}
// 省份变化时更新城市
provinceSelect.addEventListener('change', function() {
var provinceValue = this.value;
if (data.province && data.province.options) {
selectedProvince = data.province.options.find(function(p) {
return p.value === provinceValue;
});
}
// 更新城市下拉框
var citySelect = document.getElementById('city');
citySelect.innerHTML = '<option value="">请选择城市</option>';
if (selectedProvince && selectedProvince.child_options) {
selectedProvince.child_options.forEach(function(city) {
var option = document.createElement('option');
option.value = city.value; // 使用 value
option.textContent = city.label; // 显示 label
citySelect.appendChild(option);
});
}
});
// 城市变化时更新区县
document.getElementById('city').addEventListener('change', function() {
var cityValue = this.value;
if (selectedProvince && selectedProvince.child_options) {
selectedCity = selectedProvince.child_options.find(function(c) {
return c.value === cityValue;
});
}
// 更新区县下拉框
var districtSelect = document.getElementById('district');
districtSelect.innerHTML = '<option value="">请选择区县</option>';
if (selectedCity && selectedCity.child_options) {
selectedCity.child_options.forEach(function(district) {
var option = document.createElement('option');
option.value = district.value; // 使用 value
option.textContent = district.label; // 显示 label
districtSelect.appendChild(option);
});
}
});
},
fail: function(error) {
console.error('获取省市区数据失败:', error);
}
});
// 提交表单时使用编码
function submitForm() {
var formData = {
country: document.getElementById('country').value, // 国家编码(可选)
province: document.getElementById('province').value, // 省份编码
city: document.getElementById('city').value, // 城市编码
district: document.getElementById('district').value, // 区县编码
address: document.getElementById('address').value // 详细地址文本
};
FsYxt.FormSDK.submitForm({
formId: 'xxx',
sceneType: 0,
data: formData,
success: function(result) {
alert('提交成功!');
},
fail: function(error) {
alert('提交失败: ' + error.errMsg);
}
});
}根据名称查找编码
// 如果用户选择了地址名称,需要查找对应的编码
FsYxt.FormSDK.getAreaData({
success: function(data) {
// 根据省份名称查找编码
function findProvinceCode(provinceName) {
if (data.province && data.province.options) {
var province = data.province.options.find(function(p) {
return p.label === provinceName;
});
return province ? province.value : '';
}
return '';
}
// 使用示例
var provinceCode = findProvinceCode('北京市');
console.log('北京市的编码:', provinceCode); // 输出: "110000"
// 提交表单
var formData = {
province: provinceCode, // 使用编码,不是名称
// ... 其他字段
};
}
});完整示例
场景一: 营销活动落地页
<!DOCTYPE html>
<html>
<head>
<title>产品活动页</title>
<script>
(function () {
var s = document.createElement("script");
s.type = "text/javascript";
s.charset = "utf-8";
s.src = "https://www.fxiaoke.com/ec/kemai/release/static/marketing-website-access.js?id=" + Math.random();
document.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
</head>
<body>
<!-- 页面内容 -->
<button id="downloadBtn" class="premium-content">下载白皮书</button>
<!-- 表单容器 -->
<div id="marketing-form"></div>
<!-- 二维码容器 -->
<div id="qrcode"></div>
<script>
// 1. 初始化SDK
window.addEventListener('load', function() {
FsYxt.configure({
ea: "88146",
websiteId: "8b28bec67afc42c281f078ff9bea7fec",
host: "crm.example.com",
enableSpaTracking: false
});
// 2. 监听SDK就绪
FsYxt.on('onReady', function() {
console.log('SDK初始化完成');
// 3. 上报页面访问埋点
FsYxt.track({
eventId: 'page_view',
attributesId: 'landing_page',
eventDescription: '访问活动落地页'
});
// 4. 创建营销表单
FsYxt.createForm({
id: 'marketing-form',
src: 'https://crm.example.com/form/xxxxx',
style: 'width:100%;height:600px;',
autoheight: true
});
// 5. 生成推广二维码
FsYxt.generateQrcode('gzh', 'xxx', {
renderType: 'auto',
success: function(result) {
console.log('二维码已生成');
}
});
// 6. 设置会员拦截
FsYxt.memberOperationIntercept({
className: 'premium-content',
expires: 7 * 24 * 60 * 60 * 1000 // 7天
});
});
// 7. 监听表单提交
FsYxt.on('onFormSubmit', function(result) {
if (result.status === 'ok') {
// 上报转化埋点
FsYxt.track({
eventId: 'form_submit',
attributesId: 'marketing_form',
eventDescription: '提交营销表单'
});
alert('提交成功!');
}
});
// 8. 按钮点击埋点
document.getElementById('downloadBtn').addEventListener('click', function() {
FsYxt.track({
eventId: 'button_click',
attributesId: 'download_whitepaper',
eventDescription: '点击下载白皮书'
});
});
});
</script>
</body>
</html>场景二: 自定义表单提交(已知字段结构)
适用于已知表单字段结构的场景,手动构建表单并提交。
<!DOCTYPE html>
<html>
<head>
<title>自定义表单</title>
<script>
(function () {
var s = document.createElement("script");
s.type = "text/javascript";
s.charset = "utf-8";
s.src = "https://www.fxiaoke.com/ec/kemai/release/static/marketing-website-access.js?id=" + Math.random();
document.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
</head>
<body>
<form id="customForm">
<div>
<label>姓名 <span style="color:red">*</span></label>
<input type="text" id="name" name="name" placeholder="请输入姓名" required>
</div>
<div>
<label>手机号 <span style="color:red">*</span></label>
<input type="tel" id="phone" name="phone" placeholder="请输入手机号" required>
<button type="button" id="sendCodeBtn">发送验证码</button>
</div>
<div>
<label>验证码 <span style="color:red">*</span></label>
<input type="text" id="smscode" name="smscode" placeholder="请输入验证码" required>
</div>
<div>
<label>邮箱</label>
<input type="email" id="email" name="email" placeholder="请输入邮箱">
</div>
<div>
<label>公司名称</label>
<input type="text" id="companyName" name="companyName" placeholder="请输入公司名称">
</div>
<div>
<label>职位</label>
<input type="text" id="position" name="position" placeholder="请输入职位">
</div>
<button type="submit">提交</button>
</form>
<script>
var formId = 'xxx';
var countdown = 0;
// 初始化SDK
window.addEventListener('load', function() {
FsYxt.configure({
ea: "88146",
websiteId: "8b28bec67afc42c281f078ff9bea7fec",
host: "crm.example.com"
});
});
// 发送验证码
document.getElementById('sendCodeBtn').addEventListener('click', function() {
if (countdown > 0) return;
var phone = document.getElementById('phone').value;
if (!phone) {
alert('请输入手机号');
return;
}
FsYxt.FormSDK.sendSMCode({
formId: formId,
mobile: phone,
success: function() {
alert('验证码已发送');
countdown = 60;
var btn = document.getElementById('sendCodeBtn');
var timer = setInterval(function() {
countdown--;
btn.innerText = countdown + 's';
if (countdown <= 0) {
clearInterval(timer);
btn.innerText = '发送验证码';
}
}, 1000);
},
fail: function(error) {
alert('发送失败: ' + error.errMsg);
}
});
});
// 提交表单
document.getElementById('customForm').addEventListener('submit', function(e) {
e.preventDefault();
// 收集表单数据(使用字段的 apiName)
var formData = {
name: document.getElementById('name').value,
phone: document.getElementById('phone').value,
smscode: document.getElementById('smscode').value,
email: document.getElementById('email').value,
companyName: document.getElementById('companyName').value,
position: document.getElementById('position').value
};
FsYxt.FormSDK.submitForm({
formId: formId,
sceneType: 0,
data: formData,
success: function(result) {
alert('提交成功!');
document.getElementById('customForm').reset();
},
fail: function(error) {
alert('提交失败: ' + error.errMsg);
}
});
});
</script>
</body>
</html>场景三: 动态渲染表单(根据字段配置自动生成)
适用于需要根据表单配置动态生成表单的场景,无需手动编写HTML,完全由SDK字段配置驱动。
<!DOCTYPE html>
<html>
<head>
<title>动态表单</title>
<script>
(function () {
var s = document.createElement("script");
s.type = "text/javascript";
s.charset = "utf-8";
s.src = "https://www.fxiaoke.com/ec/kemai/release/static/marketing-website-access.js?id=" + Math.random();
document.getElementsByTagName("head")[0].appendChild(s);
})();
</script>
<style>
.form-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.form-group textarea {
min-height: 80px;
}
.form-group .help-text {
font-size: 12px;
color: #666;
margin-top: 4px;
}
.required {
color: red;
}
.checkbox-group {
margin: 5px 0;
}
.checkbox-group label {
font-weight: normal;
margin-left: 5px;
}
.send-code-btn {
margin-left: 10px;
padding: 8px 15px;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.send-code-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.submit-btn {
width: 100%;
padding: 12px;
background: #28a745;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="form-container">
<h2 id="form-title">加载中...</h2>
<form id="dynamicForm"></form>
</div>
<script>
var formId = 'xxx';
var countdown = 0;
var formFields = []; // 存储字段配置
// 初始化SDK
window.addEventListener('load', function() {
FsYxt.configure({
ea: "88146",
websiteId: "8b28bec67afc42c281f078ff9bea7fec",
host: "crm.example.com"
});
// 等待SDK就绪后加载表单
FsYxt.on('onReady', function() {
loadForm();
});
});
// 加载表单配置并渲染
function loadForm() {
FsYxt.FormSDK.getFieldDescriptions({
formId: formId,
success: function(data) {
formFields = data.fields;
// 设置表单标题
document.getElementById('form-title').textContent = data.info.title || data.info.name || '表单';
// 渲染表单
renderForm(data.fields);
},
fail: function(error) {
console.error('获取表单配置失败:', error);
document.getElementById('form-title').textContent = '表单加载失败';
}
});
}
// 渲染表单字段
function renderForm(fields) {
var form = document.getElementById('dynamicForm');
form.innerHTML = ''; // 清空表单
fields.forEach(function(field) {
var formGroup = document.createElement('div');
formGroup.className = 'form-group';
// 创建标签
var label = document.createElement('label');
label.textContent = field.label;
if (field.isRequired) {
var required = document.createElement('span');
required.className = 'required';
required.textContent = ' *';
label.appendChild(required);
}
formGroup.appendChild(label);
// 根据字段类型创建输入元素
var inputElement = null;
if (field.type === 'text' || field.type === 'email' || field.type === 'phone_number') {
inputElement = document.createElement('input');
inputElement.type = field.type === 'email' ? 'email' :
field.type === 'phone_number' ? 'tel' : 'text';
inputElement.name = field.apiName;
inputElement.placeholder = field.helpText || field.label;
inputElement.required = field.isRequired;
// 如果是手机号字段,添加发送验证码按钮
if (field.type === 'phone_number') {
var phoneContainer = document.createElement('div');
phoneContainer.style.display = 'flex';
phoneContainer.style.alignItems = 'center';
inputElement.style.flex = '1';
phoneContainer.appendChild(inputElement);
var sendCodeBtn = document.createElement('button');
sendCodeBtn.type = 'button';
sendCodeBtn.className = 'send-code-btn';
sendCodeBtn.textContent = '发送验证码';
sendCodeBtn.onclick = function() {
sendSMCode(field.apiName, sendCodeBtn);
};
phoneContainer.appendChild(sendCodeBtn);
formGroup.appendChild(phoneContainer);
} else {
formGroup.appendChild(inputElement);
}
} else if (field.type === 'multi_text') {
inputElement = document.createElement('textarea');
inputElement.name = field.apiName;
inputElement.placeholder = field.helpText || field.label;
inputElement.required = field.isRequired;
formGroup.appendChild(inputElement);
} else if (field.type === 'select_one') {
inputElement = document.createElement('select');
inputElement.name = field.apiName;
inputElement.required = field.isRequired;
// 添加空选项
var emptyOption = document.createElement('option');
emptyOption.value = '';
emptyOption.textContent = '请选择';
inputElement.appendChild(emptyOption);
// 添加选项(使用 value 作为选项值)
if (field.options && field.options.length > 0) {
field.options.forEach(function(option) {
var optionElement = document.createElement('option');
optionElement.value = option.value; // 使用 value
optionElement.textContent = option.label;
inputElement.appendChild(optionElement);
});
}
formGroup.appendChild(inputElement);
} else if (field.type === 'select_manny') {
// 多选使用 checkbox
if (field.options && field.options.length > 0) {
field.options.forEach(function(option) {
var checkboxContainer = document.createElement('div');
checkboxContainer.className = 'checkbox-group';
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.name = field.apiName;
checkbox.value = option.value; // 使用 value
checkbox.id = field.apiName + '_' + option.value;
var checkboxLabel = document.createElement('label');
checkboxLabel.htmlFor = checkbox.id;
checkboxLabel.textContent = option.label;
checkboxContainer.appendChild(checkbox);
checkboxContainer.appendChild(checkboxLabel);
formGroup.appendChild(checkboxContainer);
});
}
} else if (field.type === 'number') {
inputElement = document.createElement('input');
inputElement.type = 'number';
inputElement.name = field.apiName;
inputElement.placeholder = field.helpText || field.label;
inputElement.required = field.isRequired;
formGroup.appendChild(inputElement);
} else if (field.type === 'date_time') {
inputElement = document.createElement('input');
inputElement.type = 'datetime-local';
inputElement.name = field.apiName;
inputElement.required = field.isRequired;
formGroup.appendChild(inputElement);
} else if (field.type === 'image' || field.type === 'files') {
// 图片或文件上传
inputElement = document.createElement('input');
inputElement.type = 'file';
inputElement.name = field.apiName;
inputElement.required = field.isRequired;
if (field.type === 'image') {
inputElement.accept = 'image/*';
}
formGroup.appendChild(inputElement);
}
// 添加帮助文本
if (field.helpText) {
var helpText = document.createElement('div');
helpText.className = 'help-text';
helpText.textContent = field.helpText;
formGroup.appendChild(helpText);
}
form.appendChild(formGroup);
});
// 添加提交按钮
var submitBtn = document.createElement('button');
submitBtn.type = 'submit';
submitBtn.className = 'submit-btn';
submitBtn.textContent = '提交';
form.appendChild(submitBtn);
// 绑定提交事件
form.addEventListener('submit', function(e) {
e.preventDefault();
submitForm();
});
}
// 发送验证码
function sendSMCode(phoneFieldName, btn) {
if (countdown > 0) return;
var phoneInput = document.querySelector('input[name="' + phoneFieldName + '"]');
var phone = phoneInput ? phoneInput.value : '';
if (!phone) {
alert('请输入手机号');
return;
}
FsYxt.FormSDK.sendSMCode({
formId: formId,
mobile: phone,
success: function() {
alert('验证码已发送');
countdown = 60;
var timer = setInterval(function() {
countdown--;
btn.textContent = countdown + 's';
btn.disabled = true;
if (countdown <= 0) {
clearInterval(timer);
btn.textContent = '发送验证码';
btn.disabled = false;
}
}, 1000);
},
fail: function(error) {
alert('发送失败: ' + error.errMsg);
}
});
}
// 提交表单
function submitForm() {
var form = document.getElementById('dynamicForm');
var formData = {};
var formElements = form.elements;
// 收集表单数据
for (var i = 0; i < formElements.length; i++) {
var element = formElements[i];
if (element.name && element.type !== 'button') {
if (element.type === 'checkbox') {
// 多选处理
if (!formData[element.name]) {
formData[element.name] = [];
}
if (element.checked) {
formData[element.name].push(element.value);
}
} else if (element.value) {
var value = element.value;
// 日期时间字段需要转换为时间戳(毫秒)
var field = formFields.find(function(f) {
return f.apiName === element.name;
});
if (field && field.type === 'date_time') {
// 将日期时间字符串转换为时间戳(毫秒)
value = new Date(value).getTime();
}
formData[element.name] = value;
}
}
}
// 验证必填字段
var missingFields = [];
formFields.forEach(function(field) {
if (field.isRequired) {
var value = formData[field.apiName];
if (!value || (Array.isArray(value) && value.length === 0)) {
missingFields.push(field.label);
}
}
});
if (missingFields.length > 0) {
alert('请填写必填字段: ' + missingFields.join('、'));
return;
}
// 提交表单
FsYxt.FormSDK.submitForm({
formId: formId,
sceneType: 0,
data: formData,
success: function(result) {
alert('提交成功!');
form.reset();
},
fail: function(error) {
alert('提交失败: ' + error.errMsg);
}
});
}
</script>
</body>
</html>场景四: React单页应用集成
import React, { useEffect } from 'react';
function App() {
useEffect(() => {
// 初始化SDK
if (window.FsYxt) {
window.FsYxt.configure({
ea: "88146",
websiteId: "8b28bec67afc42c281f078ff9bea7fec",
host: "crm.example.com",
enableSpaTracking: true // 启用SPA路由埋点
});
// 监听SDK就绪
window.FsYxt.on('onReady', () => {
console.log('FsYxt SDK Ready');
});
// 监听表单提交
window.FsYxt.on('onFormSubmit', (result) => {
if (result.status === 'ok') {
console.log('Form submitted successfully');
}
});
}
}, []);
const handleButtonClick = () => {
// 上报点击事件
window.FsYxt.track({
eventId: 'button_click',
attributesId: 'cta_button',
eventDescription: 'CTA按钮点击'
});
};
return (
<div className="App">
<h1>React应用示例</h1>
<button onClick={handleButtonClick}>立即咨询</button>
</div>
);
}
export default App;最佳实践
1. 初始化时机
- 建议在
window.onload或DOMContentLoaded事件后初始化 - 确保DOM元素已加载完成再调用相关方法
2. 错误处理
FsYxt.on('onError', function(error) {
// 记录错误日志
console.error('SDK Error:', error);
// 可选: 上报到监控系统
});3. 事件埋点规范
- 使用清晰的事件ID命名规则,如
page_view、button_click、form_submit - 事件描述应简洁明了,便于后续数据分析
- 关键业务节点务必添加埋点
4. 表单数据验证
function validateFormData(data) {
if (!data.name || !data.phone) {
alert('姓名和手机号不能为空');
return false;
}
if (!/^1[3-9]\d{9}$/.test(data.phone)) {
alert('手机号格式不正确');
return false;
}
return true;
}
// 使用
if (validateFormData(formData)) {
FsYxt.FormSDK.submitForm({ ... });
}5. 性能优化
- 避免频繁调用
track()方法,建议对高频事件进行节流或防抖处理 - 使用
createForm()时,合理设置 iframe 尺寸,避免影响页面性能
常见问题
Q1: SDK初始化失败怎么办?
A: 检查以下几点:
- 确认
ea、websiteId、host参数正确 - 检查网络连接是否正常
- 查看浏览器控制台是否有报错信息
- 监听
onError事件获取详细错误信息
Q2: 单页应用如何启用路由埋点?
A: 在
configure() 时设置 enableSpaTracking: true:FsYxt.configure({
ea: "88146",
websiteId: "xxx",
host: "crm.example.com",
enableSpaTracking: true // 关键配置
});Q3: 如何获取访客ID?
A: 使用
getConfig() 方法:var config = FsYxt.getConfig();
var visitorId = config.visitorId; // 或 config.fsWebsiteUidQ4: 表单提交失败如何排查?
A:
- 确认
formId是否正确 - 检查表单数据格式是否符合要求
- 查看
fail回调中的错误信息 - 确认SDK已成功初始化(监听
onReady事件)
Q5: 会员拦截功能不生效?
A:
- 确认元素类名设置正确
- 检查会员登录态是否已过期
- 确认后台会员配置是否正确
Q6: FsYxt报错 "FsYxt is not defined" 或方法调用失败?
A: 请确保SDK已完成加载后再执行相关方法:
// 推荐做法:在 window.onload 事件后执行
window.addEventListener('load', function() {
FsYxt.configure({
ea: "88146",
websiteId: "xxx",
host: "crm.example.com"
});
// 等待SDK初始化完成
FsYxt.on('onReady', function() {
// 在此处调用其他SDK方法
FsYxt.track({ ... });
});
});常见错误场景:
- ❌ 在SDK脚本加载前就调用
FsYxt对象 - ❌ 在
<head>中直接执行SDK方法 - ✅ 使用
window.onload或DOMContentLoaded事件 - ✅ 监听
onReady事件确保SDK初始化完成
技术支持
如有其他问题,请联系技术支持团队。
版本: v2.0
更新日期: 2025-12-05