HLWorkTime

time

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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...
})();