HLWorkTime

time

// ==UserScript==
// @license MIT
// @name HLWorkTime
// @description time
// @namespace http://tampermonkey.net/
// @version 1.0
// @author Li
// @match https://*.cn/manager/views/inner.html
// @icon https://www.google.com/s2/favicons?sz=64&domain=133.cn
// @grant GM_addStyle
// ==/UserScript==


(function() {
    'use strict';
    GM_addStyle(`.hlbtnT{
    color: sandybrown;
    margin-left: 10px;
    line-height: 40px;
    font-size: 17px;
}

.hlbtnBg{

    background-color:orange;
    color: #f0f0f0;
    padding-left: 20px;
    padding-right: 20px;
    height: 30px;
    border-radius: 20px;
    font-size: 15px;
    display: flex;
    align-items: center;
    justify-content: center;

}

.hlxxx{
    width: 150%;
    flex-direction: row;
    display: flex;
    justify-content:space-between;
    align-items: center;
    padding: 0 0.5em;

    border-radius: 5px;

}


.hlxxdesc{
    font-size: 1.3em;
    color: #888;
    font-weight: 600;
    text-align: left;

}`)








    !(async function () {
  var __DB;
  async function genDbKey(key) {
    var name = document.querySelector(".userInfo").innerText;
    name = name.substring(name.length - 3);
    const encoder = new TextEncoder();
    const data = encoder.encode("" + key + name);
    const hashBuffer = await crypto.subtle.digest("SHA-256", data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray
      .map((byte) => byte.toString(16).padStart(2, "0"))
      .join("");
    return hashHex;
  }

  async function getDb() {
    if (__DB) {
      return __DB;
    }

    return new Promise((r) => {
      const request = indexedDB.open("hlxxx", 1);
      request.onupgradeneeded = function (event) {
        __DB = event.target.result;
        const objectStore = __DB.createObjectStore("myObjectStore", {
          keyPath: "key",
        });
      };

      request.onerror = function (event) {
        console.error("Error opening database:", event.target.error);
        r(null);
      };

      request.onsuccess = function (event) {
        __DB = event.target.result;
        // 在此处进行数据操作
        console.log("create db succ");
        r(__DB);
      };
    });
  }

  var _KEYOBJ;
  async function genEncKey() {
    if (_KEYOBJ) {
      return _KEYOBJ;
    }
    var substl = crypto.subtle;
    let keyRaw = new TextEncoder().encode("了991");

    let key = await substl.importKey("raw", keyRaw, "PBKDF2", false, [
      "deriveBits",
    ]);

    let salt = "盐烟衍嫣彦燕言";

    let pbkdf2 = {
      name: "PBKDF2",
      hash: "SHA-256",
      iterations: 2,
      salt: new TextEncoder().encode(salt),
    };
    let af = await substl.deriveBits(pbkdf2, key, 256);
    let arrPri = new Uint8Array(af);

    _KEYOBJ = await substl.importKey("raw", arrPri, "AES-CBC", false, [
      "decrypt",
      "encrypt",
    ]);
    return _KEYOBJ;
  }

  async function encryptData(data) {
    let dataraw = new TextEncoder().encode(data);

    const iv = crypto.getRandomValues(new Uint8Array(16)); // 生成随机的初始向量 IV
    const encrypted = await crypto.subtle.encrypt(
      { name: "AES-CBC", iv: iv, length: 256 },
      await genEncKey(),
      dataraw
    );
    return { encryptedData: new Uint8Array(encrypted), iv: iv };
  }

  async function decryptData(dataObj) {
    try {
      var plain = await crypto.subtle.decrypt(
        { name: "AES-CBC", iv: dataObj.iv, length: 256 },
        await genEncKey(),
        dataObj.encryptedData
      );
      return new TextDecoder().decode(plain);
    } catch (error) {
      return null;
    }
  }

  async function setCache(key0, value) {
    key0 = await genDbKey(key0);
    if (value) {
      value = await encryptData(value);
    }

    var db = await getDb();
    const transaction = db.transaction(["myObjectStore"], "readwrite");
    const objectStore = transaction.objectStore("myObjectStore");

    // 使用 add 方法添加数据,数据对象中必须包含 "customId" 字段作为主键
    const request = objectStore.put({ key: key0, value: value });

    request.onsuccess = function (event) {
      console.log("Data added to the database.");
    };

    request.onerror = function (event) {
      console.error("Error adding data to the database:", event.target.error);
    };
  }

  async function getCache(key0) {
    key0 = await genDbKey(key0);
    var db = await getDb();
    const transaction = db.transaction(["myObjectStore"], "readonly");
    const objectStore = transaction.objectStore("myObjectStore");
    const request = objectStore.get(key0);
    return new Promise((r) => {
      request.onsuccess = async function (event) {
        const data = event.target.result;
        if (data && data.value) {
          let v = await decryptData(data.value);
          r(v);

          return;
        }
        r(null);
      };
      request.onerror = function (event) {
        console.error(
          "Error retrieving data from the database:",
          event.target.error
        );
        r(null);
      };
    });
  }

  async function doTask() {
    if (!/manager\/views\/inner.html/.test(window.location.href)) {
      console.log("0-0000000000000000000000000000");
      return;
    }

    /// 从前一个月28到这个月28
    const StartDay = 28;

    var g_data = {};

    const NoonSleep2 = 2 * 3600;
    const NoonSleep1 = 1 * 3600;
    const keySleep = "rJOIEc";
    var stime = localStorage.getItem(keySleep);
    stime = parseInt("" + stime);
    var resestTime = NoonSleep2;
    if (stime == NoonSleep1 || NoonSleep2 == stime) {
      resestTime = stime;
    }

    function getTimeValue(t) {
      if (t) {
        var timeComponets = t.split(":");
        if (timeComponets.length == 3) {
          return (
            parseInt(timeComponets[0]) * 3600 +
            parseInt(timeComponets[1]) * 60 +
            parseInt(timeComponets[2])
          );
        }
      }
    }

    function timeValueToStr(sum) {
      let h = Math.floor(sum / 3600);
      let m = Math.floor((sum - h * 3600) / 60);
      let s = sum % 60;

      return `${h}:${m}:${s}`;
    }

    async function wait(t) {
      return new Promise((r, j) => {
        setTimeout(() => {
          r(1);
        }, t * 1000);
      });
    }

    async function getCheckInData() {
      while (document.querySelector(".fc-center > h2:nth-child(1)") == null) {
        await wait(0.5);
      }

      var host = location.host;
      return new Promise((r) => {
        var httpRequest = new XMLHttpRequest();
        httpRequest.open(
          "POST",
          `https://${host}/manager/attendance/querylistByUser`,
          true
        );
        httpRequest.setRequestHeader(
          "Content-type",
          "application/x-www-form-urlencoded"
        );

        let date = document
          .querySelector(".fc-center > h2:nth-child(1)")
          .innerText.replace(/年|月/g, "");
        httpRequest.send("recordDateStr=" + date + "&userName=");
        httpRequest.onreadystatechange = function () {
          //请求后的回调接口,可将请求成功后要执行的程序写在其中
          if (httpRequest.readyState == 4 && httpRequest.status == 200) {
            //验证请求是否发送成功
            try {
              var jsonStr = httpRequest.responseText; //获取到服务端返回的数据
              var json = JSON.parse(jsonStr);
              console.log("result", json);

              let bjDate = beijingDate();

              let y = parseInt(bjDate.split("-")[0]);
              let m = parseInt(bjDate.split("-")[1]);

              let y2 = parseInt(date.split("-")[0]);
              let m2 = parseInt(date.split("-")[1]);

              if (y * 10000 + m > y2 * 10000 + m2) {
                console.log("缓存", date);
                setCache(date, jsonStr);
              } else {
                console.log("不  缓存", date);
              }

              r(json);
            } catch (error) {
              console.log(error);
              r(null);
            }
          }
        };
      });
    }

    function beijingDate() {
      return new Date(Date.now() + 8 * 3600000).toISOString().substring(0, 10);
    }

    async function getTipStr(json) {
      let ymd = beijingDate();
      let today = "" + parseInt(ymd.substring(8, 10));

      let date = document
        .querySelector(".fc-center > h2:nth-child(1)")
        .innerText.replace(/年|月/g, "");

      var showStr = "" + date;

      {
        var sum = 0;
        var dayC = 0;

        var sumAll = 0;
        for (var i = 1; i < 32; i++) {
          const element = json.data["" + i];

          if (!element) {
            break;
          }

          if (element.startTime && element.endTime) {
            var s = getTimeValue(element.startTime);
            var e = getTimeValue(element.endTime);
            if (e && s && e - s > 3600) {
              var worktitme = e - s - resestTime;
              worktitme = worktitme > 0 ? worktitme : 0;
              sum += worktitme - 8 * 3600;
              sumAll += worktitme;

              dayC += 1;
            }

            /// 补卡 扣30分钟工作时长
            if (element.isLeave == "3") {
              sum -= 30 * 60;
              sumAll -= 30 * 60;
            }
          }
        }

        let timeNotEnough = 0;
        if (sum < 0) {
          sum = -sum;
          timeNotEnough = 1;
        }

        let avg = sumAll / (dayC * 3600);

        showStr += `\nTotal:  ${timeNotEnough ? "- " : "+ "}${(
          sum / 3600
        ).toFixed(2)}\nAvg:    ${(sumAll / 3600).toFixed(
          2
        )}/${dayC} = ${avg.toFixed(2)}`;

        showStr += '\n'

        if (date != null) {
          let y = parseInt(date.split("-")[0]);
          let m = parseInt(date.split("-")[1]);
          m -= 1;
          if (m == 0) {
            m = 12;
            y -= 1;
          }

          var key = `${y}-${m}`;
          console.log("pre", key);
          var preDataStr = await getCache(key);
          if (preDataStr) {
            var preData = JSON.parse(preDataStr);
            var dataInfo = preData.data;

            var preMonthWork = 0;

            var preDayC = 0;

            /// 上个月
            //  StartDay
            for (let i = StartDay; i <= 31; i++) {
              let ele = dataInfo[`${i}`];
              if (ele && ele.isHoliday == "0") {
                var s = getTimeValue(ele.startTime);
                var e = getTimeValue(ele.endTime);
                if (e && s && e - s > 3600) {
                  var worktitme = e - s - resestTime;
                  worktitme = worktitme > 0 ? worktitme : 0;
                  preMonthWork += worktitme;

                  preDayC += 1;
                }

                /// 补卡 扣30分钟工作时长
                if (ele.isLeave == "3") {
                  preMonthWork -= 30 * 60;
                }
              }
            }

            var currentWork = 0;
            var currentDayC = 0;

            for (let i = 0; i < StartDay; i++) {
              let ele = json.data[`${i}`];
              if (ele && ele.isHoliday == "0") {
                var s = getTimeValue(ele.startTime);
                var e = getTimeValue(ele.endTime);
                if (e && s && e - s > 3600) {
                  var worktitme = e - s - resestTime;
                  worktitme = worktitme > 0 ? worktitme : 0;
                  currentWork += worktitme;
                  currentDayC += 1;
                }

                /// 补卡 扣30分钟工作时长
                if (ele.isLeave == "3") {
                  currentWork -= 30 * 60;
                }
              }
            }

            var total = (currentWork + preMonthWork) / 3600;

            console.log("preMonth", preMonthWork, preDayC);

            showStr += `\nPre28:  ${(
              (preMonthWork - 8 * preDayC * 3600) /
              3600
            ).toFixed(2)}`;

            const totalDlt = (total - (currentDayC + preDayC) * 8)
            showStr += `\nTotal: ${totalDlt.toFixed(2)} = ${(totalDlt * 60).toFixed(1)} 分`

            showStr += `\nAvg28:  ${total.toFixed(2)}/${
              currentDayC + preDayC
            } = ${(total / (currentDayC + preDayC)).toFixed(2)}`;


          } else {
            showStr += "\nNo Last Month Data";
          }
        }

        {
          let todayData = json.data[today];

          if (
            todayData["recordDate"] ==
            new Date(new Date().getTime() + 8 * 3600000)
              .toISOString()
              .substring(0, 10)
          ) {
            let todayStart = todayData.startTime;
            let v1 = getTimeValue(todayStart);
            console.log("today,", todayData, todayStart, resestTime);
            /// + 60 按分钟
            let v2 = v1 + 8 * 3600 + resestTime + 60;
            if (v2 < getTimeValue("18:00:00")) {
              v2 = getTimeValue("18:00:00");
            }
            if (!isNaN(v2)) {
              showStr += `\ntoday:  ${timeValueToStr(v2)}`;
            }
          }
        }

        return showStr;
      }
      return "error";
    }

    function updateTip(str) {
      /// 当前不在考勤页面,3s 后重试
      if (!/attenmanager\/listuseratten/.test(window.location.href)) {
        setTimeout(() => {
          updateTip(str);
        }, 3000);
        return;
      }

      var d = document.evaluate(
        '//button[contains(@class,"fc-next-button")]/..',
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE
      );
      if (d && d.singleNodeValue) {
        var div = d.singleNodeValue;

        var node = document.getElementById("re0091");
        var nodeBtn = document.getElementById("re0092");
        if (!node) {
          var nodeFather = document.createElement("div");
          nodeFather.setAttribute("class", "hlxxx");
          div.appendChild(nodeFather);

          node = document.createElement("pre");
          node.setAttribute("id", "re0091");
          node.setAttribute("class", "hlxxdesc");
          nodeFather.appendChild(node);

          nodeBtn = document.createElement("div");
          nodeBtn.setAttribute("id", "re0092");
          nodeBtn.setAttribute("class", "hlbtnBg");
          nodeBtn.onclick = async () => {
            console.log("xxxab");
            resestTime = resestTime == NoonSleep2 ? NoonSleep1 : NoonSleep2;
            localStorage.setItem(keySleep, "" + resestTime);
            let strNewTip = await getTipStr(g_data);
            updateTip(strNewTip);
            console.log("xxxa");
          };
          nodeFather.appendChild(nodeBtn);

          var nodeBtn2 = document.createElement("div");
          nodeBtn2.setAttribute("id", "reX0092");
          nodeBtn2.setAttribute("class", "hlbtnBg");
          nodeBtn2.onclick = async () => {
            fresh();
          };
          nodeBtn2.innerText = "换月重算";
          nodeFather.appendChild(nodeBtn2);
        }
        node.innerText = str;
        nodeBtn.innerText = `午休${resestTime / 3600}h,点击切换`;
        console.log("xxx", nodeBtn.innerText);
      } else {
        setTimeout(() => {
          updateTip(str);
        }, 1000);
      }
    }

    async function fresh() {
      g_data = await getCheckInData();
      var str = await getTipStr(g_data);
      updateTip(str);
    }

    fresh();
  }

  var pushState = history.pushState;
  history.pushState = function (data, unused, url) {
    pushState(data, unused, url).bind(history);
    setTimeout(() => {
      doTask();
    }, 10);
  };
  doTask();
})();


    // Your code here...
})();