ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

进销存可视化看板实践:从图表选型到交互设计的完整复盘

进销存可视化看板实践:从图表选型到交互设计的完整复盘 最近给朋友的一家商贸公司收拾后台他们仓库里管着上千个SKU采购、销售、库存全挤在Excel里。月初对账能把人对到怀疑人生老板要的“这个月卖得怎么样”没人能三句话讲清楚。所以新系统里我坚持加了一个“可视化看板”模块这也是我第一次正经做商品进销存系统的交互式图表。做完之后最大的感受是图表本身不难难的是让图表真的回答业务问题。这篇就当是初体验的复盘把需求拆解、图表选型、交互设计、接口联调还有踩过的坑一块儿捋一捋给准备做同类功能的兄弟一个参考。项目背景很简单采购入库有单据销售出库有单据库存表天天在变但老板看报表只看三个问题——库存压了多少钱、哪些商品卖不动、哪些商品该补货。原来的表格系统这些数都有就是看不出趋势和异常一个数字错了要翻三层表格才能定位。所以我这次的目标不是做漂亮的“大屏”而是做能交互、能下钻、能定位问题的经营看板把进销存数据变成一眼能看懂的东西。1. 看板功能规划与数据口径1.1 先弄清楚老板到底想看什么做可视化之前我以为难点在选图表库、调色、做动效真开工了才发现最花时间的居然是“确定指标口径”。说白了就是每个数字怎么算、取哪张表的数据、含不含税、退换货算不算进去。这些不在一开始定死后面每个图表都可能返工。我第一版直接奔着“酷炫”去做了十几个图表什么雷达图、热力图全往上堆。结果朋友看了一眼说“这一屏我看不懂你就告诉我库存超了没有。”所以第二版我做了减法把看板收敛成四个核心区域库存总览、销售趋势、品类结构、商品排行再配一个低库存和高库存的预警列表。这四块基本覆盖了进销存系统里最常被问的问题——仓库里有什么、卖得怎么样、什么好卖、什么该补。这里有个很实用的经验跟业务方确认口径的时候不要只问“销售额怎么算”要拿着商品明细问“七天无理由退货的订单算不算销售”“预售的订金算不算收入”。这些对照表做出来之后前后端开发、产品、业务各拿一份比口头对齐靠谱一百倍。1.2 图表模块怎么拆我当时把整个看板拆成两层第一层是“总览层”放销售总额、采购总额、库存总额、动销率这几个关键卡片再配两三个大趋势图第二层是“明细层”点总览上的任意一个卡片或者趋势图上的某一天能自动钻到对应的商品列表和单据列表。这个设计的好处是老板日常只需要看第一屏发现问题后再点进去定位不会一上来就被几百行的表格淹没。模块拆分还有一个隐藏好处图表之间的数据依赖被切断了。销售趋势图挂了不影响库存预警列表展示后端接口也有独立的缓存不会因为一个慢查询拖垮整个看板。我见过不少项目把所有的可视化数据塞到一个大接口里前端拿回来再自己拆一旦数据量上来首屏loading能转十秒体验非常差。2. 图表选型每个指标用对图2.1 销售趋势折线图配时间轴销售趋势我选了折线图这个基本没有争议。折线图天生适合展示连续时间维度上的变化走势、拐点、季节性一眼就能看出来。但有个细节要注意折线图横轴的时间粒度必须跟后端查询的粒度完全一致。前端显示“每日”“每周”“每月”后端接口就要支持对应的聚合维度否则切换粒度的时候前端拿到的还是原来的汇总数据图会直接画错。实现的时候我用的是ECharts的折线图xAxis的type可以根据时间粒度切换日粒度用category月粒度也用category只是后端返回的label不同。这里不要偷懒用time类型因为进销存数据不是均匀采样的周末可能没有销售time类型会把空日期也画成0值干扰判断。用category类型只显示实际有数据的时间点趋势看上去更真实。还有一点关于面积图如果销售数据波动不大可以给折线加一个透明渐变面积视觉上更有“量级感”但如果同一张图里要对比“销售额”和“订单数”两组数据就别用面积了叠加起来会糊成一团。我当时还加了一个平滑曲线结果被朋友吐槽“数据被美化了”后来改回折线让拐点锐利一些反而不容易误导人。2.2 品类结构环形图比饼图更好品类销售占比第一反应是饼图但实际做下来环形图更合适。环形图中间的空白区域可以放总销售额或者总占比信息密度更高涉及多个品类时环形图的色块面积也更直观不会像饼图那样挤在一起。这里有个容易踩的坑如果某个品类销售额占比低于某个小数值图例还是会在只是扇区小到看不见鼠标悬停也很难选中。我们后来在接口层就做了处理占比小于1%的品类统一合并成“其他”前端图例里只显示较大品类。这样既保留了整体分布又不会让图表变成“一坨细碎的色带”。配色方面我一开始直接用了ECharts默认色板好几个颜色肉眼区分度很差尤其是蓝色和绿色偏暗的时候。后来我换成了固定的业务配色销售用暖色系库存用冷色系预警用红色系。这个习惯延续到后面所有图表老板看久了颜色就知道什么类型的信息在变化比任何图例都管用。2.3 商品排行条形图比柱状图更顺手商品销售排行很多新手会默认用纵向柱状图但商品名称通常很长纵向柱状图的x轴标签会挤成一坨斜线根本看不清。我后来统一改成了横向条形图商品名称放在y轴销售额放在x轴标签空间大了不止一倍按金额排序后谁是销冠、谁是吊车尾一眼扫完。这里还要注意排序逻辑不要写反。我看过一些后台销售额从高到低排但图例顺序却从低到高导致“第一名”永远在图表底部非常反直觉。所以我在前端里定了一条规矩排行类图表一律按数值降序排列并且展示前10名后面加一个“查看全部”跳转到明细列表。进销存排行本质上是为了识别重点商品不是真的为了让你数完所有商品。Top N的场景里还有一种情况就是并列排名。有两个商品销售额完全一样时后端如果只按金额排序顺序会不稳定比如第一次A在B前面第二次B在A前面用户会以为图表“抽风”。我后来在排序字段里加了第二个条件——按商品编码升序保证并列时结果始终稳定。2.4 库存预警状态比图形更管用库存预警这部分我用的是表格加状态色没有强行上图表。很多可视化方案喜欢把库存预警画成仪表盘一个半圆表盘显示“库存健康度”说实话对实际业务帮助不大。仓库管理需要的是“哪些商品要补货”“哪些商品积压了”不是“整体健康指数”。我做的预警列表是这样的低于安全库存的商品标红高于最高库存限制的标橙正常区间的不显示或默认灰显。每一项都带“库存天数”这个指标——当前库存除以最近30天平均日销算出来的数字直接告诉老板这批货还够卖几天。这个比单纯看库存绝对值有用得多因为库存绝对值1000件到底多不多得看它每天卖多少。预警规则在系统里做成可配置的每种商品有自己的安全库存和最高库存后台可以按SKU单独调也可以按分类批量设置。这里前端要做的是只展示后端算好的“预警结果”不要在浏览器里自己判断否则每次规则变更都要改一次前端代码维护成本太高。3. 交互细节让图表会说话3.1 全局时间筛选器和粒度切换交互可视化跟纯报表最大的区别就是用户能主动改变视角。我在看板右上角放了一个全局时间筛选器支持“最近7天”“最近30天”“本季度”“本年”几个常用范围还放了一个粒度切换按钮日/周/月可以随时切。所有图表都监听这个筛选器的变化重新拉数据后刷新。这里的实现思路是把筛选条件放进一个统一的状态对象里比如{ dateRange: [2024-01-01, 2024-01-31], granularity: month }任何一个图表组件只关心这个状态不关心筛选器UI在哪里。Vue里我用了一个简单的provide/injectReact里可以用Context本质上就是让图表订阅同一个“数据源状态”。一开始我犯过一个错把时间筛选写死在某一个图表组件内部结果其他图表没法联动更新。后来不得不重构把所有筛选条件提升到看板顶层再往下传。这个教训值一次返工。3.2 点击联动从趋势图钻到明细交互可视化的另一个重点是“钻取”。我实现的联动是销售趋势图上用户点击某个月的柱子下方商品排行就自动切换成“该月销冠排行”库存预警列表也切换成“该月动销数据”。这个功能在老板问“上个月到底什么卖得好”的时候特别有用不用再手动拉一堆筛选条件。ECharts的点击事件是通过chart.on(click, params {})拿到的params里面包含横轴时间点、系列名、数值。拿到时间点后我把它拼进查询参数重新调后端接口。这里要给接口预留startDate和endDate参数前端只需要改这两个值。联动还有一个细节点击之后要给用户一个明确的“当前状态”反馈比如被点击的柱子高亮其他柱子变淡筛选条件同步显示在页面上。否则用户点了一下没反应或者数据变了但不知道为什么变会觉得系统不稳定。3.3 tooltip和横轴标签的打磨进销存系统里图表的tooltip不是鼠标悬停显示数值那么简单。我复写了绝大部分tooltip的formatter把单位、符号、关键计算逻辑都塞进去。比如库存总览的tooltip除了显示“库存金额”还会加一行“较上周变动12.3%”销售趋势图的tooltip则显示“订单数”和“客单价”让看的人不用来回对比就能拿到完整信息。横轴标签也值得花时间。日粒度展示时如果横轴跨了90天默认每个点都显示日期标签会重叠。我做了按刻度跳显只显示每7天的标签其余留空切换成月粒度时标签显示“2024-01”“2024-02”这种短格式。这些看起来是细节实际体验差别非常大尤其是给老板演示的时候标签糊成一团很尴尬。y轴的单位格式也要统一。金额类数值超过1万我显示成“1.2万”超过1亿显示“1.05亿”用格式化函数统一处理不能有的图上万有的图直接显示一长串数字。后端返回的是分还是元在接口文档里写死前端格式化之前先除以100避免因为单位不一致导致图表数字偏差。3.4 空数据和加载态不画错的图比画好看的图重要做进销存可视化空数据是家常便饭。新店刚开业还没有销售记录某个月份没有任何采购出入库筛选时间段内完全没有数据……如果接口返回空数组ECharts默认会画一个空坐标系横纵轴还在但没有任何内容。更坑的是有些图表会默认显示一条数值为0的线让老板误以为销量暴跌到零。我的处理方案是前端在拿到空数组后不用ECharts渲染而是直接渲染一个“暂无数据”的占位图同时把横纵轴隐藏。这样至少不会给出错误的视觉暗示。另外如果图表是“部分缺失”比如有的月份有数据有的没有后端在聚合时就要把缺失月份补成null而不是0前端遇到null直接断线不要连到点不要当成0值。加载态也不能忽视。每个图表从点击筛选到数据回来可能需要几百毫秒。我做了统一的loading效果每个图表组件在请求期间显示一个轻量的转圈数据到齐后再整体渲染。一开始图省事只在页面顶部加一个全局loading结果用户改了筛选之后整个页面白屏体验很差。4. 前后端联调的关键环节4.1 后端聚合接口怎么设计才能支撑图表图表数据和明细数据不一样图表要的是“聚合结果”不是列表。后端如果直接把明细表吐给前端让前端用JS做sum、groupBy数据量小的时候没感觉数据量大了浏览器直接卡死。所以凡是图表要用的数我都让后端在SQL里聚合完前端只负责展示。我设计了几个专用接口销售趋势接口按时间粒度返回汇总、品类占比接口返回分类汇总、商品排行接口返回Top N、库存总览接口返回库存金额和预警数量。每个接口的返回结构尽量扁平比如销售趋势就长这样{ code: 0, data: [ { date: 2024-01-01, salesAmount: 12345.00, orderCount: 86 }, { date: 2024-01-02, salesAmount: 9876.50, orderCount: 71 } ] }接口名是dashboard.salesTrend接收startDate、endDate、granularity、storeId四个参数。storeId是为了后续多门店扩展预留的这次单门店用默认值但字段先留着省得后期改接口。4.2 前端拿到数据后的二次加工后端聚合得很好前端依然要做一些必要的加工。第一是格式化金额、数量、百分比都要统一格式这个前面已经提过。第二是排序后端在SQL里已经排好序但前端图表组件有时会默认按照系列名排序我需要强制按数据顺序映射。第三是计算派生指标。比如动销率、库存天数、环比变化这些可以在后端算也可以在前端算。我的原则是需要业务规则判断的比如库存预警等级必须在后端算纯展示类的比如环比百分比可以在前端算。因为预警等级和业务规则绑定后端改规则前端不用动而环比只是(本月 - 上月) / 上月这种数学运算前端算省一次请求。这里要注意除法坑环比基数是0的时候百分数会变成infinity。我在前端对这种情况做了兜底基数为0时展示“新增”而不是“infinity%”。还有金额精度JS用浮点数算钱容易出精度问题我统一用“分”做整数运算展示时才转成元这样不会出现“0.1 0.2 0.30000000000000004”。4.3 刷新策略和数据缓存怎么做合理进销存系统的看板数据不需要跟交易事务保持实时同步。我设置了两个刷新机制进入看板页面时拉一次最新数据然后每5分钟自动轮询一次。5分钟这个值我是照抄了后台运营的经验——太频繁后端扛不住太慢了老板看到的“今日销售额”会显得不新鲜。轮询还有一个好处自动纠正其他操作导致的数据偏差。比如有人在前台开了一张销售单库存减了看板5分钟内会自动更新。我用的是setInterval配合页面可见性监听tab切到后台时就暂停轮询切回来再立刻刷新省资源又能保证数据相对新鲜。缓存方面后端给图表接口加了Redis缓存key按时间范围粒度storeId拼接过期时间设为60秒。这样同一个图表快速切换筛选条件时接口可以命缓存的不会每次都打MySQL。当然缓存时间不能太长否则用户改了商品资料之后看板迟迟不变容易误以为系统出问题了。5. 常见问题与排查技巧5.1 ECharts容器大小和resize的坑做可视化图表一定会遇到的经典问题图表在页面加载时容器宽度是0或者容器被折叠/隐藏初始化后图表就渲染不出来或宽度不对。我遇到的场景是看板Tab切换从“库存”Tab切到“销售”Tab时销售图经常显示成一行细线。原因是ECharts在init的时候读取了容器尺寸容器当时是隐藏的读取到0后面就算容器显示了图表也不会自动重绘。解决方案是在Tab切换完成、容器真正显示之后调用chart.resize()如果容器是动态宽度的还要配合window.resize事件做监听。更稳妥的做法是只在容器可见时初始化图表而不是在组件mounted时无脑init。我现在封装图表组件时多了一个visibleprop容器不可见时直接返回一个占位div不做init。5.2 图例和数据的颜色对不上有一次用户反馈说品类占比图里“饮料”是红色但同一个商品在商品排行图里“饮料”又变成了绿色。原因是图表库的默认色板是自动分配的每次渲染都按照数据顺序重新分配颜色同一个品类在不同图表里颜色不一致。解决这个问题我用了一个统一色板映射后端返回品类时带一个categoryCode前端维护一张品类编码 - 固定颜色的映射表不在表里的用默认灰色。这样同一个品类在任何图表里都是同一个颜色老板看习惯之后甚至不需要看图例看到红色就知道是饮料区。分类不多的时候这个方法很简单分类很多就要做成配置化了。5.3 指标口径对不齐跟财务对不上账这是进销存系统做可视化最容易炸雷的地方。销售金额到底是含税还是不含税退货单是冲减销售额还是单独一列采购入库的成本价是加权平均还是先进先出我第一版用“含税销售额”财务那边报表示口径是“不含税”结果老板拿着我的看板和财务报表对比差了十几个点差点以为系统出了bug。排查过程很曲折最后发现就是口径问题。解决方案是把口径做成系统级配置配置项写清楚每个指标的计算规则而且图表标题旁边加了一个信息图标鼠标悬停显示“销售金额已支付订单金额合计不含税费”。这样业务方自己就能看懂不用每次找开发对口径。5.4 loading过多导致页面卡顿看板页面一般至少五六个图表如果每个图表独立发请求、独立渲染首屏会同时发起六七个请求接口慢的时候浏览器并发连接数打满后面的请求全部排队页面就会卡顿。我发现这个问题是在一个网络较慢的环境下实测加载时间居然到了8秒。后来我做了两件事一是给请求分组首屏先拉“库存总览”和“销售趋势”两个核心图表其他图表等这两个返回后再拉让用户最先看到最重要的内容二是给每次请求做一个简单的防抖合并用户快速切换筛选条件时只取最后一次条件发起请求避免疯狂刷新图表。做了这两步之后页面加载从8秒降到3秒左右体验好了非常多。5.5 避坑速查表问题现象原因解决折线图横轴日期错乱周末显示为0time类型轴自动补点改用category轴只显示实际数据日期图例颜色不稳定同一品类颜色不同色板自动分配建立品类颜色映射表图表在Tab切换后变窄容器隐藏时init图表生成时容器宽度为0Tab切换后再init并监听resize金额精度异常0.30000000000004浮点运算用“分”做整数计算展示时转元环比显示infinity上月基数为0JS除零判断基数为0时显示“新增”数据为空仍画线无数据时显示0值空数组未处理返回null前端断线并展示空态Top N顺序不稳定并列排名顺序跳变仅按金额排序追加第二个排序字段这条速查表我打印了一份贴在公司白板上后面其他同事做报表模块也照着查省了不少沟通成本。很多可视化的问题看起来是“显示不对”根子其实在数据层和生命周期管理不是图表库本身的问题。做这套商品进销存的交互可视化图表我最大的体会是图表库只是最后一道工序真正的功夫在需求拆解、数据口径、接口设计和交互细节上。“初体验”就是踩坑的过程这个标题起得很实在。如果你正准备给自己的系统加可视化看板建议先拿一张纸把老板最常问的五个问题写下来再对应到图表不要从图表库的示例开始倒推功能。数据能回答生意上的问题图表才有存在的意义。
返回列表