双十一销量实时统计图表

cybozu发表于:2019年10月29日 17:11:14更新于:2019年10月30日 17:04:45

Index

前言

echarts 是apache的一个孵化项目,这次我们将它和kintone进行整合,实现了kintone门户页面的双十一的销量实时统计的Dashboard 。

我们先看下效果图。

0015db7d84dd9dbb4d488351a9a9401

折线图显示了双十一期间的产品销量走势,而饼图则显示了各渠道的产品销量的占比,同时他们都是实时变化的。

接下来我们就来看下它是怎么实现的。

公用的库

下面是我们要用到的库:

echarts

kintone JS SDK

※ 这里不对这两个库做具体介绍,如果还不熟悉它们,请先参阅相关文档。

应用的自定义开发

我们先来模拟一个kintone的销量统计的应用。

Stp1: 创建新应用

选择“通过导入模板文件创建”。

0015db7f1e9ae69a9c8496e8b84fcb7

Step2: 通过导入模板文件创建

选择模板压缩文件后,点击创建应用,便创建完成了。

(模板下载地址:双十一销量统计模板

0015db7f35581b41562e21ff0a20b39

Step3: 创建JavaScript文件

graph.js

(应用列表页和应用详情页显示绘图的js,为了方便这边将pc端和mobile端的代码整合到了一起。)

(function () {
    'use strict';
    kintone.events.on(['app.record.detail.show', 'app.record.edit.show'], function (res) {
        const pcSetting = {
            type: 'pc',
            showContent: true,
            style: "width: 600px;height:400px;",
        };
        generateDetail(pcSetting, res);
    });
    kintone.events.on(['mobile.app.record.detail.show', 'mobile.app.record.edit.show'], function (res) {
        const mobileSetting = {
            type: 'mobile',
            showContent: false,
            style: "width: 350px;height:400px;",
        };
        generateDetail(mobileSetting, res);
    });
    kintone.events.on(['app.record.index.show'], function (res) {
        const pcSetting = {
            type: 'pc',
            showContent: true,
            style: "width: 900px;height:400px;"
        };
        generateTotal(pcSetting);
    });
    kintone.events.on(['mobile.app.record.index.show'], function (res) {
        const mobileSetting = {
            type: 'mobile',
            showContent: false,
            style: "width: 350px;height:400px;"
        };
        generateTotal(mobileSetting);
    });
    function generateDetail(setting, res) {
        var record = res.record;
        var report_el;
        if (setting.type === "mobile") {
            report_el = kintone.mobile.app.record.getSpaceElement("report");
        }
        else {
            report_el = kintone.app.record.getSpaceElement("report");
        }
        var report_div = document.createElement('div');
        report_div.id = "graph";
        report_div.style = setting.style;
        var myChart = echarts.init(report_div);
        var option = {
            title: {
                text: '各渠道销量统计',
                x: 'center'
            },
            tooltip: {
                trigger: 'item',
                formatter: "{a} <br/>{b} : {c} ({d}%)",
                showContent: setting.showContent
            },
            legend: {
                orient: 'vertical',
                left: 'left',
                data: ['京东', '淘宝', '拼多多', '天猫', '考拉']
            },
            series: [
                {
                    name: '假期类型',
                    type: 'pie',
                    radius: '55%',
                    center: ['50%', '60%'],
                    data: [
                        { value: record.channel1.value, name: '京东' },
                        { value: record.channel2.value, name: '淘宝' },
                        { value: record.channel3.value, name: '拼多多' },
                        { value: record.channel4.value, name: '天猫' },
                        { value: record.channel5.value, name: '考拉' }
                    ],
                    itemStyle: {
                        emphasis: {
                            shadowBlur: 10,
                            shadowOffsetX: 0,
                            shadowColor: 'rgba(0, 0, 0, 0.5)'
                        }
                    }
                }
            ]
        };
        myChart.setOption(option);
        report_el.appendChild(report_div);
    }
    function generateTotal(setting) {
        if (document.getElementById('graph') !== null) {
            return;
        }
        var graph = document.createElement('div');
        graph.id = 'graph';
        graph.style = setting.style;
        var app;
        if (setting.type === "mobile") {
            kintone.mobile.app.getHeaderSpaceElement().appendChild(graph);
            app = kintone.mobile.app.getId();
        }
        else {
            kintone.app.getHeaderSpaceElement().appendChild(graph);
            app = kintone.app.getId();
        }
        var myChart = echarts.init(graph);
        var kintoneRecord = new kintoneJSSDK.Record();
        var rcOption = {
            app: app,
            query: 'order by date asc'
        };
        kintoneRecord.getAllRecordsByCursor(rcOption).then((rsp) => {
            var records = rsp.records;
            var graphData = { 'channel1': [], 'channel2': [], 'channel3': [], 'channel4': [], 'channel5': [] };
            var dateArray = [];
            for (var record of records) {
                var dateKey = record.date.value;
                graphData.channel1.push(record.channel1.value);
                graphData.channel2.push(record.channel2.value);
                graphData.channel3.push(record.channel3.value);
                graphData.channel4.push(record.channel4.value);
                graphData.channel5.push(record.channel5.value);
                dateArray.push(dateKey);
            }
            var option = {
                tooltip: {
                    trigger: 'axis',
                    axisPointer: {
                        type: 'shadow'
                    },
                    showContent: setting.showContent
                },
                legend: {
                    data: ['京东', '淘宝', '拼多多', '天猫', '考拉']
                },
                grid: {
                    left: '3%',
                    right: '4%',
                    bottom: '3%',
                    containLabel: true
                },
                xAxis: {
                    type: 'value'
                },
                yAxis: {
                    type: 'category',
                    data: dateArray
                },
                series: [
                    {
                        name: '京东',
                        type: 'bar',
                        stack: '总量',
                        label: {
                            normal: {
                                show: true,
                                position: 'insideRight'
                            }
                        },
                        data: graphData.channel1
                    },
                    {
                        name: '淘宝',
                        type: 'bar',
                        stack: '总量',
                        label: {
                            normal: {
                                show: true,
                                position: 'insideRight'
                            }
                        },
                        data: graphData.channel2
                    },
                    {
                        name: '拼多多',
                        type: 'bar',
                        stack: '总量',
                        label: {
                            normal: {
                                show: true,
                                position: 'insideRight'
                            }
                        },
                        data: graphData.channel3
                    },
                    {
                        name: '天猫',
                        type: 'bar',
                        stack: '总量',
                        label: {
                            normal: {
                                show: true,
                                position: 'insideRight'
                            }
                        },
                        data: graphData.channel4
                    },
                    {
                        name: '考拉',
                        type: 'bar',
                        stack: '总量',
                        label: {
                            normal: {
                                show: true,
                                position: 'insideRight'
                            }
                        },
                        data: graphData.channel5
                    }
                ]
            };
            myChart.setOption(option);
        }).catch((err) => {
            document.getElementById('graph').innerText = "获取数据失败";
        });
    }
}());
addData.js

(这部分代码只是为了模拟自动更新数据。当画面停留在应用列表页,会定时更新数据来模拟销量的变化。)

(function () {
    'use strict';
    kintone.events.on(['app.record.index.show'], function (res) {
        var kintoneRecord = new kintoneJSSDK.Record();
        var app = xxx;
        var id = x;
        setInterval(function updateData() {
            kintoneRecord.getRecord({ app, id }).then((rsp) => {
                var data = rsp.record;
                var channel1Data = Number(data.channel1.value) + Number(random(1, 10));
                var channel2Data = Number(data.channel1.value) + Number(random(1, 20));
                var channel3Data = Number(data.channel1.value) + Number(random(1, 10));
                var channel4Data = Number(data.channel1.value) + Number(random(1, 10));
                var channel5Data = Number(data.channel1.value) + Number(random(1, 10));
                var record = {
                    "channel1": { "value": channel1Data },
                    "channel2": { "value": channel2Data },
                    "channel3": { "value": channel3Data },
                    "channel4": { "value": channel4Data },
                    "channel5": { "value": channel5Data }
                }
                var revision = -1;
                kintoneRecord.updateRecordByID({ app, id, record, revision }).then((rsp) => {
                    console.log(rsp);
                }).catch((err) => {
                    // This SDK return err with KintoneAPIException
                    console.log(err);
                });
            }).catch((err) => {
                // This SDK return err with KintoneAPIException
                console.log(err);
            });
            return updateData;
        }(), 3000)
    });
    function random(lower, upper) {
        return Math.floor(Math.random() * (upper - lower)) + lower;
    }
}());

注意:请注意修改 appid 这两个变量的值,分别是这个应用的id和要更新的记录的id。

Step4: 导入自定义开发文件到kintone

· 因为代码已经对应了手机端和电脑端,所以可以使用相同的js文件。

· 请注意文件的导入顺序。

0015db7f735bfefce3b27669d4e262b

step5: 添加模拟数据

接下来让我们来添加一些模拟数据吧。

请添加2019-11-05~2019-11-11 这几天的模拟数据:

0015db7ff68411315565600c10eccac

详情页和列表页显示效果

完成自定义和添加数据之后,在列表页和详情页就能看到通过echars生成的图表了。

应用详情页
0015db7f866d68e5dffdbae6b2aa231

应用列表页
0015db7f8f79e6a6ab518a7241a3335

手机端画面
   0015db7fadf37447648bdada5abd91b  0015db7fade858808a05943d7ec866d

门户的自定义开发

应用完成之后,我们现在就来利用kintone的门户首页显示事件来自定义首页的Dashboard 。

注意:此开发需有系统管理员权限。

Step1: 创建JavaScript文件

portal.js
(function () {
    'use strict';
    kintone.events.on('portal.show', function (res) {
        const pcSetting = {
            type: 'pc',
            legend: { top: '10%' },
            grid: { left: '10%', width: '35%', top: '25%' },
            pie: { center: ['70%', '60%'], radius: '50%' },
            style: "width: auto;height:400px;background-color:#fff;margin: 16px 16px 0 16px;"
        };
        init(pcSetting);
        generateGraph(pcSetting);
    });
    kintone.events.on('mobile.portal.show', function (res) {
        const mobileSetting = {
            type: 'mobile',
            legend: { top: '5%' },
            grid: { left: '10%', height: '40%', top: '18%' },
            pie: { center: ['50%', '80%'], radius: '30%' },
            style: "width: auto;height:600px;background-color:#fff;margin: 10px 10px 0 10px;border-radius: 6px"
        };
        init(mobileSetting);
        generateGraph(mobileSetting);
    });
    function generateGraph(setting) {
        var kintoneRecord = new kintoneJSSDK.Record();
        var rcOption = {
            app: xxx,
            query: 'order by date asc'
        };
        var myChart = echarts.init(document.getElementById('graph'));
        myChart.on('updateAxisPointer', function (event) {
            var xAxisInfo = event.axesInfo[0];
            if (xAxisInfo) {
                var dimension = xAxisInfo.value + 1;
                myChart.setOption({
                    series: {
                        id: 'pie',
                        label: {
                            formatter: '{b}: {@[' + dimension + ']} ({d}%)'
                        },
                        encode: {
                            value: dimension,
                            tooltip: dimension
                        }
                    }
                });
            }
        });
        setInterval(function setchart() {
            kintoneRecord.getAllRecordsByCursor(rcOption).then((rsp) => {
                var records = rsp.records;
                var graphData = { 'channel1': ['京东'], 'channel2': ['淘宝'], 'channel3': ['拼多多'], 'channel4': ['天猫'], 'channel5': ['考拉'] };
                var dateArray = ['渠道'];
                for (var record of records) {
                    var dateKey = record.date.value;
                    graphData.channel1.push(record.channel1.value);
                    graphData.channel2.push(record.channel2.value);
                    graphData.channel3.push(record.channel3.value);
                    graphData.channel4.push(record.channel4.value);
                    graphData.channel5.push(record.channel5.value);
                    dateArray.push(dateKey);
                }
                var option = {
                    legend: setting.legend,
                    title: {
                        text: '2019双十一各渠道实时销量统计',
                        left: 'center'
                    },
                    tooltip: {
                        trigger: 'axis',
                        showContent: false
                    },
                    dataset: {
                        source: [
                            dateArray,
                            graphData.channel1,
                            graphData.channel2,
                            graphData.channel3,
                            graphData.channel4,
                            graphData.channel5
                        ]
                    },
                    xAxis: { type: 'category' },
                    yAxis: { gridIndex: 0, name: '单位(万台)' },
                    grid: setting.grid,
                    series: [
                        { type: 'line', smooth: true, seriesLayoutBy: 'row' },
                        { type: 'line', smooth: true, seriesLayoutBy: 'row' },
                        { type: 'line', smooth: true, seriesLayoutBy: 'row' },
                        { type: 'line', smooth: true, seriesLayoutBy: 'row' },
                        { type: 'line', smooth: true, seriesLayoutBy: 'row' },
                        {
                            type: 'pie',
                            id: 'pie',
                            radius: setting.pie.radius,
                            center: setting.pie.center,
                            label: {
                                formatter: '{b}: {@2019-11-11} ({d}%)'
                            },
                            encode: {
                                itemName: '渠道',
                                value: '2019-11-11',
                                tooltip: '2019-11-11'
                            }
                        }
                    ]
                };
                myChart.setOption(option);
            }).catch((err) => {
                document.getElementById('graph').innerText = "获取数据失败";
            });
            return setchart;
        }(), 3000)
    }
    function init(setting) {
        if (document.getElementById('graph') !== null) {
            return;
        }
        var graph = document.createElement('div');
        graph.id = 'graph';
        graph.style = setting.style;
        if (setting.type === "mobile") {
            kintone.mobile.portal.getContentSpaceElement().appendChild(graph);
        }
        else {
            kintone.portal.getContentSpaceElement().appendChild(graph);
        }
    }
}());

注意:请注意修改 app 这个变量的值,是这个应用的id。

Step2: 导入自定义开发文件到kintone

首页的自定义开发是在系统管理里进行导入的。

同样,我们的代码已经对应了手机端和电脑端,可以使用相同的js文件。

0015db7fdd68d305fbf0d272aaf5d09

0015db7fddd7538905de7a98ba05d50

Step3: 删除之前应用里导入的相同的文件

因为上面的自定义开发文件同样会在kintone的应用内部生效,所以我们可以把之前“双十一销量统计”这个应用内的两个js文件(kintone-js-sdk.min.js和echarts.common.js )删除掉以避免重复调用。

0015db7fed9939b079494df3b784fc3

验证

请同时打开两个画面:

· 画面1:应用列表页(用来模拟数据的自动添加)

· 画面2:首页

这样我们就能看到文章开头的那个首页实时销量统计的效果了。

代码下载

所有自定义文件及模板下载

总结

这里我们用echarts开源画图库举了一个小小的例子。其实它的表现形式非常多,还可以和kintone整合实现更多酷炫有趣的图表效果。
真的是又方便又好用,大家可以根据自己的实际需求实践起来,一起实现自己想要的BI报表吧!

注意事项

  • 本示例代码不保证其运行。

  • 我们不为本示例代码提供技术支持。

回复(4)

  • cybozu

    不好意思 ,上次没上传成功。现在上传了。

    引用 追梦人 的回复:

    首先感谢你的回复。我重新试了一下,还是报哪个错误。我怀疑  你 说的 哪个  新的kintone-js-sdk.min.js文件已经上传 ,没有上传成功哦,麻烦再看下,谢谢。错误如下所示:Uncaught SyntaxError: Unexpected token '<'index.js:975 Uncaught ReferenceError: kintoneJSSDK is not defined    at download.do?app=600&contentId=16145&jsType=DESKTOP&hash=983311617a6f99927183a86afb6db6fbbc446b3d:4    at index.js:239    at Array.forEach (<anonymous>)    at v (index.js:870)    at qv (index.js:239)    at rv (index.js:239)    at l_.<anonymous> (index.js:519)    at Gk (index.js:131)    at Hk (index.js:130)    at Sk.e.eJ (index.js:974)

  • 追梦人

    首先感谢你的回复。

    我重新试了一下,还是报哪个错误。我怀疑  你 说的 哪个  新的kintone-js-sdk.min.js文件已经上传 ,没有上传成功哦,麻烦再看下,谢谢。


    错误如下所示:

    Uncaught SyntaxError: Unexpected token '<'

    index.js:975 Uncaught ReferenceError: kintoneJSSDK is not defined

        at download.do?app=600&contentId=16145&jsType=DESKTOP&hash=983311617a6f99927183a86afb6db6fbbc446b3d:4

        at index.js:239

        at Array.forEach (<anonymous>)

        at v (index.js:870)

        at qv (index.js:239)

        at rv (index.js:239)

        at l_.<anonymous> (index.js:519)

        at Gk (index.js:131)

        at Hk (index.js:130)

        at Sk.e.eJ (index.js:974)

    引用 betsy_yan 的回复:

    谢谢你的提醒。新的kintone-js-sdk.min.js文件已经上传,请再次尝试。

  • betsy_yan

    谢谢你的提醒。

    新的kintone-js-sdk.min.js文件已经上传,请再次尝试。

    引用 追梦人 的回复:

    我尝试使用这个模板,导入我们公司环境当中,在 详情页面一切都是正常的,但是在 应用列表页  看不到 教程中 的那种效果, F12 看到报下面的错误: Uncaught SyntaxError: Unexpected token '<'index.js:959 Uncaught ReferenceError: kintoneJSSDK is not defined    at download.do?app=598&contentId=16049&jsType=DESKTOP&hash=6282653529231693a129adc41478b3bd4a6d2c35:4    at index.js:239    at Array.forEach (<anonymous>)    at t (index.js:868)    at sv (index.js:239)    at tv (index.js:239)    at j_.<anonymous> (index.js:519)    at dk (index.js:129)    at ek (index.js:128)    at pk.e.cJ (index.js:958)请问 是不是  kintone-js-sdk.min.js   提供的 这个js 文件 有问题,可以 帮忙看下吗?谢谢。

  • 追梦人

    我尝试使用这个模板,导入我们公司环境当中,在 详情页面一切都是正常的,但是在 应用列表页  看不到 教程中 的那种效果, F12 看到报下面的错误:

     

    Uncaught SyntaxError: Unexpected token '<'

    index.js:959 Uncaught ReferenceError: kintoneJSSDK is not defined

        at download.do?app=598&contentId=16049&jsType=DESKTOP&hash=6282653529231693a129adc41478b3bd4a6d2c35:4

        at index.js:239

        at Array.forEach (<anonymous>)

        at t (index.js:868)

        at sv (index.js:239)

        at tv (index.js:239)

        at j_.<anonymous> (index.js:519)

        at dk (index.js:129)

        at ek (index.js:128)

        at pk.e.cJ (index.js:958)


    请问 是不是  kintone-js-sdk.min.js   提供的 这个js 文件 有问题,可以 帮忙看下吗?谢谢。


注意:贴代码时请注意格式并使用"代码语言",与本文无关的问题请至“讨论社区”提问。
您需要登录后才可以回复