浏览器用户画像大屏搭建:从静态布局到交互联动(附完整代码)

📅 2026/7/5 15:18:13 👁️ 阅读次数 📝 编程学习
浏览器用户画像大屏搭建:从静态布局到交互联动(附完整代码)

本文为 Uniplore 「浏览器用户画像分析」实验系列全流程指南,覆盖静态布局制作、数据接入、交互联动三大核心模块,包含可直接复用的 SQL、蓝图节点代码与避坑技巧,新手也能零代码复刻企业级数据大屏。

一、实验背景与目标

本系列实验基于user_profile_stats用户画像统计表,完成从大屏静态设计到动态交互的全链路开发,最终实现三大核心能力:

  1. 零代码搭建浏览器用户画像分析大屏的完整布局
  2. 通过蓝图编辑器实现筛选器与全图表的联动数据刷新
  3. 配置大屏切换、地图下钻交互,完成企业级数据可视化开发

适用场景:数据分析教学实验、企业用户画像看板、数据可视化学习练手

以下为本次实验所使用平台的基础信息:

  • 平台全称:助睿数智(Uniplore)一站式数据科学实验平台
  • 平台定位:覆盖数据接入、ETL 处理、机器学习建模到可视化分析的全链路 Agentic 零代码数据智能平台

二、实验 1:大屏静态布局制作(组件搭建)

(一)核心设计框架

先明确大屏的业务叙事逻辑,再按模块拆分组件,避免盲目拖拽布局:

模块

子模块

推荐组件

核心作用

数据概览

核心指标(用户数 / 平均年龄 / 本科占比 / 中高收入占比)

数字翻牌器

一眼传递用户规模与质量

地域分布

省份热力地图 + TOP5 排行榜

基础平面地图 + 轮播列表

展示用户地理分布与热点区域

人口属性

性别 / 居住地类型

基础饼图 / 轮播饼图

展示用户结构占比

人口属性

年龄 / 职业 / 收入分布

基础柱状图 / 水平柱状图

对比不同类别的用户数量

人口属性

学历分布

水平基础柱图

适配长类别名称的对比展示

交互控件

浏览器筛选器

下拉多选组件

实现多浏览器数据切换

(二)快速搭建步骤(避坑版)

步骤 1:图层分组与复用

将上一实验的「市场分析大屏」组件整体复制、重命名为市场分析组并隐藏,新建用户画像组用于本次实验,两个大屏可通过图层可见性切换,避免组件重叠干扰。

步骤 2:组件与背景配置(附素材)

按参考图依次添加组件,统一设置背景样式,推荐素材链接如下:

素材

页面背景

图表区域背景

指标标题背景

排行榜背景

步骤 3:关键组件设置技巧

  • 基础平面地图:添加「区域热力层」子组件,开启省份边界高亮,后续将通过数据动态渲染热力颜色
  • 轮播列表(TOP5 省份):设置交替行背景色、行高与字体样式,提升可读性
  • 饼图组件:性别分布用基础饼图,居住地类型用轮播饼图,增强大屏动态效果
  • 下拉多选筛选器:设置 6 个浏览器选项,默认选中第一个浏览器,开启可清空 / 全选功能

三、实验 2:数据接入与筛选器联动(蓝图配置)

(一)核心数据流逻辑

页面加载 / 筛选器选择 → 触发参数接收 → 维度数据 SQL + 核心指标 SQL 并行查询 → 数据分发到各图表组件,实现一次查询、全图表联动刷新。

(二)前置准备:表结构优化

为准确计算平均年龄,先修改user_profile_stats表结构,添加age字段:

DROP TABLE IF EXISTS `user_profile_stats`; CREATE TABLE `user_profile_stats` ( `browser_name` VARCHAR(50) NOT NULL COMMENT '浏览器名称', `gender` VARCHAR(10) COMMENT '性别', `age` INT NOT NULL COMMENT '年龄', `age_group` VARCHAR(10) COMMENT '年龄段', `edu` VARCHAR(50) COMMENT '学历', `job` VARCHAR(50) COMMENT '职业', `income` VARCHAR(50) COMMENT '收入', `city_type` VARCHAR(10) COMMENT '居住地类型', `province` VARCHAR(50) COMMENT '省份', `user_count` INT NOT NULL COMMENT '用户数' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户画像统计表';

修改「用户画像表加工」转换流,在分组组件中保留age字段并重新执行。

(三)蓝图节点配置(完整代码)

节点 1:浏览器参数接收(并行数据处理)

接收筛选器选中的浏览器,存入全局变量,供后续 SQL 调用:

节点 2:维度数据 SQL 请求(一次查询全维度数据)

UNION ALL合并所有人口属性维度数据,统一输出格式:

const selectedBrowser = window.GLOBAL_SELECTED_BROWSER; let sql = ` -- 性别分布 select 'gender' as dimension_type, gender as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' group by gender union all -- 年龄段分布 select 'age' as dimension_type, age_group as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' group by age_group union all -- 学历分布 select 'edu' as dimension_type, edu as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' group by edu union all -- 职业分布 select 'job' as dimension_type, job as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' group by job union all -- 收入分布 select 'income' as dimension_type, income as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' group by income union all -- 居住地类型分布 select 'city_type' as dimension_type, city_type as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' group by city_type union all -- 省份分布 select 'province' as dimension_type, province as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' group by province `; return sql;

节点 3:维度数据分发(并行数据处理)

dimension_type过滤数据,分发给对应图表组件,以下为关键分支代码:

// 分支1:性别分布(饼图) var filtered = data.filter(item => item.dimension_type === 'gender'); return filtered.map(item => ({ name: item.name, value: item.value })); // 分支2:年龄段分布(柱状图,按顺序排序) var filtered = data.filter(item => item.dimension_type === 'age'); var order = ['<18', '18-25', '26-35', '36-45', '>45']; filtered.sort((a, b) => order.indexOf(a.name) - order.indexOf(b.name)); return filtered.map(item => ({ x: item.name, y: item.value, s: '用户数' })); // 分支3:学历分布(条形图) var filtered = data.filter(item => item.dimension_type === 'edu'); var order = ['小学及以下', '初中', '高中/中专/技校', '大专', '大学本科', '硕士及以上']; filtered.sort((a, b) => order.indexOf(a.name) - order.indexOf(b.name)); return filtered.map(item => ({ x: item.name, y: item.value, s: '学历'})); // 分支4:职业分布(柱状图) var filtered = data.filter(item => item.dimension_type === 'job'); return filtered.map(item => ({ x: item.name, y: item.value, s: '职业'})); // 分支5:收入分布(柱状图) var filtered = data.filter(item => item.dimension_type === 'income'); // 按收入金额升序排序(提取数字进行比较) filtered.sort((a, b) => { // 提取收入段中的最小金额 var getMinIncome = (incomeStr) => { // 处理 "无收入"、"500元及以下" 等特殊情况 if (incomeStr === '无收入') return -1; if (incomeStr === '500元及以下') return 0; // 提取数字,如 "1501~2000元" 提取 1501 var match = incomeStr.match(/(\d+)/); return match ? parseInt(match[1]) : 999999;}; return getMinIncome(a.name) - getMinIncome(b.name);}); return filtered.map(item => ({ x: item.name, y: item.value, s: '收入'})); // 分支6:居住地类型分布(饼图) var filtered = data.filter(item => item.dimension_type === 'city_type'); return filtered.map(item => ({ name: item.name === 'unknown' ? '未知' : item.name, value: item.value})); // 分支7:省份TOP5排行榜(轮播列表) var filtered = data.filter(item => item.dimension_type === 'province'); filtered.sort((a, b) => b.value - a.value); var top5 = filtered.slice(0, 5); return top5.map(item => ({ province: item.name, user_count: item.value }));

节点 4:核心指标 SQL 请求与分发

查询四个核心指标并拆分分发到对应指标卡:

// SQL请求节点代码 const selectedBrowser = window.GLOBAL_SELECTED_BROWSER; let sql = ` select 'total_users' as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' union all select 'avg_age' as name, round(sum(age * user_count) / sum(user_count), 1) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' union all select 'high_edu_ratio' as name, round(sum(case when edu in ('本科', '硕士及以上') then user_count else 0 end) * 100.0 / sum(user_count), 1) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' union all select 'high_income_ratio' as name, round(sum(case when income in ('5001~8000元', '8001~12000元','12000元以上') then user_count else 0 end) * 100.0 / sum(user_count), 1) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' `; return sql; // 分发节点分支示例(总用户数) var item = data.find(item => item.name === 'total_users');

四、实验 3:大屏交互配置(切换 + 地图下钻)

(一)大屏切换功能实现

通过 Tab 列表组件控制两个大屏的图层可见性,实现无刷新切换:

  1. 添加 Tab 列表组件,设置 1 行 2 列,id分别为 1(市场分析)和 2(用户画像),背景透明度设为 0,与导航按钮重合
  2. 导出 Tab 列表、两个图层组到蓝图编辑器,添加分支判断节点:return data.id == 1;
  3. 满足分支:显示「市场分析组」,隐藏「用户画像组」;不满足分支则相反

(二)地图省份点击联动(核心交互)

点击地图省份时,右侧核心指标卡自动刷新为该省份数据,实现地理下钻分析:

步骤 1:省份名称映射(并行数据处理)

处理地图返回的全称与数据表简称的匹配问题:

const specialMap = { '北京市': '北京', '天津市': '天津', '上海市': '上海', '重庆市': '重庆', '广西壮族自治区': '广西', '内蒙古自治区': '内蒙古', '西藏自治区': '西藏', '宁夏回族自治区': '宁夏', '新疆维吾尔自治区': '新疆', '香港特别行政区': '香港', '澳门特别行政区': '澳门' }; let provinceName = data.name; if (specialMap[provinceName]) { provinceName = specialMap[provinceName]; } else { provinceName = provinceName.replace(/(省|自治区|市)$/, ''); } window.globalProvinceName = provinceName; return provinceName;

步骤 2:省份核心指标 SQL 查询

根据选中的浏览器和省份,查询该省份的核心指标:

const selectedProvince = window.globalProvinceName; const selectedBrowser = window.GLOBAL_SELECTED_BROWSER; const sql = ` select 'total_users' as name, sum(user_count) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' and province = '${selectedProvince}' union all select 'avg_age' as name, round(sum(age * user_count) / sum(user_count), 0) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' and province = '${selectedProvince}' union all select 'high_edu_ratio' as name, round(sum(case when edu in ('本科', '硕士及以上') then user_count else 0 end) * 100.0 / sum(user_count), 2) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' and province = '${selectedProvince}' union all select 'high_income_ratio' as name, round(sum(case when income in ('5001~8000元', '8001~12000元','12000元以上') then user_count else 0 end) * 100.0 / sum(user_count), 2) as value from cs_group_8.user_profile_stats where browser_name = '${selectedBrowser}' and province = '${selectedProvince}' `; return sql;

步骤 3:地图热力层动态渲染

根据用户数自动调整省份颜色深浅,实现热力效果:

  • 添加「提取 adcode 映射表」节点,从地图 GeoJSON 数据中提取省份与 adcode 的映射关系

  • 配置「各省份用户数查询」SQL 节点,统计当前浏览器下各省份用户数

  • 添加「地图数据映射」节点,将查询结果转换为热力层所需格式:
    function convertToMapData(data) { return data.map(item => { const provinceName = item.name; let area_id = globalProvinceAdcode[provinceName]; if (!area_id) { const simplifiedName = provinceName.replace(/省|市|自治区/g, ''); for (const fullName in globalProvinceAdcode) { if (fullName.includes(simplifiedName)) { area_id = globalProvinceAdcode[fullName]; break; } } } return { name: provinceName, value: parseFloat(item.value) || 0, area_id: Number(area_id) }; }); } return convertToMapData(data);

  • 将映射结果导入「区域热力层」的「导入热力值数据接口」

五、常见问题与避坑指南

  1. 筛选器不联动:检查参数接收节点是否正确存入全局变量,SQL 语句中是否正确引用window.GLOBAL_SELECTED_BROWSER,节点连线是否为「执行成功」→「执行 SQL」
  2. 地图省份不匹配:优先检查省份名称映射代码,确保数据表中的省份简称与地图返回的全称能正确转换
  3. 热力层颜色不更新:确认 adcode 映射表已正确加载,SQL 查询结果包含所有省份,且数据映射节点输出格式为{name, value, area_id}
  4. 指标卡数据为 0:检查 SQL 查询是否按浏览器和省份过滤,核心指标分发节点是否按name字段正确过滤数据

六、实验总结

通过本系列实验,我们掌握了助睿 Max 数据大屏的完整开发流程:从静态组件布局、蓝图编辑器数据接入,到多场景交互配置,实现了浏览器用户画像分析大屏的从 0 到 1 搭建。这种低代码可视化开发方式,大幅降低了企业级数据大屏的开发门槛,适合数据分析、产品运营、前端开发等多场景使用。

#助睿数智 #Uniplore #数据大屏