一、写在前面:告别“吃灰数据”,让你的大屏“活”起来!

经过前两个实验的爆肝,咱们已经成功拿下了“浏览器市场分析大屏”的静态排版与数据打通,也搞定了“用户画像大屏”的多维绑定及筛选联动。但说句实在话,一块真正具备商业杀伤力的数据大屏,光有“颜值”是远远不够的,核心还得看“交互”——得让用户点一点、切一切,就能像剥洋葱一样挖出数据背后的业务洞察。

本次实操,我们将直接杀入 Max 蓝图编辑器的高阶玩法,手把手带你实现三大神级功能:

👉 Tab 无感切换:巧用图层显隐魔法,在同一个文件里搞定“市场分析”和“用户画像”两大视图的丝滑横跳。

👉 地理下钻联动:点哪查哪!点击地图上的任意省份,右侧的核心指标面板瞬间刷新为该省的专属数据。

👉 动态热力渲染:让地图拥有数据“感知力”,根据受众规模自动分配颜色深浅,受众分布一目了然。

敲黑板! 这波操作完全不需要你手撸复杂的后端 API,也不用写让人头秃的前端监听事件。全程只需在蓝图编辑器里像搭乐高一样拖拽节点、盘盘逻辑,企业级的高级交互大屏就能轻松拿捏。

二、今日核心目标与技能树点亮指南

2.1 我们要掌握哪些硬核本领?

能力雷达

具体技能点拆解

图层管理

玩转图层可见性控制,实现多屏内容的无缝切换

导航交互

驾驭 Tab 列表组件,精准控制页面模块的显隐状态

地理下钻

挖掘地图组件交互事件,搞定省份点击的数据联动

数据联动

打通神经末梢,让指标卡跟随地图点击动态刷新

热力渲染

吃透 adcode 映射原理,赋予地图热力层动态着色的能力

2.2 我们的“工作台”配置

  • 实验基地:助睿在线实验平台

  • 生产力工具:助睿 Max(一站式可视化开发利器)

  • 弹药库(数据源):团队私有数据库(MySQL)

  • 前置通关要求:完成实验6-1(静态排版)、实验6-2(数据注入)

2.3 最终火力展示 (效果预览)

跟着本篇教程走完,你的大屏将进化出以下超能力:

  • 顶部 Tab 导航一键切换“市场分析”与“用户画像”双线战场

  • 地图彻底激活,根据各省份的浏览器用户基数自动生成热力涂层

  • 点击任意省份,右侧四大核心指标卡秒级响应,实时展示地方战报

  • 切换浏览器筛选器时,全图热力层同步洗牌更新

三、上帝视角:全局交互架构拆解

3.1 系统交互全景图

3.2 两大核心交互链路

链路一:大屏切换交互(图层可见性控制)

Tab列表组件
    │
    ├── 点击"市场分析" (id=1) ──→ 分支判断(id==1)为真
    │                              ├── 设置图层:市场分析组 → 显示
    │                              └── 设置图层:用户画像组 → 隐藏
    │
    └── 点击"用户画像" (id=2) ──→ 分支判断(id==1)为假
                                   ├── 设置图层:市场分析组 → 隐藏
                                   └── 设置图层:用户画像组 → 显示

链路二:地图省份点击联动(地理下钻)

基础平面地图(区域热力层)
    │
    ├── 点击区域时事件 ──→ 提取地区名称(并行数据处理)
    │                        │
    │                        ├── 省份全称 → 简称映射
    │                        └── 存入全局变量 window.globalProvinceName
    │                             │
    │                             └──→ 省份核心指标查询(SQL请求)
    │                                      │
    │                                      ├── 读取 window.globalProvinceName
    │                                      ├── 读取 window.GLOBAL_SELECTED_BROWSER
    │                                      └── 执行动态SQL
    │                                           │
    │                                           └──→ 省份核心指标分发(并行数据处理)
    │                                                    │
    │                                                    ├── 分支1:总用户数 → 指标卡1
    │                                                    ├── 分支2:平均年龄 → 指标卡2
    │                                                    ├── 分支3:本科占比 → 指标卡3
    │                                                    └── 分支4:中高收入占比 → 指标卡4
    │
    └── 热力渲染链路(独立)
              │
              ├── 地理边界geojson加载完成 ──→ 提取adcode映射表
              │                                    │
              │                                    └── 存入 window.globalProvinceAdcode
              │                                         │
              └── 浏览器切换/页面加载 ──→ 各省份用户数查询(SQL)
                                                    │
                                                    └──→ 地图数据与用户数映射
                                                              │
                                                              └──→ 导入热力值数据接口

四、硬核揭秘:核心技术底层逻辑

4.1 图层显隐魔法:单文件搞定多屏切换

业务痛点:在真实的打工人日常中,一个数据复盘项目绝对不止一个页面(往往要兼顾市场动态、用户画像、运营大盘等)。如果傻乎乎地给每个视角都建一个独立的大屏文件,你将面临:

  • 修改噩梦:换个皮肤或者改个数据源,得挨个文件改到自闭。

  • 体验拉胯:跨页面跳转那刺眼的白屏,老板看了直摇头。

  • 状态割裂:好不容易选好的筛选条件,跳个页面全重置了。

Max 满分答案图层组打包 + 可见性动态控制,一个文件 Hold 住全场。

技术原理解析

  1. 把“市场分析”所有的零件打包成一个 Folder(比如命名为“市场分析组”)。

  2. 把“用户画像”所有的零件也打包成另一个 Folder(“用户画像组”)。

  3. 拦截 Tab 列表组件的点击事件,写点简单的判断逻辑,让这两个大组此消彼长地“显示/隐藏”。

方案降维打击对比

方案

维护文件数

用户切换体验

全局状态共享

后期维护成本

传统多页面

2个起步

极差(闪烁重载)

不支持

极高

图层切换法(本文)

1个

极致(秒切无感)

原生支持

极低

4.2 拿捏地图交互事件体系

Max 中的“基础平面地图”组件可以说是天生自带交互 Buff,核心事件一览:

事件触发器

触发时机

吐出的核心数据

当数据接口地理边界geojson数据加载完成时

地图骨架 (GeoJSON) 渲染完毕

包含各地理边界的 GeoJSON 对象

点击区域时

鼠标左键点击任意省份地块

包含所点击区域的 { name, adcode... }

鼠标移入区域触发

鼠标悬停在某省份上方

{ name, adcode... }

鼠标移出区域触发

鼠标离开某省份

{ name, adcode... }

我们这次要榨干的两个核心事件:

  • 点击区域时:当导火索,触发下钻逻辑,刷新指标卡数据。

  • 当地理边界加载完成时:做战前准备,提前把所有省份的 adcode 映射表拿出来,为后面的热力图渲染铺路。

4.3 省份名称大清洗:全称与简称的完美 Match

踩坑预警:地图组件丢给你的名字是带着后缀的完整官方称呼(如“江苏省”、“广西壮族自治区”),但这往往跟我们数据库里存的干净简称(如“江苏”、“广西”)是对不上的。直接拿去查 SQL?妥妥的查无此人。

解决方案:写个中间件清洗数据,建立一套全称到简称的双向映射机制。

// 核心城市的特殊待遇名单(直辖市、特别行政区、各大自治区)
const specialMap = {
    '北京市': '北京', '天津市': '天津', '上海市': '上海', '重庆市': '重庆',
    '广西壮族自治区': '广西', '内蒙古自治区': '内蒙古', 
    '西藏自治区': '西藏', '宁夏回族自治区': '宁夏', 
    '新疆维吾尔自治区': '新疆',
    '香港特别行政区': '香港', '澳门特别行政区': '澳门'
};

暴力美学与优雅并存的处理规则

  • 遇到 VIP(特殊映射表):直接 VIP 通道查表替换。

  • 遇到普通省份:上正则,强行扒掉词尾的“省”、“自治区”、“市”等外套。

  • 最终输出:数据库能无缝识别的极简省份名。

4.4 搞懂行政区划代码(adcode)映射

灵魂发问:adcode 是啥? 简单来说,它就是国家统计局给每个地块发的“身份证号”。Max 的地图组件必须靠这个身份证号,才知道要把颜色涂在哪个区域里。

为什么必须搞映射? 因为热力图层的数据接口非常“挑食”,它只认这种格式:

{
    "name": "江苏",
    "value": 1500,
    "area_id": 320000
}

这里的 area_id 其实就是 adcode!所以我们得把业务里的“省份名”翻译成“adcode”。

去哪找数据? 根本不用自己找,地图内置的 GeoJSON 里就已经藏好这份花名册了,趁它刚加载完,我们直接拦截提取就行。

五、保姆级实操:跟着步骤闭眼入

5.1 Step 1:打通大屏 Tab 导航奇脉

5.1.1 核心脑洞

Tab 列表其实是个高度可定制的 UI 组件。这里的骚操作是“隐身术”——把 Tab 列表的底色、高亮色、悬浮色统统调成 100% 透明,然后把它盖在咱们原本的顶部导航 UI 上。用户以为点的是导航按钮,其实点的是隐形的 Tab,这就是偷天换日!

5.1.2 搞定组件配置

(1)拉入 Tab 组件

  • 去组件库把“Tab 列表”拖出来,稳稳丢到顶部导航栏的位置。​​​​​​​

  • 微调尺寸,让它跟底下的“市场分析”、“用户画像”两个文字按钮严丝合缝地贴脸重合。​​​​​​​

(2)开启隐身模式(样式魔改)

  • 点开 Tab 列表的基础设置:

    • 改行数:1

    • 改列数:2 列​​​​​​​

  • 找到“标签默认配置”,对下面三兄弟下狠手,透明度全部拉到 0

    • 背景颜色 ➔ 0%

    • 选中背景色 ➔ 0%

    • 悬浮背景色 ➔ 0%

  • 现在,Tab 已经变身透明结界,虽然看不见,但随时准备拦截用户的点击。

(3)喂入干净数据 切到数据面板,精简成两条清爽的数据:

[
    { "id": 1, "content": "" },
    { "id": 2, "content": "" }
]

  • id:作为接下来路由分发的暗号。

  • content:留空即可(毕竟字长在底图上)。

弄完记得随手点一下刷新数据接口。

5.1.3 蓝图逻辑编排

(1)把演员请上蓝图画布 将以下组件从图层树里拖拽到蓝图编辑器:

  • “市场分析”图层组

  • “用户画像”图层组

  • 刚才做的“隐形 Tab 列表”​​​​​​​

(2)加入大脑(分支判断) 拖入一个“分支判断”节点,在代码区写下这句灵魂判词:

return data.id == 1;

逻辑极简:

  • 点左边按钮(市场分析) ➔ 输出 1 ➔ true ➔ 走满足分支。

  • 点右边按钮(用户画像) ➔ 输出 2 ➔ false ➔ 走不满足分支。

(3)接线:满足分支(亮出市场大屏) 在为 true 的出口,连上两个“设置图层可见性”节点:

  • 市场分析组 ➔ 动作切到:显示

  • 用户画像组 ➔ 动作切到:隐藏

(4)接线:不满足分支(亮出用户大屏) 在为 false 的出口,同样配两个动作:

  • 市场分析组 ➔ 动作切到:隐藏

  • 用户画像组 ➔ 动作切到:显示

(5)节点大串联

5.1.4 自查 checklist

  • 狂点“市场分析”,检查是不是这半边活了,另一半藏起来了

  • 狂点“用户画像”,检查是不是反过来了

  • 左右横跳,感受一下没有任何刷新白屏的丝滑感

5.2 Step 2:触发地图点击,召唤数据下钻

5.2.1 整体套路

用户猛击地图 ➔ 截获省份名 ➔ 去数据库捞这片地的数据 ➔ 拆分给四个指标卡。这就是经典的地理下钻分析 (Geo-drilldown) 套路。

5.2.2 核心节点配置与代码

(1)洗数据:提取地区名称(并行处理节点)

  • 目的:接住点击事件吐出的名字,过一遍清洗机,变成标准简称并存入全局变量。

  • 怎么做:拖一个“并行数据处理”节点,改名“提取地区名称”,贴入代码:

// 特权省市VIP通道
const specialMap = {
    '北京市': '北京', 
    '天津市': '天津', 
    '上海市': '上海', 
    '重庆市': '重庆',
    '广西壮族自治区': '广西', 
    '内蒙古自治区': '内蒙古', 
    '西藏自治区': '西藏',
    '宁夏回族自治区': '宁夏', 
    '新疆维吾尔自治区': '新疆',
    '香港特别行政区': '香港', 
    '澳门特别行政区': '澳门'
};
 
let provinceName = data.name;
 
// 优先VIP通道走一波
if (specialMap[provinceName]) {
    provinceName = specialMap[provinceName];
} else {
    // 没走通的,直接暴力脱掉后缀外套
    provinceName = provinceName.replace(/(省|自治区|市)$/, '');
}
 
// 注入全局,方便全场调用
window.globalProvinceName = provinceName;
 
return provinceName;

(2)向数据库开炮:查询核心指标(SQL 请求节点)

  • 目的:带上选中的省份和当前的浏览器参数,去库里拿四大指标。

  • 怎么做:加个“SQL请求”节点,改名“省份核心指标查询”,贴入代码:

const selectedProvince = window.globalProvinceName;
console.log("拦截到的点击省份:", selectedProvince);
const selectedBrowser = window.GLOBAL_SELECTED_BROWSER;
 
const sql = `
-- 战报1:地盘总人数
select 'total_users' as name, sum(user_count) as value
from labs.user_profile_stats
where browser_name = '${selectedBrowser}' 
  and province = '${selectedProvince}'
 
union all
 
-- 战报2:用户年龄盘点(加权平均)
select 'avg_age' as name,
       round(sum(age * user_count) / sum(user_count), 0) as value
from labs.user_profile_stats
where browser_name = '${selectedBrowser}' 
  and province = '${selectedProvince}'
 
union all
 
-- 战报3:高学历人群浓度
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 labs.user_profile_stats
where browser_name = '${selectedBrowser}' 
  and province = '${selectedProvince}'
 
union all
 
-- 战报4:高氪金玩家占比(中高收入)
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 labs.user_profile_stats
where browser_name = '${selectedBrowser}' 
  and province = '${selectedProvince}'
`;
 
console.log("拼好的查库SQL:", sql);
return sql;

高能解析:为什么用 UNION ALL 一把梭?因为四项指标长得一样(都是 name 和 value),一次性打捆发给数据库,大大降低请求延迟,贼快。

(3)精准发牌:数据分发(并行处理节点)

  • 目的:SQL 吐出来的是个四行数组,我们需要拆开塞给四个不同的指标卡。

  • 怎么做:搞四个“并行数据处理”节点,作为分发器。

发给“总用户数”:

var item = data.find(item => item.name === 'total_users');
return [{ value: item ? item.value : 0 }];

发给“平均年龄”:

var item = data.find(item => item.name === 'avg_age');
return [{ value: item ? item.value : 0 }];

发给“高学历占比”:

var item = data.find(item => item.name === 'high_edu_ratio');
return [{ value: item ? item.value : 0 }];

发给“中高收入占比”:

var item = data.find(item => item.name === 'high_income_ratio');
return [{ value: item ? item.value : 0 }];

5.2.3 蓝图连线

5.2.4 自查 checklist

  • 猛击江苏板块,看指标卡是不是切成了散装江苏的数据

  • 再点广东大本营,数据必须立刻刷新

  • 核对一下数据,确保和顶部的浏览器筛选器是联动的

  • 换个浏览器,重新点省份,数据有变化即算通关

5.3 Step 3:点亮全国地图热力层

5.3.1 怎么玩转热力图?

想让地图“按人头涂颜色”,分三步走:

  1. 偷出名册表:等地图骨架刚加载完,赶紧把所有省份的 adcode 字典存下来。

  2. 盘点各地兵力:根据筛选器,用 SQL 查出全国每个省到底有多少人。

  3. 两表一碰:把查出来的省份去匹配 adcode 字典,输出热力图引擎能吃的标准 JSON。

5.3.2 核心节点实操

(1)偷名册:提取 adcode(并行处理节点)

  • 触发机制:一定要连在区域热力层的“当数据接口地理边界geojson数据加载完成时”后面。

  • 怎么做:加个节点叫“提取adcode映射表”,代码如下:

/**
 * 从骨架里扒出 adcode 映射表
 */
function extractAdcodeAndName(data) {
    if (!data || !Array.isArray(data.features)) {
        console.error('地图数据格式不熟!');
        return {};
    }
    
    const nameToAdcode = {};
    
    // 遍历每一个地块,建立档案
    data.features.forEach(feature => {
        const props = feature.properties;
        if (props && props.adcode && props.name) {
            nameToAdcode[props.name] = props.adcode;
        }
    });
    
    return nameToAdcode;
}
 
const mapping = extractAdcodeAndName(data);
 
// 放进全局保险箱,等会儿渲染要用
window.globalProvinceAdcode = mapping;
 
console.log("全国adcode字典加载完毕,总计:", Object.keys(mapping).length);
 
return mapping;

(2)盘兵力:查询全盘数据(SQL请求节点)

  • 目的:一把抓出当前浏览器在全国各省的势力分布。

  • 怎么做:加节点“各省份用户数查询”:

const selectedBrowser = window.GLOBAL_SELECTED_BROWSER;
 
const sql = `
SELECT
    province AS name,
    SUM(user_count) AS value
FROM labs.user_profile_stats
WHERE browser_name = '${selectedBrowser}'
  AND province IS NOT NULL
  AND province != ''
GROUP BY province
ORDER BY value DESC
`;
 
console.log("热力图群嘲SQL生成:", sql);
return sql;

(3)数据缝合:映射与格式化(并行处理节点)

  • 目的:把刚才查出的数据,跟第一步存的 adcode 字典缝合。

  • 怎么做:加节点“地图数据与用户数映射”:

function convertToMapData(data) {
    if (!Array.isArray(data) || data.length === 0) {
        return [];
    }
 
    return data.map(item => {
        const provinceName = item.name;
        
        // Plan A:精准打击(直接匹配)
        let area_id = window.globalProvinceAdcode[provinceName];
 
        // Plan B:模糊搜索(剔除少数民族自治等长串后缀)
        if (!area_id) {
            const simplifiedName = provinceName.replace(
                /省|市|自治区|特别行政区|回族|壮族|维吾尔|藏族|苗族/g, 
                ''
            );
            
            for (const fullName in window.globalProvinceAdcode) {
                if (fullName.includes(simplifiedName)) {
                    area_id = window.globalProvinceAdcode[fullName];
                    break;
                }
            }
        }
 
        // Plan C:兜底策略(找不到就发配到公海)
        if (!area_id) {
            area_id = "000000";
        }
 
        return {
            name: provinceName,
            value: parseFloat(item.value) || 0,
            area_id: Number(area_id) // 这个就是大屏引擎认的身份证
        };
    });
}
 
const result = convertToMapData(data);
return result;

5.3.3 蓝图连线(热力渲染链路)

5.3.4 自查 checklist

  • 一刷新,祖国山河是不是已经五颜六色了?

  • 用户大省(北上广深浙)颜色是不是最深的?

  • 动一下浏览器筛选项,颜色分布是不是跟着跳舞了?

  • 就算有个别省没数据,也没有报红死机,而是优雅地灰掉。

5.4 Step 4:最终蓝图大一统

这几招练完,你的蓝图画布上应该躺着这三条优美的神经链路:

5.4.1 大屏切换链路

5.4.2 省份点击下钻链路

5.4.3 热力渲染链路

六、进阶课堂:关键技术的千层套路

6.1 玩转全局变量

咱们这次疯狂使用了 window 全局变量来做数据跨节点的搬运工:

变量大名

存了啥宝贝

谁写的

谁在用

window.GLOBAL_SELECTED_BROWSER

当前激活的浏览器

下拉框触发器

各大数据库查表节点

window.globalProvinceName

刚才点击的省份

地图点击洗数据节点

省份专属 SQL 查询节点

window.globalProvinceAdcode

adcode 通讯录

地理边界加载节点

热力图数据组装节点

老司机忠告

  • ✅ 一定要加上 GLOBAL_ 这类的前缀,避免和原生变量打架。

  • ✅ 生命周期要门清:谁负责初始化,谁负责覆写,理得越清 bug 越少。

6.2 分支节点的高端玩法

我们在切换大屏时用的是最朴素的 return data.id == 1;,其实它还能这么玩:

  • 数值卡点return data.value > 10000; (大户自动触发特殊动效)

  • 复合连招return data.id == 1 && data.role == 'admin'; (多条件组合)

  • 正则过滤return data.name.includes('大区'); (模糊匹配分流)

6.3 揭秘热力图底层引擎

  • 你以为的热力图:很复杂。

  • Max 的底层热力图:一套管道全自动处理。 数据喂进去 ➔ 匹配地块 ➔ 平台内置引擎自动算出数据最大最小值,分发深浅色阶 ➔ Canvas/SVG 原生渲染。完全不用你自己算 RGB,美滋滋。

七、避坑指南:高频报错与自救攻略

Q1:切大屏时,为啥画面重影了?

救火办法:你肯定没把那两个大组独立打包好,或者在“分支判断”后面,两根线的“显示/隐藏”动作写顺手写成一样的了。仔细核对动作配置!

Q2:疯狂点省份,指标卡就是装死?

救火办法

  1. 第一步,在“提取名称”节点加上 console.log(data),看看点击事件到底有没有触发。

  2. 第二步,查 window.globalProvinceName 是不是 undefined

  3. 第三步,把拼好的 SQL 复制到数据库直接跑一下,八成是字符串单引号拼漏了!

Q3:全国地图一片灰,完全不上色?

救火办法

  1. 八成是 adcode 字典没装好。按 F12 看看“提取adcode”节点报没报错。

  2. 检查最后拼出来的数据结构,一定要确认 area_id 字段塞的是数字 (Number)!如果你传了个字符串过去,地图引擎会直接摆烂的。

Q4:内蒙古、新疆这种名字死活匹配不上?

救火办法:正则大法也不是万能的。老老实实在代码开头的 specialMap 字典里给他们加个硬性 VIP 通道。

Q5:动了顶部筛选器,热力图不动?

救火办法:查你的蓝图触发源!确保“浏览器筛选器”的节点有一根线连到了“各省份用户数查询”节点上,没线连它怎么知道该刷新了?

八、干货总结与高阶玩法脑暴

8.1 今天咱们赚到了什么?

一套打通任督二脉的完整企业级交互闭环:

  • 图层可见性流派 ➔ 零代码实现单体巨石应用的无感切换。

  • 事件驱动流派 ➔ 点击捕获 + SQL 变量注入,实现了真正的“探索式”洞察。

  • 数据驱动流派 ➔ 吃透 adcode,搞懂前端渲染与数据的绑定美学。

8.2 给你点新灵感 (课后拓展)

掌握了这些,你完全可以自己去魔改:

  • 🚀 时空穿梭:加个时间轴组件,连上 SQL 的日期变量,做一个数据按月份随时间变幻的动态热力推演。

  • 🚀 套娃式下钻:配上城市级的 GeoJSON,点击省份后直接下钻到地级市。

  • 🚀 立体打击:点击地图,不但右边指标卡变,把底下的饼图、折线图全部联动刷新,全屏共鸣。

8.3 想让系统飞起来?(性能优化)

如果你数据量极大,建议给 browser_nameprovince 这俩字段加个数据库联合索引,速度能飙升 60%。或者把首屏用不到的图层做个懒加载,体验直接拉满。

8.4 大功告成,赶紧发朋友圈

点击右上角“发布”,打开分享开关,把生成的链接甩到群里,尽情享受大佬的注视吧!

九、CV工程师福利:完整代码速查表

9.1 省份名称清洗插件

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

9.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 labs.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 labs.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 labs.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 labs.user_profile_stats
where browser_name = '${selectedBrowser}' and province = '${selectedProvince}'
`;
return sql;

9.3 潜行偷取 adcode

function extractAdcodeAndName(data) {
    if (!data || !Array.isArray(data.features)) {
        console.error('无效的地图数据格式');
        return {};
    }
    const nameToAdcode = {};
    data.features.forEach(feature => {
        const props = feature.properties;
        if (props && props.adcode && props.name) {
            nameToAdcode[props.name] = props.adcode;
        }
    });
    return nameToAdcode;
}
const mapping = extractAdcodeAndName(data);
window.globalProvinceAdcode = mapping;
return mapping;

9.4 热力图数据终极缝合

function convertToMapData(data) {
    if (!Array.isArray(data) || data.length === 0) return [];
    
    return data.map(item => {
        const provinceName = item.name;
        let area_id = window.globalProvinceAdcode[provinceName];
        
        if (!area_id) {
            const simplifiedName = provinceName.replace(/省|市|自治区|特别行政区|回族|壮族|维吾尔|藏族|苗族/g, '');
            for (const fullName in window.globalProvinceAdcode) {
                if (fullName.includes(simplifiedName)) {
                    area_id = window.globalProvinceAdcode[fullName];
                    break;
                }
            }
        }
        
        if (!area_id) area_id = "000000";
        
        return {
            name: provinceName,
            value: parseFloat(item.value) || 0,
            area_id: Number(area_id)
        };
    });
}
return convertToMapData(data);

Logo

一站式 AI 云服务平台

更多推荐