Greasy Fork is available in English.

生财有术志愿者后台打卡统计专用

https://i.shengcaiyoushu.com/volunteer/operation/activity/user?id=XXX 作为一个懒人,不想手动统计,干脆做个工具一目了然

// ==UserScript==
// @name         生财有术志愿者后台打卡统计专用
// @namespace    http://www.heigoou.com
// @version      1.1
// @description  https://i.shengcaiyoushu.com/volunteer/operation/activity/user?id=XXX 作为一个懒人,不想手动统计,干脆做个工具一目了然
// @author       北封,jexxx
// @match        https://i.shengcaiyoushu.com/volunteer/operation/activity/user*
// @require      https://fastly.jsdelivr.net/npm/echarts@5.4.1/dist/echarts.min.js
// @icon         data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMzYwcHgiIGhlaWdodD0iMzYwcHgiIHZpZXdCb3g9IjAgMCAzNjAgMzYwIiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPgogICAgPCEtLSBHZW5lcmF0b3I6IFNrZXRjaCA2MC4xICg4ODEzMykgLSBodHRwczovL3NrZXRjaC5jb20gLS0+CiAgICA8dGl0bGU+57yW57uEPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGRlZnM+CiAgICAgICAgPHBvbHlnb24gaWQ9InBhdGgtMSIgcG9pbnRzPSIxLjEzMzQwMjI0ZS0wNSAwIDM1OS45OTk4NjMgMCAzNTkuOTk5ODYzIDM2MCAxLjEzMzQwMjI0ZS0wNSAzNjAiPjwvcG9seWdvbj4KICAgIDwvZGVmcz4KICAgIDxnIGlkPSIxLjEt5by556qXIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0i57yW57uEIj4KICAgICAgICAgICAgPGc+CiAgICAgICAgICAgICAgICA8bWFzayBpZD0ibWFzay0yIiBmaWxsPSJ3aGl0ZSI+CiAgICAgICAgICAgICAgICAgICAgPHVzZSB4bGluazpocmVmPSIjcGF0aC0xIj48L3VzZT4KICAgICAgICAgICAgICAgIDwvbWFzaz4KICAgICAgICAgICAgICAgIDxnIGlkPSJDbGlwLTIiPjwvZz4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xOTAuMTU5NCwzMzUuMDA1MiBDOTYuNTE4NCwzNDAuOTk2MiAxOS4wMDA0LDI2My40NzgyIDI0Ljk5MzQsMTY5LjgzODIgQzI5Ljk0NjQsOTIuNDQ2MiA5Mi40NjE0LDI5LjkzNjIgMTY5Ljg1MjQsMjQuOTkxMiBDMjYzLjQ4MjQsMTkuMDA5MiAzNDAuOTg3NCw5Ni41MTMyIDMzNS4wMDg0LDE5MC4xNDQyIEMzMzAuMDYzNCwyNjcuNTM3MiAyNjcuNTUzNCwzMzAuMDUzMiAxOTAuMTU5NCwzMzUuMDA1MiBNMTg4LjQ0ODQsMC4xOTQyIEM4Mi41NDE0LC00LjY3MzggLTQuNjczNiw4Mi41NDIyIDAuMTk0NCwxODguNDQ3MiBDNC40Mzg0LDI4MC43ODMyIDc5LjIxNjQsMzU1LjU2MTIgMTcxLjU1MjQsMzU5LjgwNjIgQzI3Ny40NTc0LDM2NC42NzQyIDM2NC42NzQ0LDI3Ny40NTkyIDM1OS44MDU0LDE3MS41NTIyIEMzNTUuNTU4NCw3OS4yMTYyIDI4MC43ODM0LDQuNDQxMiAxODguNDQ4NCwwLjE5NDIiIGlkPSJGaWxsLTEiIGZpbGw9IiMwQTVENEYiIG1hc2s9InVybCgjbWFzay0yKSI+PC9wYXRoPgogICAgICAgICAgICA8L2c+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xNjYuMzQ5OCwyMDguOTY4MSBMMTY2LjM0OTgsMjIxLjc4OTEgTDE0OC43MjM4LDIyMS43ODkxIEMxNDguMDYxOCwyMjEuNzg5MSAxNDcuODA3OCwyMjAuOTI0MSAxNDguMzYzOCwyMjAuNTY0MSBMMTY2LjM0OTgsMjA4Ljk2ODEgWiBNMTkzLjY0ODgsMTYxLjQ5MTEgTDE5My42NDg4LDE0OS4wMTAxIEwyMTAuNTk4OCwxNDkuMDEwMSBDMjExLjMzNjgsMTQ5LjAxMDEgMjExLjYyMDgsMTQ5Ljk3NTEgMjEwLjk5NzgsMTUwLjM3MzEgTDE5My42NDg4LDE2MS40OTExIFogTTI0Ny40NjY4LDIyMS43ODkxIEwxOTMuODc3OCwyMjEuNzg5MSBMMTkzLjY1MDgsMjE5LjU5MjEgTDE5My42NDg4LDE5MS40NzkxIEwyNDguMTM1OCwxNTYuNzY4MSBDMjUzLjQ2MzgsMTUzLjM3MzEgMjU2Ljc0NTgsMTQ1LjM5ODEgMjU0LjIwMzgsMTM2Ljg1NTEgQzI1MS43ODI4LDEyOS4yNzIxIDI0NC43MzM4LDEyNC4xMjQxIDIzNi43NzI4LDEyNC4xMjQxIEwxOTMuNjQ4OCwxMjQuMTI0MSBMMTkzLjY0ODgsODcuOTcyMSBDMTkzLjY0ODgsODQuMTYzMSAxOTAuNTYwOCw4MS4wNzYxIDE4Ni43NTE4LDgxLjA3NjEgTDE3My4yNDI4LDgxLjA3NjEgQzE2OS40MzU4LDgxLjA3NjEgMTY2LjM0OTgsODQuMTYyMSAxNjYuMzQ5OCw4Ny45NjkxIEwxNjYuMzQ5OCwxMjQuMTI0MSBMMTEyLjc0NjgsMTI0LjEyNDEgQzEwNy45Nzk4LDEyNC4xMjQxIDEwNC4xMTI4LDEyNy45ODkxIDEwNC4xMTI4LDEzMi43NTkxIEwxMDQuMTEyOCwxNDAuMjA5MSBDMTA0LjExMjgsMTQ1LjA3MTEgMTA4LjA1MzgsMTQ5LjAxMDEgMTEyLjkxMjgsMTQ5LjAxMDEgTDE2Ni4zNDk4LDE0OS4wMTAxIEwxNjYuMzQ5OCwxNzkuMDEzMSBMMTEyLjczMjgsMjEzLjQxODEgQzEwNi44Njg4LDIxNy4xODgxIDEwMy4yMjM4LDIyNS44MTIxIDEwNS40OTQ4LDIzMy41NzIxIEMxMDcuODUwOCwyNDEuMjM5MSAxMTQuOTMzOCwyNDYuNDc0MSAxMjIuOTU2OCwyNDYuNDc0MSBMMTY2LjM0OTgsMjQ2LjQ3NDEgTDE2Ni4zNDk4LDI3NS41NDQxIEMxNjYuMzQ5OCwyNzkuMzU0MSAxNjkuNDM3OCwyODIuNDQwMSAxNzMuMjQ1OCwyODIuNDQwMSBMMTg2Ljc2MDgsMjgyLjQ0MDEgQzE5MC41NjQ4LDI4Mi40NDAxIDE5My42NDg4LDI3OS4zNTgxIDE5My42NDg4LDI3NS41NTMxIEwxOTMuNjQ4OCwyNDYuNDc0MSBMMjQ3LjU2MDgsMjQ2LjQ3NDEgQzI1Mi4xNTY4LDI0Ni40NzQxIDI1NS44ODM4LDI0Mi43NDYxIDI1NS44ODM4LDIzOC4xNTAxIEwyNTUuODgzOCwyMzAuMjA0MSBDMjU1Ljg4MzgsMjI1LjU1NzEgMjUyLjExNjgsMjIxLjc4OTEgMjQ3LjQ2NjgsMjIxLjc4OTEgTDI0Ny40NjY4LDIyMS43ODkxIFoiIGlkPSJGaWxsLTMiIGZpbGw9IiMwQTVENEYiPjwvcGF0aD4KICAgICAgICAgICAgPHBhdGggZD0iTTIyOC45Mjg5LDEwNi42NjQ0IEwyNDEuMTM0OSwxMDYuNjY0NCBDMjQ1LjI0NTksMTA2LjY2NDQgMjQ4LjU3ODksMTAzLjMzMTQgMjQ4LjU3ODksOTkuMjIxNCBMMjQ4LjU3ODksODguNTIzNCBDMjQ4LjU3ODksODQuNDExNCAyNDUuMjQzOSw4MS4wNzY0IDI0MS4xMzE5LDgxLjA3NjQgTDIyOC45Mjg5LDgxLjA3NjQgQzIyNC44MTg5LDgxLjA3NjQgMjIxLjQ4NTksODQuNDA5NCAyMjEuNDg1OSw4OC41MTk0IEwyMjEuNDg1OSw5OS4yMjE0IEMyMjEuNDg1OSwxMDMuMzMxNCAyMjQuODE4OSwxMDYuNjY0NCAyMjguOTI4OSwxMDYuNjY0NCIgaWQ9IkZpbGwtNSIgZmlsbD0iIzBBNUQ0RiI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+
// @grant        none
// @license GPLv3
// @run-at document-end

// ==/UserScript==

(function () {

    'use strict';
    let baseData = data;
    let stage = baseData.stage;
    let xData = [];
    let dayInfo = {};

    for (let d = 0; d < stage.length; d++) {
        let dayItem = stage[d];

        let time = dayItem.gmt_start * 1000;
        if (time > new Date().getTime()) {
            // 还没到的时间,就不统计了
            break;
        }

        let date = new Date(time);
        let year = date.getFullYear();
        let month = date.getMonth() + 1; // 月份
        let day = date.getDate(); // 日
        let dayStr = year + '-' + month + '-' + day;

        let dayData = {
            id: dayItem.id,
            idx: dayItem.idx,
            name: dayItem.name,
            dayStr: dayStr,
        };
        dayInfo[dayData.id] = dayData;
        xData.push(dayData.name + '\r\n' + dayData.dayStr);
    }
    $('.cxd-Panel--form').after('<div id="bf-container" style="width: 100%;height: 400px"></div>');
    $('#bf-container').after('<div id="pf-container" style="width: 100%;height: 400px"></div>');
    $('#pf-container').after('<div id="text-container" style="width: 90%;margin-left:auto;"></div>')
    $.post(window.location.href.replace('user', 'user/ajax'), function (response) {
        var items = response.data.items
        var result = {};
        // let totalPeople = Object.keys(response.data.extra.user).length;
        let yDaKaDataObject = {};
        const days = Object.keys(dayInfo);
        const highPoints = []; // 重点关注对象
        let wxids = new Set();
        for (let dd = 0; dd < days.length; dd++) {
            const dInfo = dayInfo[days[dd]];
            for (var k = 0; k < items.length; k++) {
                const item = items[k];
                if (item.identity) {
                    // identity: "志愿者" 领队 教练角色不统计
                    continue;
                }
                wxids.add(item.info['微信号(微信ID)']);
                yDaKaDataObject[dInfo['id']] = yDaKaDataObject[dInfo['id']] || {};


                var cnt = item.project_cnt;
                if (!result[cnt]) {
                    result[cnt] = [];
                }
                var name = response.data.extra.user[item.xq_group_number]['name'];
                var wx_name = response.data.extra.user[item.xq_group_number]['wx_name'];
                if (name != wx_name) {
                    name = name + '(' + wx_name + ')';
                }
                var user = name;
                var project_left_cnt = item.project_left_cnt;
                if (dd === 0) highPoints.push({ user, cnt, project_left_cnt });
                if (!result[cnt].includes(user)) {
                    result[cnt].push(user);
                }
                const project = item.project;
                yDaKaDataObject[dInfo['id']]['n'] = yDaKaDataObject[dInfo['id']]['n'] || [];
                yDaKaDataObject[dInfo['id']]['y'] = yDaKaDataObject[dInfo['id']]['y'] || [];

                if (project) {
                    if (project[dInfo['id']]) {
                        yDaKaDataObject[dInfo['id']]['y'].push(user);

                    } else {
                        yDaKaDataObject[dInfo['id']]['n'].push(user);
                    }


                } else {
                    if (!yDaKaDataObject[dInfo['id']]['n'].includes(user)) {
                        yDaKaDataObject[dInfo['id']]['n'].push(user);
                    }
                }


            }


        }

        let yNoDaKaData = [];
        let yDaKaData = [];

        for (let da = 0; da < days.length; da++) {
            const dayDaka = yDaKaDataObject[days[da]];
            const dayStep = dayInfo[days[da]];
            let daKaData = {
                value: dayDaka['y'].length,
                names: dayDaka['y'],
                percent: (Math.round((dayDaka['y'].length / (dayDaka['y'].length+dayDaka['n'].length)) * 100) / 100),
            };
            yDaKaData.push(daKaData);
            let ndaKaData = {
                value: dayDaka['n'].length,
                names: dayDaka['n'],
                percent: (Math.round((dayDaka['n'].length / (dayDaka['y'].length+dayDaka['n'].length)) * 100) / 100),
            };
            yNoDaKaData.push(ndaKaData)

        }

        var text = '';
        var keys = Object.keys(result);
        var step = 0;
        var _highPoints = highPoints.sort(function (a, b) {
            return Number(a.cnt) - Number(b.cnt);
        });
        for (var j = 0; j < keys.length; j++) {
            var day = keys[j];
            var people = result[day];
            step = step + people.length;
            text += '完成打卡 ' + day + ' 天的有 ' + people.length + ' 人,他们是:' + people + '<br>';
        }
        // console.log(text);
        // console.log(yNoDaKaData[yNoDaKaData.length-1].names);
        text += '-------------------------华丽的分割线-------------------------------------<br>'
        text += '今日未打卡人员>>>' + yNoDaKaData[yNoDaKaData.length - 1].names+'<br><br>';
        text += '微信ID:';
        for(let wId of wxids){
            text += wId+' , ';
        }
        $('#text-container').html(text)
        var dom = document.getElementById('bf-container');
        var pdom = document.getElementById('pf-container');
        var myChart = echarts.init(dom, null, { renderer: 'canvas', useDirtyRect: false });
        var myChart2 = echarts.init(pdom, null, { renderer: 'canvas', useDirtyRect: false });

        var option;

        option = {
            tooltip: {
                trigger: 'axis',
                backgroundColor: '#FFFFFF',
                borderColor: '#EEF1F7',
                borderWidth: 1,
                textStyle: {
                    width: 160,
                    height: 250,
                    lineHeight: 24,
                    color: '#666666',
                    fontSize: '14',
                    fontFamily: 'SourceHanSansCN-Normal'
                },
                formatter: params => {
                    // 获取xAxis data中的数据
                    let dataStr = `<div><p style="font-weight:bold;margin:0 8px 15px;">${params[0].axisValue} 打卡统计</p></div>`
                    params.forEach(item => {
                        dataStr += `<div>
          <div style="margin: 0 8px;">
            <span style="display:inline-block;margin-right:5px;width:10px;height:10px;background-color:${item.color};"></span>
            <span>${item.seriesName} ${item.data.percent}:</span>
            <span style="float:right;color:#000000;margin-left:20px;">${item.data.value}人</span>`;
                        let names = item.data.names;
                        names.forEach(name => {
                            dataStr += `${name} `
                        });

                        //
                        //
                        dataStr +=
                            `</div>
        </div>`
                    })
                    return dataStr
                }
            },

            legend: {
            },
            // grid: {
            //   left: '3%',
            //   right: '4%',
            //   bottom: '3%',
            //   containLabel: true
            // },
            xAxis: {
                type: 'category',
                data: xData
            },
            yAxis: [
                {
                    type: 'value',
                    name: '积极打卡',
                },
                {
                    type: 'value',
                    name: '偷懒了~',
                },
            ],
            series: [
                {
                    name: '积极打卡',
                    type: 'bar',
                    stack: 'total',
                    label: {
                        show: true
                    },
                    emphasis: {
                        focus: 'series'
                    },
                    data: yDaKaData,

                },
                {
                    name: '偷懒了~',
                    type: 'bar',
                    stack: 'total',
                    label: {
                        show: true
                    },
                    emphasis: {
                        focus: 'series'
                    },
                    data: yNoDaKaData
                },
            ]
        };

        var option2 = {

            xAxis: {
                type: 'category',
                data: _highPoints.map(item => item.user),
                axisLabel: {
                    show: true,
                    interval: 0,
                    rotate: 40,
                    textStyle: {
                        color: '#333'
                    }
                },
            },
            yAxis: [
                {
                    type: 'value',
                    name: '打卡',
                },
            ],
            series: [
                {
                    name: '打卡',
                    type: 'bar',
                    stack: 'total',
                    label: {
                        show: true
                    },
                    emphasis: {
                        focus: 'series'
                    },
                    data: _highPoints.map(item => item.cnt),

                },
            ]
        };

        if (option && typeof option === 'object') {
            myChart.setOption(option);
        }

        if (option2 && typeof option2 === 'object') {
            myChart2.setOption(option2);
        }

        window.addEventListener('resize', () => {
            myChart.resize();
            myChart2.resize();
        });
    });
})();