在每日日记显示当天的完成的任务

在每日日记显示当天的完成的任务

代码

利用了 dv.view() 展示

2024-09-09

```dataviewjs
await dv.view("taskToday",{})
```

只要文件夹的名字是 taskToday就行

视频讲解

\taskToday\view.js

// Obsidian view template for displaying today's tasks
let { pages, date, showParent = true } = input

// Error Handling
if (!date && !dv.date(date) && !dv.current().file.day) {
  dv.span(
    '> [!ERROR] Missing date \n \
    > \n \
    > Please set the pages name include the date in the format of `YYYY-MM-DD` in the file name. \n \
    > \n \
    > Or set the date in the `{date: YYYY-MM-DD}` \
    ',
  )
  return false
}

const dv_current_day = dv.current().file.day ?? dv.date(date)

const calDay = (d, f = 'YYYY-MM-DD') => {
  return moment(dv_current_day.plus({ days: d }).toString()).format(f)
}

// default values

if (!pages) {
  pages = '3-Tasks'
}
if (!date) {
  date = calDay(0)
  console.log(date)
}

// const

const taskIcon = {
  todo: '📝',
  done: '✅',
  due: '📅',
  scheduled: '⏳',
  recurrence: '🔁',
  overdue: '⚠️',
  process: '⏺️',
  cancelled: '🚫',
  start: '🛫',
  dailyNote: '📄',
}

const fieldNames = ['due', 'scheduled', 'created', 'start', 'completion']

// 添加判断条件,请使用互斥条件,即只能有一个条件为真,虽然后续代码一个为真后会跳出循环
const times = {
  全天: (task, field) => task[field].c.hour == 0 && task[field].c.minute == 0,
  上午: (task, field) =>
    task[field].c.hour < 12 &&
    !(task[field].c.hour == 0 && task[field].c.minute == 0),
  下午: (task, field) => task[field].c.hour >= 12 && task[field].c.hour < 18,
  晚上: (task, field) => task[field].c.hour >= 18,
}

const extendFields = ['priority', 'repeat']

const isOneDay = (a, b) => {
  if (!a || !b) return false
  if (typeof a == 'string') a = dv.date(a)
  if (typeof b == 'string') b = dv.date(b)
  if (!a || !b) return false
  if (!('c' in a) || !('c' in b)) return false
  // console.log(a, b)
  if (a.c.year == b.c.year && a.c.month == b.c.month && a.c.day == b.c.day)
    return true
  else return false
}

const getData = (pages, date) => {
  let todayTasks = {}

  const taskAll = dv.pages('"' + pages + '"').file.tasks

  taskAll.forEach((t) => {
    // dateview 的 annotated 为 true 表示有元数据
    if (t.annotated) {
      // 每个字段都匹配一下
      for (const field of fieldNames) {
        if (field in t && isOneDay(t[field], dv.date(date))) {
          // 分配到时间段
          for (let time in times) {
            // 判断是否符合时间段
            if (times[time](t, field)) {
              if (!(time in todayTasks)) {
                todayTasks[time] = {}
              }
              if (!(field in todayTasks[time])) {
                todayTasks[time][field] = []
              }
              todayTasks[time][field].push(t)

              break
            }
          }
        }
      }
    }
  })
  console.log(todayTasks)
  return todayTasks
}
const priorityColorMap = {
  low: 'rgb(55 166 155)',
  medium: 'orange',
  high: 'red',
}

// regex to remove the field priority in text
const regex = /\[priority[^\]]+\]/g

const taskVisual = (t, field) => {
  let icon = ''
  // 有icon的可以显示icon
  field.forEach((f) => {
    if (f in taskIcon) {
      icon += taskIcon[f]
    }
  })
  // 在前方显示一条颜色线区分优先级
  t.visual =
    icon +
    ' ' +
    getColorCode(t?.priority) +
    t.text.replace(/\[[^\]]*::[^\]]*\]/g, '').trim()
  const meta = dv.page(t.path)
  const project = meta.project ?? meta.file.name

  if (showParent && t.parent) {
    const parentTask = meta.file.tasks.find((k) => k.line == t.parent)

    // 有可能parentTask不是任务,而是列表
    if (parentTask) {
      parentTask.subtasks.forEach((k) => {
        k.visual = k.text.replace(/\[[^\]]*::[^\]]*\]/g, '').trim()
      })

      parentTask.subtasks[
        parentTask.subtasks.findIndex((k) => k.line == t.line)
      ] = t
      t = parentTask
    }
  }
  const el = dv.el('div', '')
  dv.api.taskList([t], false, el, dv.component)
  return [field, el, project]
}

function listId(item) {
  return item.path + ':' + item.line
}

function parentListId(item) {
  return item.path + ':' + item.parent
}

const todayTaskTable = (taskLists) => {
  if (Object.keys(taskLists).length == 0) return

  // 保存显示的table的内容
  const tdata = []

  // todo 把每个时间段的任务合并

  for (let time in taskLists) {
    // 保存每个时间段内的任务 和 字段
    const taskTime = new Map()

    for (let field in taskLists[time]) {
      for (let task of taskLists[time][field]) {
        const listItem = listId(task)

        if (taskTime.has(listItem)) {
          taskTime.get(listItem).push(field)
        } else {
          taskTime.set(listItem, [field])
        }
      }
    }
    console.log('taskTime', taskTime)
    // 读取每个时间段内的任务,合并成一个数组
    const totalArr = []
    Object.keys(taskLists[time]).forEach((item) => {
      totalArr.push(...taskLists[time][item])
    })
    for (let [timeItem, times] of taskTime) {
      const task = totalArr.find((item) => listId(item) == timeItem)
      if (task) tdata.push([time, ...taskVisual(task, times)])
    }
  }

  // 排序
  tdata.sort(
    (a, b) =>
      Object.keys(times).indexOf(a[0]) - Object.keys(times).indexOf(b[0]),
  )

  if (tdata.length != 0) {
    dv.header(3, '行云')
    dv.table(['时间', '操作', '任务', '项目'], tdata)
  } else {
    dv.span('> [!] 暂无任务')
  }
}

function getColorCode(priority) {
  const color = priorityColorMap[priority] ?? 'grey'
  return `<span style='border-left: 3px solid ${color};'>&nbsp;</span>`
}

function render() {
  const taskList = getData(pages, date)
  console.log(taskList)

  todayTaskTable(taskList)
  // dv.taskList(taskList, false)
}

render()

1 个赞

哇哦,专业啊,我有个问题,请大佬帮帮忙,我的日记模板,里面会及时添加日程任务,任务分别放在标题“##工作日程”和“##生活日程”下面,文件夹位置是"02-Area领域/03阅读写作/日记" ,我想要通过dataview实现,在新建日记的时候,单独有一个标题“##以前未完成任务”,使其搜索显示此文件夹下面之前未完成的任务,并且还是保持“##工作日程”和“##生活日程”进行显示,请问如何实现。

修改 pages 的值可以改变文件来源

task 的属性有 header 和 section 下都有 subpath 可以区分 不同标题下面的任务,增加一个过滤条件就行了

但是,统计未完成的任务,一般会放在home 或者 首页 的地方吧,因为会不断变化的,完成后就被过滤条件过滤了,(根据日记的时间也能实现,昨天显示没完成,今天显示完成,对task 需要增加属性)

你既在每日日记下面写需要完成的工程任务,其实不太适用这个代码,因为我是任务写在项目里,需要汇总才用这种方式汇总

好的,谢谢,我再试试