【StartNode】工作站 --Task List

前言:又是新的一年开始,我想把今年想做的事情收集起来。刚好之前博客做过一个类似 TODO 的东西,但是效果并不好,因为没有本地服务去支持他。

乘着这个想法在重新完善一下,顺便重新梳理了后台node服务文件的处理流程。

1.需求

1.1 后台需求

获取任务与生成:当前端发送获取任务的请求后,node 服务需要返回当前月份和年度任务的信息,若没有对应 json 文件,则生成后再返回。

记录任务信息:当用户修改了任务信息后,并发送记录的需求,node 服务需要将对应的数据信息保存到文件里。

后台核心的需求大致就是这样。

1.2 页面需求

任务信息展示,分为年度任务和当日任务。
任务编辑:需要做一个编辑框来编辑任务信息。
其他编辑操作:维护信息相关的一些功能按钮。

2.实现

1.接口实现

1.1 geDaytask

获取任务信息,逻辑很简单,检测文件路径,设置文件流打开文件读取内容,最后将内容返回给用户。

看上去很简单,但具体实现的时候似乎没那么顺利。

在做文件检测的时候(CheckFile),发现 node 的 ESM 模式下无法直接获取__filepath,因为检测文件,需要知道目标文件的路径,这个路径对应 static 下存储文件的文件夹。也就是说我有一个 maketest 的接口,接口的处理逻辑在 maketestjs 文件里,当这个接口涉及文件的时候,比如生成文件,生成的文件路径就是 static/maketest 这个文件夹。

所以第一步要解决 ESM 没有本地文件的问题,我在 command 里做了一个 ESMbase 的 js 文件来解决:

import { fileURLToPath } from "url";
import path from "path";

/**
 *
 * @param {*} defaulturl 默认url 一般是import.meta.url
 * @returns
 *
 * 该函数用获取当前的文件名和文件夹,某种程度上可以解决ESM模式下没有__filename,__dirname的问题
 */
export const getFileNamePath = function (defaulturl) {
  return {
    __filename: fileURLToPath(defaulturl),
    __dirname: path.dirname(fileURLToPath(defaulturl)),
    __puerfilename: fileURLToPath(defaulturl).split("\\").pop().split('.')[0],
  };
};

这样就能直获取当前执行文件的文件名了。

当文件路径问题解决后,需要生成文件了。这部分没遇到什么问题,不过当时想写一个通用的检测函数,但是每个接口的业务逻辑可能不同,所以检测内的文件生成函数也略有不同,还是按接口业务逻辑来做不同的检测函数吧。

2.2 setDaytask

该接口的逻辑流程大致和 geDaytask 一样,只不过多了文件内容写入的逻辑。

2.3 页面实现

页面的实现需要 taskdom 弹出框 dom,以及 task 的功能函数,包括 task 的编辑,删除,完成,定时。

这里主要讲一下定时功能的实现吧,由于我惯性摸鱼,所以有时候头昏的时候,code 一会就打开网页逛街了,回过神来已经过了半小时,非常不好,所以我给任务加一个“专注模式”,当点击对应按钮的时候,该任务会每 3 分钟在通知栏报一次。

let timerID = {};
const setClock = (key, e) => {
  Notification.requestPermission().then(function (result) {
    if (result === "denied") {
      showMessage("没有通知权限,无法使用专注功能", "err");
      return;
    }
    if (result === "default") {
      return;
    }
    if (taskClock.value.key == key) {
      const taskitemdom = document.querySelectorAll(".taskitem");
      taskitemdom[taskClock.value.key].style = "";
      showMessage(
        "已解除专注任务--" + todayTask.value[taskClock.value.key].taskTitle
      );
      clearTimeout(timerID);
      taskClock.value.key = -1;
      taskClock.value.state = false;
      e.target.children[0].style = "";
      return;
    }
    if (taskClock.value.state) {
      showMessage(
        "请先完成当前专注任务--" +
          todayTask.value[taskClock.value.key].taskTitle,
        "err"
      );
      return;
    }

    taskClock.value.key = key;
    taskClock.value.state = true;
    const taskitemdom = document.querySelectorAll(".taskitem");
    taskitemdom[taskClock.value.key].style = " background: rgb(252, 252, 124);";
    showMessage("设定专注任务成功--" + todayTask.value[key].taskTitle);
    e.target.children[0].style = "fill:red";
    // 默认三分钟一次
    timerID = setInterval(() => {
      new Notification("我诞生于灼烧bug的火焰中", {
        dir: "ltr",
        lang: "zh-CN",
        body: "想要做的太多,会的又太少,不要摸鱼了!时间不等人哩!",
        icon:
          "https://file.fishpi.cn/2022/07/MOSHED2022621164630-1b1ec532.gif?imageView2/1/w/210/h/210/interlace/0/q/100",
      });
      // 300000
    }, 180000);
  });
};

上述代码包含一个浏览器调用系统通知的实现,部分代码来自百度。

317dc7d1865f94816b5b012b8eadbeb.png

3.其他问题

当正式完成这个功能后,由于界面样式的一些缘故,在某些尺寸下,这个任务表会遮挡其他dom。所以必须做一下适配。

因为我个人很喜欢用鼠标滚轮来调整网页的大小缩放,来获取当前最舒服的显示感觉,而某些比例下可能dom之间会有影响。

由于css的媒体查询做起来感觉比较麻烦,所以这里我直接用js处理了。

import { calcluateRenderStrategy } from "@/class/view/adpater/aimwork.js";

var _wr = function (type) {
  var orig = history[type];
  return function () {
    var rv = orig.apply(this, arguments);
    var e = new Event(type);
    e.arguments = arguments;
    window.dispatchEvent(e);
    return rv;
  };
};
history.pushState = _wr("pushState");
history.replaceState = _wr("replaceState");

//  作者:luojian001
//  链接:https://juejin.cn/post/6844903749421367303
//  来源:稀土掘金
//  著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

let taskboxdom = {};
window.onload = function () {
  // 所有资源加载完成后的代码逻辑
  taskboxdom = document.querySelector(".aimwork");
  window.addEventListener("resize", handleViewportChange);

  window.addEventListener("replaceState", function (event) {
    setTimeout(() => {
      handleViewportChange();
    }, 100);
  });
};

function handleViewportChange() {
  // 在这里处理视口变化的逻辑
  const viewportWidth = window.innerWidth;
  const titlePart = getTitlePartOfUrl();
  try {
    calcluateRenderStrategy(titlePart, taskboxdom);
  } catch (error) {
    taskboxdom.style = "left:-200px";
  }
}

function getTitlePartOfUrl() {
  return window.location.href.split("/")[4];
}
const renderStrategy = {
  log: function (dom) {
    const viewportWidth = window.innerWidth;
    if (viewportWidth < 1300) {
      dom.style = "left:-200px";
    } else {
      dom.style = "";
    }
    return 1;
  },
  home: (dom) => {
    dom.style = "";
    return 0;
  },
};

export const calcluateRenderStrategy = function (level, dom) {
  return renderStrategy[level](dom);
};

ok,这样就差不多了,这样在不同页面下,面对不同的dom视觉冲突可以做出不同的处理,非常好用。

仓库地址:stillwarter/startnode: How did I start nodejs as an noob (github.com)

剩余的年度任务等摸鱼的时候做吧。