aboutsummaryrefslogtreecommitdiffstats
path: root/tiddlers/plugin-task-board-widget.tid
diff options
context:
space:
mode:
authorshu <nvcsadjkojf@outlook.com>2026-07-28 19:16:16 +0800
committershu <nvcsadjkojf@outlook.com>2026-07-28 19:16:16 +0800
commit8efba22ed34b6eeb28b5f410177e5a1545b5d798 (patch)
treeb4e751ec9a0940659bc50651974e96c57f6a66d8 /tiddlers/plugin-task-board-widget.tid
parent1e2031e8850fa91ed9e3b5cd11f7a5b01bbd999e (diff)
用 Org GTD 工作流替换事情看板
注册 text/x-org 内容类型和 CodeMirror 编辑器,支持 TODO 状态流转、层级调整、子树移动与折叠。 新增 Org 文档解析、Agenda 日期分组、源任务定位、侧栏入口和响应式样式,并补充中文使用说明。 快捷键改为仅在 Org 编辑器中按配置加载,默认不绑定;无效 JSON 或键名只禁用自定义键位,不影响编辑器启动。 移除旧事情看板的字段模板、组件、样式和默认入口,统一使用 Org GTD 工作流。
Diffstat (limited to 'tiddlers/plugin-task-board-widget.tid')
-rw-r--r--tiddlers/plugin-task-board-widget.tid729
1 files changed, 0 insertions, 729 deletions
diff --git a/tiddlers/plugin-task-board-widget.tid b/tiddlers/plugin-task-board-widget.tid
deleted file mode 100644
index 397811b..0000000
--- a/tiddlers/plugin-task-board-widget.tid
+++ /dev/null
@@ -1,729 +0,0 @@
-title: $:/plugins/local/task-board/widget.js
-type: application/javascript
-module-type: widget
-
-/*\
-title: $:/plugins/local/task-board/widget.js
-type: application/javascript
-module-type: widget
-
-Task board with kanban, list, calendar, and Gantt views.
-\*/
-(function() {
-"use strict";
-
-var Widget = require("$:/core/modules/widgets/widget.js").widget,
- VIEW_STATE_TITLE = "$:/state/task-board/view",
- MONTH_STATE_TITLE = "$:/state/task-board/month",
- DAY_MS = 24 * 60 * 60 * 1000,
- DAY_WIDTH = 30,
- STATUSES = ["待处理","进行中","等待","完成"],
- PRIORITIES = ["P1","P2","P3"],
- VIEW_LABELS = {
- board: "看板",
- list: "列表",
- calendar: "日历",
- gantt: "甘特图"
- };
-
-var TaskBoardWidget = function(parseTreeNode,options) {
- this.initialise(parseTreeNode,options);
-};
-
-TaskBoardWidget.prototype = new Widget();
-TaskBoardWidget.prototype.constructor = TaskBoardWidget;
-
-function pad(value) {
- return value < 10 ? "0" + value : "" + value;
-}
-
-function todayKey() {
- var now = new Date();
- return now.getFullYear() + "-" + pad(now.getMonth() + 1) + "-" + pad(now.getDate());
-}
-
-function monthKey(date) {
- return date.getUTCFullYear() + "-" + pad(date.getUTCMonth() + 1);
-}
-
-function parseDate(value) {
- var match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value || ""),
- date;
- if(!match) {
- return null;
- }
- date = new Date(0);
- date.setUTCHours(0,0,0,0);
- date.setUTCFullYear(Number(match[1]),Number(match[2]) - 1,Number(match[3]));
- if(date.getUTCFullYear() !== Number(match[1]) || date.getUTCMonth() !== Number(match[2]) - 1 || date.getUTCDate() !== Number(match[3])) {
- return null;
- }
- return date;
-}
-
-function dateKey(date) {
- return date.getUTCFullYear() + "-" + pad(date.getUTCMonth() + 1) + "-" + pad(date.getUTCDate());
-}
-
-function dayNumber(date) {
- return Math.floor(date.getTime() / DAY_MS);
-}
-
-function addDays(date,amount) {
- return new Date(date.getTime() + amount * DAY_MS);
-}
-
-function isValidMonth(value) {
- var match = /^(\d{4})-(\d{2})$/.exec(value || "");
- return !!match && Number(match[2]) >= 1 && Number(match[2]) <= 12;
-}
-
-function priorityRank(priority) {
- var rank = PRIORITIES.indexOf(priority);
- return rank === -1 ? PRIORITIES.length : rank;
-}
-
-function compareTasks(left,right) {
- var priorityDifference = priorityRank(left.effectivePriority) - priorityRank(right.effectivePriority);
- if(priorityDifference) {
- return priorityDifference;
- }
- var leftDue = left.dueDate ? dayNumber(left.dueDate) : Infinity,
- rightDue = right.dueDate ? dayNumber(right.dueDate) : Infinity;
- if(leftDue !== rightDue) {
- return leftDue - rightDue;
- }
- return left.title.localeCompare(right.title);
-}
-
-TaskBoardWidget.prototype.render = function(parent,nextSibling) {
- this.parentDomNode = parent;
- this.computeAttributes();
- this.execute();
-
- this.containerNode = this.document.createElement("div");
- this.containerNode.className = "tc-task-board";
- parent.insertBefore(this.containerNode,nextSibling);
- this.domNodes.push(this.containerNode);
- this.renderBoard();
-};
-
-TaskBoardWidget.prototype.execute = function() {
- this.tag = this.getAttribute("tag","事情");
-};
-
-TaskBoardWidget.prototype.createElement = function(tagName,className,text) {
- var node = this.document.createElement(tagName);
- if(className) {
- node.className = className;
- }
- if(text !== undefined && text !== null) {
- node.textContent = text;
- }
- return node;
-};
-
-TaskBoardWidget.prototype.createButton = function(label,className,handler) {
- var button = this.createElement("button",className,label);
- button.setAttribute("type","button");
- button.addEventListener("click",handler);
- return button;
-};
-
-TaskBoardWidget.prototype.setState = function(title,value) {
- this.wiki.addTiddler({title: title,text: value});
-};
-
-TaskBoardWidget.prototype.getView = function() {
- var view = this.wiki.getTiddlerText(VIEW_STATE_TITLE,"board");
- return VIEW_LABELS[view] ? view : "board";
-};
-
-TaskBoardWidget.prototype.getMonth = function() {
- var value = this.wiki.getTiddlerText(MONTH_STATE_TITLE,"");
- if(isValidMonth(value)) {
- return value;
- }
- var today = parseDate(todayKey());
- return monthKey(today);
-};
-
-TaskBoardWidget.prototype.getTasks = function() {
- var self = this,
- tasks = [];
-
- this.wiki.getTiddlersWithTag(this.tag).forEach(function(title) {
- var tiddler = self.wiki.getTiddler(title);
- if(!tiddler || tiddler.fields["draft.of"]) {
- return;
- }
- var fields = tiddler.fields,
- start = fields.start || "",
- end = fields.end || "",
- due = fields.due || "",
- startDate = parseDate(start),
- endDate = parseDate(end),
- dueDate = parseDate(due),
- issues = [],
- rangeError,
- dateError,
- calendarDates = [];
-
- if(start && !startDate) {
- issues.push("开始日期无效");
- }
- if(end && !endDate) {
- issues.push("结束日期无效");
- }
- if(due && !dueDate) {
- issues.push("截止日期无效");
- }
- if(startDate && endDate && endDate < startDate) {
- issues.push("结束日期早于开始日期");
- }
- dateError = !!((start && !startDate) || (end && !endDate) || (due && !dueDate));
- rangeError = !!((start && !startDate) || (end && !endDate) || (startDate && endDate && endDate < startDate));
- [
- {type: "start",label: "开始",date: startDate},
- {type: "end",label: "结束",date: endDate},
- {type: "due",label: "截止",date: dueDate}
- ].forEach(function(item) {
- if(item.date) {
- calendarDates.push(item);
- }
- });
- calendarDates.sort(function(left,right) {
- return dayNumber(left.date) - dayNumber(right.date);
- });
- if(STATUSES.indexOf(fields.status) === -1) {
- issues.push("状态无效");
- }
- if(PRIORITIES.indexOf(fields.priority) === -1) {
- issues.push("优先级无效");
- }
-
- tasks.push({
- title: title,
- status: fields.status || "",
- effectiveStatus: STATUSES.indexOf(fields.status) === -1 ? STATUSES[0] : fields.status,
- priority: fields.priority || "",
- effectivePriority: PRIORITIES.indexOf(fields.priority) === -1 ? "P2" : fields.priority,
- start: start,
- end: end,
- due: due,
- startDate: startDate,
- endDate: endDate,
- dueDate: dueDate,
- rangeValid: !!startDate && !rangeError,
- rangeError: rangeError,
- dateError: dateError,
- calendarDates: calendarDates,
- tags: (fields.tags || []).filter(function(tag) {return tag !== self.tag;}),
- issues: issues
- });
- });
-
- return tasks.sort(compareTasks);
-};
-
-TaskBoardWidget.prototype.navigateTo = function(title,node,event) {
- var bounds = node.getBoundingClientRect();
- this.dispatchEvent({
- type: "tm-navigate",
- navigateTo: title,
- navigateFromTitle: this.getVariable("storyTiddler"),
- navigateFromNode: this,
- navigateFromClientRect: {
- top: bounds.top,
- left: bounds.left,
- width: bounds.width,
- right: bounds.right,
- bottom: bounds.bottom,
- height: bounds.height
- },
- navigateSuppressNavigation: event && (event.metaKey || event.ctrlKey || event.button === 1)
- });
-};
-
-TaskBoardWidget.prototype.createTaskLink = function(task,className,label) {
- var self = this,
- button = this.createButton(label !== undefined ? label : task.title,"tc-btn-invisible " + className,function(event) {
- self.navigateTo(task.title,event.currentTarget,event);
- });
- button.title = "打开事情:" + task.title;
- button.setAttribute("aria-label","打开事情:" + task.title);
- return button;
-};
-
-TaskBoardWidget.prototype.renderBoard = function() {
- var tasks = this.getTasks(),
- view = this.getView();
- this.renderToolbar(view);
- if(view === "list") {
- this.renderList(tasks);
- } else if(view === "calendar") {
- this.renderCalendar(tasks);
- } else if(view === "gantt") {
- this.renderGantt(tasks);
- } else {
- this.renderKanban(tasks);
- }
-};
-
-TaskBoardWidget.prototype.renderToolbar = function(currentView) {
- var self = this,
- toolbar = this.createElement("div","tc-task-board-toolbar"),
- switcher = this.createElement("div","tc-task-board-switcher");
-
- toolbar.appendChild(this.createButton("+ 新建事情","tc-btn-invisible tc-task-board-new",function() {
- self.dispatchEvent({
- type: "tm-new-tiddler",
- paramObject: {
- tags: $tw.utils.stringifyList([self.tag]),
- priority: "P2",
- status: "待处理",
- start: todayKey()
- },
- navigateFromTitle: self.getVariable("storyTiddler")
- });
- }));
-
- Object.keys(VIEW_LABELS).forEach(function(view) {
- var button = self.createButton(VIEW_LABELS[view],"tc-btn-invisible tc-task-board-view-button" + (view === currentView ? " tc-selected" : ""),function() {
- self.setState(VIEW_STATE_TITLE,view);
- });
- button.setAttribute("aria-pressed",view === currentView ? "true" : "false");
- switcher.appendChild(button);
- });
- toolbar.appendChild(switcher);
- this.containerNode.appendChild(toolbar);
-};
-
-TaskBoardWidget.prototype.renderPriority = function(task) {
- var badge = this.createElement("span","tc-task-priority tc-task-priority-" + task.effectivePriority.toLowerCase(),task.priority || "未设置");
- badge.title = "优先级";
- return badge;
-};
-
-TaskBoardWidget.prototype.renderIssues = function(task,parent) {
- if(task.issues.length) {
- var issue = this.createElement("span","tc-task-issues","⚠ " + task.issues.join(";"));
- issue.title = task.issues.join(";");
- parent.appendChild(issue);
- }
-};
-
-TaskBoardWidget.prototype.renderTaskDates = function(task,parent) {
- var values = [];
- if(task.start) {values.push("开始 " + task.start);}
- if(task.end) {values.push("结束 " + task.end);}
- if(task.due) {values.push("截止 " + task.due);}
- parent.appendChild(this.createElement("div","tc-task-card-dates",values.length ? values.join(" · ") : "未排期"));
-};
-
-TaskBoardWidget.prototype.updateTaskStatus = function(title,status) {
- var tiddler = this.wiki.getTiddler(title);
- if(tiddler && !tiddler.fields["draft.of"] && (tiddler.fields.tags || []).indexOf(this.tag) !== -1 && STATUSES.indexOf(status) !== -1) {
- this.wiki.addTiddler(new $tw.Tiddler(tiddler,{status: status},this.wiki.getModificationFields()));
- }
-};
-
-TaskBoardWidget.prototype.renderKanban = function(tasks) {
- var self = this,
- board = this.createElement("div","tc-task-kanban");
-
- STATUSES.forEach(function(status) {
- var column = self.createElement("section","tc-task-kanban-column tc-task-status-" + STATUSES.indexOf(status)),
- columnTasks = tasks.filter(function(task) {return task.effectiveStatus === status;}),
- heading = self.createElement("h2","tc-task-kanban-heading",status + " · " + columnTasks.length),
- cards = self.createElement("div","tc-task-kanban-cards");
-
- column.setAttribute("data-task-status",status);
- column.addEventListener("dragover",function(event) {
- event.preventDefault();
- column.classList.add("tc-drag-over");
- });
- column.addEventListener("dragleave",function() {
- column.classList.remove("tc-drag-over");
- });
- column.addEventListener("drop",function(event) {
- event.preventDefault();
- column.classList.remove("tc-drag-over");
- var title = self.draggedTaskTitle;
- if(title) {
- self.updateTaskStatus(title,status);
- }
- self.draggedTaskTitle = null;
- });
-
- column.appendChild(heading);
- columnTasks.forEach(function(task) {
- var card = self.createElement("article","tc-task-card");
- card.setAttribute("draggable","true");
- card.setAttribute("data-task-title",task.title);
- card.addEventListener("dragstart",function(event) {
- self.draggedTaskTitle = task.title;
- if(event.dataTransfer) {
- event.dataTransfer.setData("application/x-tiddlywiki-task",task.title);
- event.dataTransfer.effectAllowed = "move";
- }
- card.classList.add("tc-dragging");
- });
- card.addEventListener("dragend",function() {
- self.draggedTaskTitle = null;
- card.classList.remove("tc-dragging");
- });
- var cardTop = self.createElement("div","tc-task-card-top");
- cardTop.appendChild(self.createTaskLink(task,"tc-task-card-title"));
- cardTop.appendChild(self.renderPriority(task));
- card.appendChild(cardTop);
- self.renderTaskDates(task,card);
- if(task.tags.length) {
- card.appendChild(self.createElement("div","tc-task-card-tags",task.tags.join(" · ")));
- }
- self.renderIssues(task,card);
- cards.appendChild(card);
- });
- if(!columnTasks.length) {
- cards.appendChild(self.createElement("div","tc-task-empty-column","暂无事情"));
- }
- column.appendChild(cards);
- board.appendChild(column);
- });
- this.containerNode.appendChild(board);
-};
-
-TaskBoardWidget.prototype.renderList = function(tasks) {
- var self = this,
- wrapper = this.createElement("div","tc-task-list-wrapper"),
- table = this.createElement("table","tc-task-list"),
- thead = this.createElement("thead"),
- headerRow = this.createElement("tr"),
- tbody = this.createElement("tbody");
-
- ["事情","优先级","状态","开始","结束","截止"].forEach(function(label) {
- headerRow.appendChild(self.createElement("th","",label));
- });
- thead.appendChild(headerRow);
- table.appendChild(thead);
-
- tasks.forEach(function(task) {
- var row = self.createElement("tr","tc-task-list-row" + (task.issues.length ? " tc-task-invalid" : "")),
- titleCell = self.createElement("td","tc-task-list-title");
- titleCell.setAttribute("data-label","事情");
- titleCell.appendChild(self.createTaskLink(task,"tc-task-list-link"));
- self.renderIssues(task,titleCell);
- row.appendChild(titleCell);
- [
- ["优先级",task.priority || "-"],
- ["状态",task.status || "-"],
- ["开始",task.start || "-"],
- ["结束",task.end || "-"],
- ["截止",task.due || "-"]
- ].forEach(function(item) {
- var cell = self.createElement("td","",item[1]);
- cell.setAttribute("data-label",item[0]);
- row.appendChild(cell);
- });
- tbody.appendChild(row);
- });
-
- if(!tasks.length) {
- var emptyRow = this.createElement("tr"),
- emptyCell = this.createElement("td","tc-task-empty","还没有事情");
- emptyCell.setAttribute("colspan","6");
- emptyRow.appendChild(emptyCell);
- tbody.appendChild(emptyRow);
- }
-
- table.appendChild(tbody);
- wrapper.appendChild(table);
- this.containerNode.appendChild(wrapper);
-};
-
-TaskBoardWidget.prototype.renderMonthControls = function(value) {
- var self = this,
- controls = this.createElement("div","tc-task-calendar-controls"),
- parts = value.split("-"),
- year = Number(parts[0]),
- month = Number(parts[1]);
-
- function move(offset) {
- self.setState(MONTH_STATE_TITLE,monthKey(new Date(Date.UTC(year,month - 1 + offset,1))));
- }
-
- controls.appendChild(this.createButton("上个月","tc-btn-invisible tc-task-month-button",function() {move(-1);}));
- controls.appendChild(this.createElement("h2","tc-task-calendar-title",year + "年" + month + "月"));
- controls.appendChild(this.createButton("本月","tc-btn-invisible tc-task-month-button",function() {
- self.setState(MONTH_STATE_TITLE,monthKey(parseDate(todayKey())));
- }));
- controls.appendChild(this.createButton("下个月","tc-btn-invisible tc-task-month-button",function() {move(1);}));
- this.containerNode.appendChild(controls);
-};
-
-TaskBoardWidget.prototype.renderCalendar = function(tasks) {
- var self = this,
- value = this.getMonth(),
- parts = value.split("-"),
- year = Number(parts[0]),
- month = Number(parts[1]),
- firstDate = new Date(Date.UTC(year,month - 1,1)),
- firstWeekday = (firstDate.getUTCDay() + 6) % 7,
- daysInMonth = new Date(Date.UTC(year,month,0)).getUTCDate(),
- calendar = this.createElement("div","tc-task-calendar-grid"),
- header = this.createElement("div","tc-task-calendar-header"),
- weeks = this.createElement("div","tc-task-calendar-weeks"),
- weekdays = ["一","二","三","四","五","六","日"],
- unscheduled = tasks.filter(function(task) {
- return !task.calendarDates.length || task.dateError || task.rangeError;
- }),
- today = todayKey(),
- monthStart = firstDate,
- monthEnd = new Date(Date.UTC(year,month - 1,daysInMonth)),
- calendarStart = addDays(firstDate,-firstWeekday),
- weekCount = Math.ceil((firstWeekday + daysInMonth) / 7);
-
- this.renderMonthControls(value);
- calendar.setAttribute("role","grid");
- header.setAttribute("role","row");
- weeks.setAttribute("role","rowgroup");
- weekdays.forEach(function(weekday) {
- var heading = self.createElement("div","tc-task-calendar-weekday",weekday);
- heading.setAttribute("role","columnheader");
- header.appendChild(heading);
- });
- calendar.appendChild(header);
-
- for(var weekIndex = 0; weekIndex < weekCount; weekIndex++) {
- var weekStart = addDays(calendarStart,weekIndex * 7),
- weekEnd = addDays(weekStart,6),
- week = this.createElement("div","tc-task-calendar-week"),
- items = [],
- lanes = [];
- week.setAttribute("role","row");
-
- tasks.forEach(function(task) {
- if(task.calendarDates.length) {
- var firstCalendarDate = task.calendarDates[0].date,
- lastCalendarDate = task.calendarDates[task.calendarDates.length - 1].date,
- segmentStart = new Date(Math.max(firstCalendarDate.getTime(),monthStart.getTime(),weekStart.getTime())),
- segmentEnd = new Date(Math.min(lastCalendarDate.getTime(),monthEnd.getTime(),weekEnd.getTime()));
- if(segmentStart <= segmentEnd) {
- items.push({
- task: task,
- type: task.calendarDates.length === 1 ? "point" : "range",
- start: dayNumber(segmentStart) - dayNumber(weekStart) + 1,
- end: dayNumber(segmentEnd) - dayNumber(weekStart) + 1,
- starts: dateKey(segmentStart) === dateKey(firstCalendarDate),
- ends: dateKey(segmentEnd) === dateKey(lastCalendarDate),
- dateLabel: task.calendarDates[0].label
- });
- }
- if(task.calendarDates.length === 3) {
- var middle = task.calendarDates[1];
- if(middle.date >= monthStart && middle.date <= monthEnd && middle.date >= weekStart && middle.date <= weekEnd) {
- var middleColumn = dayNumber(middle.date) - dayNumber(weekStart) + 1;
- items.push({task: task,type: "milestone",start: middleColumn,end: middleColumn,dateLabel: middle.label});
- }
- }
- }
- });
-
- items.forEach(function(item) {
- var lane = 0;
- while(lanes[lane] && lanes[lane].some(function(occupied) {
- return item.start <= occupied.end && item.end >= occupied.start;
- })) {
- lane++;
- }
- lanes[lane] = lanes[lane] || [];
- lanes[lane].push(item);
- item.lane = lane;
- });
-
- week.style.gridTemplateRows = "minmax(2.25rem,1fr) repeat(" + lanes.length + ",1.5rem)";
- for(var column = 0; column < 7; column++) {
- var cellDate = addDays(weekStart,column),
- inMonth = cellDate >= monthStart && cellDate <= monthEnd,
- key = dateKey(cellDate),
- cell = self.createElement("div","tc-task-calendar-day" + (!inMonth ? " tc-task-calendar-blank" : "") + (key === today ? " tc-task-calendar-today" : ""));
- cell.style.gridColumn = (column + 1) + "";
- cell.style.gridRow = "1 / span " + (lanes.length + 1);
- cell.setAttribute("role","gridcell");
- cell.setAttribute("aria-label",key);
- if(inMonth) {
- cell.appendChild(self.createElement("div","tc-task-calendar-day-number",cellDate.getUTCDate()));
- }
- week.appendChild(cell);
- }
-
- items.forEach(function(item) {
- var className = item.type === "range" ? "tc-task-calendar-range" + (item.starts ? " tc-range-start" : "") + (item.ends ? " tc-range-end" : "") : item.type === "point" ? "tc-task-calendar-point" : "tc-task-calendar-milestone",
- label = item.type === "point" || item.type === "milestone" ? item.dateLabel + " · " + item.task.title : item.task.title,
- entry = self.createTaskLink(item.task,className,label);
- entry.style.gridColumn = item.start + " / " + (item.end + 1);
- entry.style.gridRow = (item.lane + 2) + "";
- entry.setAttribute("data-priority",item.task.effectivePriority);
- week.appendChild(entry);
- });
- weeks.appendChild(week);
- }
-
- calendar.appendChild(weeks);
- this.containerNode.appendChild(calendar);
- this.renderUnscheduled(unscheduled,"未排期或日期异常");
-};
-
-TaskBoardWidget.prototype.renderUnscheduled = function(tasks,label) {
- var self = this;
- if(!tasks.length) {
- return;
- }
- var section = this.createElement("section","tc-task-unscheduled"),
- list = this.createElement("div","tc-task-unscheduled-list");
- section.appendChild(this.createElement("h3","",label + " · " + tasks.length));
- tasks.forEach(function(task) {
- list.appendChild(self.createTaskLink(task,"tc-task-unscheduled-link"));
- });
- section.appendChild(list);
- this.containerNode.appendChild(section);
-};
-
-TaskBoardWidget.prototype.renderGantt = function(tasks) {
- var self = this,
- datedTasks = tasks.filter(function(task) {return task.rangeValid || (!task.start && task.endDate) || task.dueDate;}),
- unscheduled = tasks.filter(function(task) {return task.rangeError || (!task.rangeValid && !(!task.start && task.endDate) && !task.dueDate);}),
- dates = [];
-
- datedTasks.forEach(function(task) {
- var renderedDates = [];
- if(task.rangeValid) {
- renderedDates.push(task.startDate,task.endDate || task.startDate);
- } else if(!task.start && task.endDate) {
- renderedDates.push(task.endDate);
- }
- if(task.dueDate) {
- renderedDates.push(task.dueDate);
- }
- renderedDates.forEach(function(date) {
- if(date) {dates.push(dayNumber(date));}
- });
- });
- if(!dates.length) {
- this.containerNode.appendChild(this.createElement("div","tc-task-empty","没有可绘制的日期"));
- this.renderUnscheduled(unscheduled,"未排期事情");
- return;
- }
-
- var minimum = Math.min.apply(Math,dates) - 2,
- maximum = Math.max.apply(Math,dates) + 2,
- span = maximum - minimum + 1,
- step = Math.max(1,Math.ceil(span / 366)),
- units = Math.ceil(span / step),
- labelReserve = Math.max.apply(Math,datedTasks.map(function(task) {
- return task.title.length * 14 + 32;
- }).concat([180])),
- startDate = new Date(minimum * DAY_MS),
- wrapper = this.createElement("div","tc-task-gantt-scroll"),
- gantt = this.createElement("div","tc-task-gantt"),
- header = this.createElement("div","tc-task-gantt-row tc-task-gantt-header"),
- headerLabel = this.createElement("div","tc-task-gantt-label","事情"),
- headerTimeline = this.createElement("div","tc-task-gantt-timeline tc-task-gantt-header-timeline"),
- monthTrack = this.createElement("div","tc-task-gantt-months"),
- dayTrack = this.createElement("div","tc-task-gantt-days"),
- monthGroups = [],
- today = parseDate(todayKey()),
- todayOffset = (dayNumber(today) - minimum) / step;
-
- gantt.style.setProperty("--task-gantt-width",units * DAY_WIDTH + labelReserve + "px");
- gantt.style.setProperty("--task-gantt-date-width",units * DAY_WIDTH + "px");
- header.appendChild(headerLabel);
- for(var index = 0; index < units; index++) {
- var date = addDays(startDate,index * step),
- key = monthKey(date),
- group = monthGroups[monthGroups.length - 1],
- day = this.createElement("div","tc-task-gantt-day",pad(date.getUTCDate()));
- if(!group || group.key !== key) {
- group = {key: key,date: date,count: 0};
- monthGroups.push(group);
- }
- group.count++;
- day.style.width = DAY_WIDTH + "px";
- day.setAttribute("title",dateKey(date) + (step > 1 ? " 起 " + step + " 天" : ""));
- day.setAttribute("aria-label",dateKey(date) + (step > 1 ? " 起 " + step + " 天" : ""));
- dayTrack.appendChild(day);
- }
- monthGroups.forEach(function(group) {
- var segment = self.createElement("div","tc-task-gantt-month-segment"),
- label = self.createElement("span","tc-task-gantt-month-label",group.date.getUTCFullYear() + "年" + (group.date.getUTCMonth() + 1) + "月");
- segment.style.width = group.count * DAY_WIDTH + "px";
- segment.setAttribute("title",label.textContent);
- segment.appendChild(label);
- monthTrack.appendChild(segment);
- });
- headerTimeline.appendChild(monthTrack);
- headerTimeline.appendChild(dayTrack);
- header.appendChild(headerTimeline);
- gantt.appendChild(header);
-
- datedTasks.forEach(function(task) {
- var row = self.createElement("div","tc-task-gantt-row"),
- label = self.createElement("div","tc-task-gantt-label"),
- timeline = self.createElement("div","tc-task-gantt-timeline"),
- barStart = task.startDate,
- barEnd = task.rangeValid ? (task.endDate || task.startDate) : null;
- label.appendChild(self.createTaskLink(task,"tc-task-gantt-task-link"));
- label.appendChild(self.renderPriority(task));
- self.renderIssues(task,label);
-
- if(barStart && barEnd) {
- var left = (dayNumber(barStart) - minimum) / step * DAY_WIDTH,
- width = (dayNumber(barEnd) - dayNumber(barStart) + 1) / step * DAY_WIDTH,
- barWidth = Math.max(width,DAY_WIDTH),
- bar = self.createTaskLink(task,"tc-task-gantt-bar",""),
- barLabel = self.createTaskLink(task,"tc-task-gantt-bar-label");
- bar.style.left = left + "px";
- bar.style.width = barWidth + "px";
- bar.setAttribute("data-priority",task.effectivePriority);
- barLabel.style.left = (left + barWidth + 8) + "px";
- timeline.appendChild(bar);
- timeline.appendChild(barLabel);
- } else if(!task.start && task.endDate) {
- var endMarker = self.createTaskLink(task,"tc-task-gantt-end","◆");
- endMarker.style.left = ((dayNumber(task.endDate) - minimum) / step * DAY_WIDTH + DAY_WIDTH / 2) + "px";
- endMarker.title = "计划结束:" + task.end;
- timeline.appendChild(endMarker);
- }
- if(task.dueDate) {
- var dueMarker = self.createTaskLink(task,"tc-task-gantt-due","◆");
- dueMarker.style.left = ((dayNumber(task.dueDate) - minimum) / step * DAY_WIDTH + DAY_WIDTH / 2) + "px";
- dueMarker.title = "截止:" + task.due;
- timeline.appendChild(dueMarker);
- }
- if(todayOffset >= 0 && todayOffset < units) {
- var todayLine = self.createElement("span","tc-task-gantt-today-line");
- todayLine.style.left = (todayOffset * DAY_WIDTH + DAY_WIDTH / 2) + "px";
- timeline.appendChild(todayLine);
- }
- row.appendChild(label);
- row.appendChild(timeline);
- gantt.appendChild(row);
- });
-
- wrapper.appendChild(gantt);
- this.containerNode.appendChild(wrapper);
- this.renderUnscheduled(unscheduled,"未排期事情");
-};
-
-TaskBoardWidget.prototype.refresh = function(changedTiddlers) {
- var changedAttributes = this.computeAttributes();
- if(Object.keys(changedAttributes).length || Object.keys(changedTiddlers).length) {
- this.refreshSelf();
- return true;
- }
- return false;
-};
-
-exports["task-board"] = TaskBoardWidget;
-
-})();