Heim > Web-Frontend > js-Tutorial > Hauptteil

So entwickeln Sie mit Javascript einen zweidimensionalen Kalender mit Wochenansicht

亚连
Freigeben: 2018-06-20 16:56:08
Original
1706 Leute haben es durchsucht

Dieser Artikel führt Sie hauptsächlich in die relevanten Informationen zur Verwendung von Javascript zum Entwickeln eines zweidimensionalen Wochenansichtskalenders ein. Der Artikel stellt ihn ausführlich anhand von Beispielcode vor. Er hat einen gewissen Referenz-Lernwert für jedermanns Studium oder Arbeit. Es ist erforderlich, Freunde, bitte folgen Sie dem Herausgeber, um gemeinsam zu lernen.

Vorwort

Dieser Artikel führt Sie in den relevanten Inhalt der Javascript-Entwicklung eines zweidimensionalen Wochenansichtskalenders, also einer Monatsansicht, ein Kalender wurde bereits implementiert. Heute werden wir einen zweidimensionalen Kalender mit Wochenansicht implementieren.

Die wichtigsten Teile werden unten analysiert.

Strukturvorbereitung

Der Unterschied besteht darin, dass es auch eine Klassifizierungsachse basierend auf dem Kalender gibt, um verschiedene Kategorien anzuzeigen. Besprechungsvereinbarungen usw. innerhalb einer Woche.

Der zweidimensionale Kalender unterscheidet sich vom vorherigen Einzelkalender. Beim Wechseln des Datums muss der zweidimensionale Kalender nicht neu gerendert werden, nur das angezeigte Datum muss geändert werden geändert wird.

Und weil es zweidimensional ist, muss der eingefügte Inhalt gleichzeitig zu einer Kategorie und einem Zeitraum gehören. Der Inhalt kann sich durchaus über die Zeit (also die Datumsachse) erstrecken, also der eingefügte Inhalt kann nicht wie der Anfang direkt eingefügt werden Der Kalender wird direkt im Kalenderraster platziert. Aber separat zu verarbeiten.

Solange die Kategorie unverändert bleibt, muss außerdem das aus Daten und Kategorien bestehende Raster nicht neu gezeichnet werden.

In Anbetracht der oben genannten Situation müssen das Einfügen von Inhalt und das Raster getrennt werden, um den fertigen Kalender in einen 3D-Effekt zu verwandeln:

Das heißt, die Ebene zum Einfügen von Inhalten wird separat über dem Raster aus Zeit und Kategorie platziert.

Basierend auf der obigen Analyse erstellen Sie zunächst die folgende Grundstruktur:

<p class="ep-weekcalendar border">
 <!-- 头部 -->
 <p class="ep-weekcalendar-header">
 <p class="ep-weekcalendar-header-left"></p>
 <p class="ep-weekcalendar-header-center">
 <span class="ep-weekcalendar-header-btn ep-weekcalendar-header-btn-prev"></span>
 <span class="ep-weekcalendar-title">2017年12月04日 - 10日</span>
 <span class="ep-weekcalendar-header-btn ep-weekcalendar-header-btn-next"></span>
 </p>
 <p class="ep-weekcalendar-header-right"></p>
 </p>
 <!-- 主体 -->
 <p class="ep-weekcalendar-body">
 <!-- 分类区域 -->
 <p class="ep-weekcalendar-category-area">
 <p class="ep-weekcalendar-category-header">
 <span class="ep-weekcalendar-category-title">车辆</span>
 </p>
 <ul class="ep-weekcalendar-category-list">
 </ul>
 </p>
 <!-- 内容区域 -->
 <p class="ep-weekcalendar-time-area">
 <!-- 每周日期渲染区域。切换日期时重新绘制内容 -->
 <p class="ep-weekcalendar-weeks"></p>
 <p class="ep-weekcalendar-main">
 <!-- 分类和内容构建的网格区域,仅在分类改变时进行调整 -->
 <p class="ep-weekcalendar-grid"> </p>
 <!-- 可插入任意内容的区域,日期切换时清空,根据使用需求插入内容 -->
 <p class="ep-weekcalendar-content"></p>
 </p>
 </p>
 </p>
 <!-- 底部 -->
 <p class="ep-weekcalendar-body"></p>
</p>
Nach dem Login kopieren

Die Struktur ist wie oben und es ist nicht erforderlich, den Implementierungscode anzuzeigen.

Zeichnungsimplementierung

Nachdem wir die notwendige Struktur initialisiert haben, fahren wir mit dem Zeichnen des Kalenders fort.

Kategorie-Zeichnung

Das erste, was man tun muss, ist die Klassifizierung. In der Wochenansicht wird die Anzahl der Tage in einer Woche erst festgelegt, wenn die Klassifizierung festgelegt ist Kann der Hauptteil des Rasters gezeichnet werden?

Berücksichtigen Sie für die Klassifizierung vorübergehend das folgende erforderliche Datenformat:

{
 id: &#39;cate-1&#39;, // 分类ID
 name: &#39;法拉利&#39;, // 分类名称
 content: &#39;苏E00000&#39; // 分类的具体描述
}
Nach dem Login kopieren

Die Implementierung ist wie folgt:

{
 // 设置分类数据
 setCategory: function (data) {
 if (!(data instanceof Array)) {
 this.throwError(&#39;分类数据必须是一个数组&#39;);
 return;
 }
 this._categoryData = data;

 // 绘制分类
 this._renderCatagories();
 // 绘制其他需要改变的部分
 this._renderChanged();
 },
 // 左侧分类渲染
 _renderCatagories: function () {
 this._categoryListEl.innerHTML = &#39;&#39;;
 var i = 0,
 data = this._categoryData,
 node = document.createElement(&#39;li&#39;),
 cataEl;
 node.className = &#39;ep-weekcalendar-category&#39;;
 // 用行作为下标记录当前分类id集合
 this._categoryIndexs = [];
 // id为键记录索引
 this._categoryReocrds = {};
 while (i < data.length) {
 this._categoryIndexs.push(data[i].id);
 this._categoryReocrds[data[i].id] = i;
 cataEl = node.cloneNode(true);
 this._rendercategory(data[i], cataEl);
 i++;
 }
 },
 _rendercategory: function (cate, cateEl) {
 cateEl.setAttribute(&#39;data-cateid&#39;, cate.id);
 var titleEl = document.createElement(&#39;span&#39;),
 contentEl = document.createElement(&#39;span&#39;);
 titleEl.className = &#39;title&#39;;
 contentEl.className = &#39;content&#39;;
 titleEl.innerHTML = cate.name;
 contentEl.innerHTML = cate.content;
 cateEl.appendChild(titleEl);
 cateEl.appendChild(contentEl);
 this.fire(&#39;categoryRender&#39;, {
 categoryEl: cateEl,
 titleEl: titleEl,
 contentEl: contentEl
 });
 this._categoryListEl.appendChild(cateEl);
 this.fire(&#39;agterCategoryRender&#39;, {
 categoryEl: cateEl,
 titleEl: titleEl,
 contentEl: contentEl
 });
 }
}
Nach dem Login kopieren

Das Obige dient dazu, den KlassifizierungsdatensatzCategory als Eintrag festzulegen. und rufen Sie die Zeichnungsklassifizierungsmethode auf, die auch _renderChanged aufruft. Diese Methode wird verwendet, um die variablen Teile des Kalenders neu zu zeichnen, wie z. B. den Titel, das Datum und seinen Inhalt, die später vorgestellt werden.

Datumszeichnung

Die Klassifizierungsachse wurde oben vorbereitet und die Datumsachse muss für die Wochenansicht gezeichnet werden der Woche Es ist ganz einfach: Rendern Sie einfach 7 Tage nacheinander basierend auf dem Startdatum der Woche. Achten Sie darauf, beim Auslosungsprozess die notwendigen Informationen zum Datum des entsprechenden Ereignisses anzugeben, damit der Benutzer das Ereignis personalisieren kann.

{
 // 渲染日历的星期
 _renderWeeks: function () {
 this._weeksEl.innerHTML = &#39;&#39;;
 var i = 0,
 currDate = this._startDate.clone(),
 node = document.createElement(&#39;p&#39;),
 week;
 node.className = &#39;ep-weekcalendar-week&#39;;
 // 单元格列作为下标记录日期
 this._dateRecords = [];
 while (i++ < 7) {
 // 更新记录日期
 this._dateRecords.push(currDate.clone());
 week = node.cloneNode(true);
 this._renderWeek(currDate, week);
 currDate.add(1, &#39;day&#39;);
 }
 // 切换日期 需要重绘内容区域
 this._rednerContent();
 },
 _renderWeek: function (date, node) {
 var dateText = date.format(&#39;YYYY-MM-DD&#39;),
 day = date.isoWeekday();
 if (day > 5) {
 node.className += &#39; weekend&#39;;
 }
 if (date.isSame(this.today, &#39;day&#39;)) {
 node.className += &#39; today&#39;;
 }
 node.setAttribute(&#39;data-date&#39;, dateText);
 node.setAttribute(&#39;date-isoweekday&#39;, day);
 var ev = this.fire(&#39;dateRender&#39;, {
 // 当前完整日期
 date: dateText,
 // iso星期
 isoWeekday: day,
 // 显示的文本
 dateText: &#39;周&#39; + this._WEEKSNAME[day - 1] + &#39; &#39; + date.format(&#39;MM-DD&#39;),
 // classname
 dateCls: node.className,
 // 日历el
 el: this.el,
 // 当前el
 dateEl: node
 });
 // 处理事件的修改
 node.innerHTML = ev.dateText;
 node.className = ev.dateCls;
 this._weeksEl.appendChild(node);
 this.fire(&#39;afterDateRender&#39;, {
 // 当前完整日期
 date: dateText,
 // iso星期
 isoWeekday: day,
 // 显示的文本
 dateText: node.innerHTML,
 // classname
 dateCls: node.className,
 // 日历el
 el: this.el,
 // 当前el
 dateEl: node
 });
 }
}
Nach dem Login kopieren

Raster und Inhalt

Die beiden Achsen in der zweidimensionalen Ansicht wurden oben vorbereitet, und dann können das Raster und die Inhaltsebene erstellt werden gezogen werden.

Raster

Hier erfolgt die Klassifizierung in Y-Richtung (Zeile) und das Datum in X-Richtung (Spalte):

{
 // 右侧网格
 _renderGrid: function () {
 this._gridEl.innerHTML = &#39;&#39;;
 var rowNode = document.createElement(&#39;p&#39;),
 itemNode = document.createElement(&#39;span&#39;),
 rowsNum = this._categoryData.length,
 i = 0,
 j = 0,
 row, item;
 rowNode.className = &#39;ep-weekcalendar-grid-row&#39;;
 itemNode.className = &#39;ep-weekcalendar-grid-item&#39;;
 while (i < rowsNum) {
 row = rowNode.cloneNode();
 row.setAttribute(&#39;data-i&#39;, i);
 j = 0;
 while (j < 7) {
 item = itemNode.cloneNode();
 // 周末标识
 if (this.dayStartFromSunday) {
  if (j === 0 || j === 6) {
  item.className += &#39; weekend&#39;;
  }
 } else {
  if (j > 4) {
  item.className += &#39; weekend&#39;;
  }
 }
 item.setAttribute(&#39;data-i&#39;, i);
 item.setAttribute(&#39;data-j&#39;, j);
 row.appendChild(item);

 j++;
 }
 this._gridEl.appendChild(row);

 i++;
 }
 rowNode = itemNode = row = item = null;
 }
}
Nach dem Login kopieren

Inhalt

Theoretisch sollten zwei Dimensionen sowohl zeilen- als auch spaltenübergreifende Situationen unterstützen, d. h. der Inhaltsbereich sollte ein ganzer Block von Elementen sein. In Kombination mit tatsächlichen Situationen treten jedoch häufig zeitübergreifende Anforderungen auf (eine Sache wird über einen bestimmten Zeitraum kontinuierlich verwendet). Die Kreuzklassifizierung hat keine große praktische Bedeutung. Sie muss separat durch die Klassifizierung verwaltet werden, und dann wird die Kreuzklassifizierung erneut kompliziert. Und selbst wenn es notwendig ist, über einen bestimmten Zeitraum mehrere Dinge gleichzeitig zu verwenden, kann dies direkt implementiert werden (Kategorie A wird im Zeitraum XX verwendet, und B wird im Zeitraum XX verwendet, XX jedoch genau das Gleiche zu diesem Zeitpunkt).

Hier beschäftigen wir uns also nur mit zeitübergreifenden Situationen, und der Inhalt kann nach Zeilen oder Kategorien gezeichnet werden, was viele Berechnungen beim Einfügen von Inhaltskomponenten vereinfachen kann.

{
 // 右侧内容
 _rednerContent: function () {
 this._contentEl.innerHTML = &#39;&#39;;
 var i = 0,
 node = document.createElement(&#39;p&#39;),
 row;
 node.className = &#39;ep-weekcalendar-content-row&#39;;
 while (i < this._categoryData.length) {
 row = node.cloneNode();
 row.setAttribute(&#39;data-i&#39;, i);

 this._contentEl.appendChild(row);
 ++i;
 }
 row = node = null;
 },
 // 日期切换时清空内容
 _clearContent: function () {
 var rows = this._contentEl.childNodes,
 i = 0;
 while (i < rows.length) {
 rows[i].innerHTML && (rows[i].innerHTML = &#39;&#39;);
 ++i;
 }
 // 部件数据清空
 this._widgetData = {};
 }
}
Nach dem Login kopieren

Wenn Sie zeilen- und spaltenübergreifende Situationen erreichen müssen, können Sie den Inhalt direkt in einen ganzen Block von Elementen zeichnen. Wenn Sie jedoch auf Ereignisse klicken und Inhaltskomponenten einfügen, müssen Sie die entsprechende Klassifizierung berechnen sowie Datum und Uhrzeit gleichzeitig.

Schwierigkeiten bei der Umsetzung

Einfügung von Inhaltsbestandteilen

Wir setzen diese zweidimensionale Woche um Kalender anzeigen Der Hauptzweck besteht darin, das Einfügen beliebiger Inhalte zu unterstützen. Das DOM-Element zum Einfügen von Inhalten wurde oben vorbereitet. Hier müssen die Daten in DOM gezeichnet und an der entsprechenden Stelle platziert werden.

Betrachten Sie die erforderliche Datenstruktur der Inhaltskomponente wie folgt:

{
 id: &#39;数据标识&#39;,
 categoryId: &#39;所属分类标识&#39;,
 title: &#39;名称&#39;,
 content: &#39;内容&#39;,
 start: &#39;开始日期时间&#39;
 end: &#39;结束日期时间&#39;
 bgColor: &#39;展示的背景色&#39;
}
Nach dem Login kopieren

Da der Inhaltsbereich direkt entsprechend der Klassifizierung gezeichnet wird, ist die entsprechende Klassifizierung bereits nach Erhalt der Daten vorhanden. Der entscheidende Punkt besteht darin, die Start- und Endpositionen basierend auf den angegebenen Start- und Endzeiten zu berechnen.

Beachten Sie Folgendes:

  • Berücksichtigen Sie die Reaktionsfähigkeit, und die Positionsberechnung wird als Prozentsatz berechnet

  • 一周的总时间是固定的,开始日期时间和这周开始日期时间的差额占总时间的百分比即开始位置的百分比

  • 结束日期时间和开始时间的差额占总时间的百分比即为结束时间距离最左侧的百分比

  • 注意处理开始和结束时间溢出本周的情况

因此关于位置计算可以用如下代码处理:

{
 // 日期时间分隔符 默认为空 对应格式为 &#39;2017-11-11 20:00&#39;
 // 对于&#39;2017-11-11T20:00&#39; 这样的格式务必指定正确的日期和时间之间的分隔符T
 _dateTimeSplit:&#39; &#39;,
 // 一周分钟数
 _WEEKMINUTES: 7 * 24 * 60,
 // 一周秒数
 _WEEKSECONDS: 7 * 24 * 3600,
 // 一天的分钟数秒数
 _DAYMINUTES: 24 * 60,
 _DAYSCONDS: 24 * 3600,
 // 计算位置的精度 取值second 或 minute
 posUnit: &#39;second&#39;,
 // 计算指定日期的分钟或秒数
 _getNumByUnits: function (dateStr) {
 var temp = dateStr.split(this._dateTimeSplit),
  date = temp[0];
 // 处理左侧溢出
 if (this._startDate.isAfter(date, &#39;day&#39;)) {
  // 指定日期在开始日期之前
  return 0;
 }
 // 右侧溢出直接算作第7天即可
 var times = (temp[1] || &#39;&#39;).split(&#39;:&#39;),
  days = (function (startDate) {
  var currDate = startDate.clone(),
   i = 0,
   d = moment(date, &#39;YYYY-MM-DD&#39;);
  while (i < 7) {
   if (currDate.isSame(d, &#39;day&#39;)) {
   return i;
   } else {
   currDate.add(1, &#39;day&#39;);
   ++i;
   }
  }
  console && console.error && console.error(&#39;计算天数时出错!&#39;);
  return i;
  }(this._startDate)),
  hours = parseInt(times[0], 10) || 0,
  minutes = parseInt(times[1], 10) || 0,
  seconds = parseInt(times[2], 10) || 0,
  // 对应分钟数
  result = days * this._DAYMINUTES + hours * 60 + minutes;
 return this.posUnit == &#39;minute&#39; ? result : (result * 60 + seconds);
 },
 // 计算日期时间的百分比位置
 _getPos: function (dateStr) {
 var p = this._getNumByUnits(dateStr) / (this.posUnit == &#39;minute&#39; ? this._WEEKMINUTES : this._WEEKSECONDS);
 return p > 1 ? 1 : p;
 }
}
Nach dem Login kopieren

上面就拿到了一个数据所对应的开始位置和结束位置。基本上是已经完成了,但是还需要再处理一个情况:相同分类下的时间冲突问题。

考虑以如下方式进行:

  • 没添加一个就记录下其数据

  • 新增的如果和当前分类下已有的存在时间重叠,则认为冲突。

实现如下:

{
 /**
 * 检查是否发生重叠
 *
 * @param {Object} data 当前要加入的数据
 * @returns false 或 和当前部件重叠的元素数组
 */
 _checkOccupied: function (data) {
 if (!this._widgetData[data.categoryId]) {
  return false;
 }
 var i = 0,
  cate = this._widgetData[data.categoryId],
  len = cate.length,
  result = false,
  occupied = [];
 for (; i < len; ++i) {
  // 判断时间是否存在重叠
  if (data.start < cate[i].end && data.end > cate[i].start) {
  occupied.push(cate[i]);
  result = true;
  }
 }
 return result ? occupied : false;
 }
}
Nach dem Login kopieren

完成以上两步就可以往我们的内容区域中插入了

{
 // 缓存widget数据
 _cacheWidgetData: function (data) {
 if (!this._widgetData[data.categoryId]) {
  this._widgetData[data.categoryId] = [];
 }
 // 记录当前的
 this._widgetData[data.categoryId].push(data);
 },
 // 新增一个小部件
 addWidget: function (data) {
 var row = this._contentEl.childNodes[this._categoryReocrds[data.categoryId]];
 if (!row) {
  this.throwError(&#39;对应分类不存在,添加失败&#39;);
  return false;
 }
 // 先查找是否含有
 var $aim = jQuery(&#39;.ep-weekcalendar-content-widget[data-id="&#39; + data.id + &#39;"]&#39;, row);
 if ($aim.length) {
  // 已经存在则不添加
  return $aim[0];
 }
 // 创建部件
 var widget = document.createElement(&#39;p&#39;),
  title = document.createElement(&#39;span&#39;),
  content = document.createElement(&#39;p&#39;),
  startPos = this._getPos(data.start),
  endPos = this._getPos(data.end),
  _data = {
  categoryId: data.categoryId,
  id: data.id,
  start: startPos,
  end: endPos,
  el: widget,
  data: data
  };
 widget.className = &#39;ep-weekcalendar-content-widget&#39;;
 title.className = &#39;ep-weekcalendar-content-widget-title&#39;;
 content.className = &#39;ep-weekcalendar-content-widget-content&#39;;
 widget.appendChild(title);
 widget.appendChild(content);
 // 通过绝对定位,指定其left和right来拉开宽度的方式来处理响应式
 // 可以通过样式设置一个最小宽度,来避免时间段过小时其中文本无法显示的问题
 widget.style.left = startPos * 100 + &#39;%&#39;;
 widget.style.right = (1 - endPos) * 100 + &#39;%&#39;;
 data.bgColor && (widget.style.backgroundColor = data.bgColor);
 data.id && widget.setAttribute(&#39;data-id&#39;, data.id);
 widget.setAttribute(&#39;data-start&#39;, data.start);
 widget.setAttribute(&#39;data-end&#39;, data.end);
 title.innerHTML = data.title;
 data.content && (content.innerHTML = data.content);
 widget.title = data.title;
 // 检查是否发生重叠
 var isoccupied = this._checkOccupied(_data);
 if (isoccupied) {
  // 触发重叠事件
  var occupiedEv = this.fire(&#39;widgetoccupied&#39;, {
  occupiedWidgets: (function () {
   var arr = [];
   for (var i = 0, l = isoccupied.length; i < l; ++i) {
   arr.push(isoccupied[i].el);
   }
   return arr;
  })(),
  currWidget: widget,
  widgetData: data
  });
  // 取消后续执行
  if (occupiedEv.cancel) {
  return false;
  }
 }
 // 缓存数据
 this._cacheWidgetData(_data);
 var addEv = this.fire(&#39;widgetAdd&#39;, {
  widgetId: data.id,
  categoryId: data.categoryId,
  start: data.start,
  end: data.end,
  startPos: startPos,
  endPos: endPos,
  widgetEl: widget
 });
 if (addEv.cancel) {
  return false;
 }
 row.appendChild(widget);
 this.fire(&#39;afterWidgetAdd&#39;, {
  widgetId: data.id,
  categoryId: data.categoryId,
  start: data.start,
  end: data.end,
  startPos: startPos,
  endPos: endPos,
  widgetEl: widget
 });
 return widget;
 },
}
Nach dem Login kopieren

点击事件和范围选择

此控件不仅用于结果展示,还要可用于点击进行添加,需要处理其点击事件,但是由于要展示内容,内容是覆盖在分类和日期构成的网格之上的,用户的点击是点击不到网格元素的,必须要根据点击的位置进行计算来获取所点击的日期和所在分类。

同时,由于展示的部件都是时间范围的,因此点击返回某天和某个分类是不够的,还需要能够支持鼠标按下拖动再松开,来直接选的一段时间。

考虑到以上需求,点击事件不能直接使用 click 来实现,考虑使用 mousedown 和 mouseup 来处理点击事件,同时需要在 mousemove 中实时给出用户响应。

{
 _initEvent: function () {
 var me = this;
 // 点击的行索引
 var row,
  // 开始列索引
  columnStart,
  // 结束列索引
  columnEnd,
  // 是否在按下、移动、松开的click中
  isDurringClick = false,
  // 是否移动过 用于处理按下没有移动直接松开的过程
  isMoveing = false,
  $columns,
  // 网格左侧宽度
  gridLeft,
  // 每列的宽度
  columnWidth
 jQuery(this.el)
  // 按下鼠标 记录分类和开始列
  .on(&#39;mousedown.weekcalendar&#39;, &#39;.ep-weekcalendar-content-row&#39;, function (e) {
  isDurringClick = true;
  gridLeft = jQuery(me._gridEl).offset().left;
  columnWidth = jQuery(me._gridEl).width() / 7;
  jQuery(me._gridEl).find(&#39;.ep-weekcalendar-grid-item&#39;).removeClass(me._selectedCls);
  row = this.getAttribute(&#39;data-i&#39;);
  $columns = jQuery(me._gridEl).find(&#39;.ep-weekcalendar-grid-row&#39;).eq(row).children();

  columnStart = (e.pageX - gridLeft) / columnWidth >> 0;
  });
 // 移动和松开 松开鼠标 记录结束列 触发点击事件 
 // 不能直接绑定在日期容器上 否则鼠标移出日历后,松开鼠标,实际点击已经结束,但是日历上处理不到。
 jQuery(&#39;body&#39;)
  // 点击移动过程中 实时响应选中状态
  .on(&#39;mousemove.weekcalendar&#39;, function (e) {
  if (!isDurringClick) {
   return;
  }
  isMoveing = true;
  // 当前列索引
  var currColumn;
  // mousemoveTimer = setTimeout(function () {
  currColumn = (e.pageX - gridLeft) / columnWidth >> 0;
  // 修正溢出
  currColumn = currColumn > 6 ? 6 : currColumn;
  currColumn = currColumn < 0 ? 0 : currColumn;
  $columns.removeClass(me._selectedCls);
  // 起止依次选中
  var start = Math.min(columnStart, currColumn),
   end = Math.max(columnStart, currColumn);
  do {
   $columns.eq(start).addClass(me._selectedCls);
  } while (++start <= end);
  })
  // 鼠标松开
  .on(&#39;mouseup.weekcalendar&#39;, function (e) {
  if (!isDurringClick) {
   return;
  }
  var startIndex = -1,
   endIndex = -1;
  columnEnd = (e.pageX - gridLeft) / columnWidth >> 0;
  columnEnd = columnEnd > 6 ? 6 : columnEnd;
  // 没有移动过时
  if (!isMoveing) {
   startIndex = endIndex = columnEnd;
   // 直接down up 没有move的过程则只会有一个选中的,直接以结束的作为处理即可
   $columns.eq(columnEnd).addClass(me._selectedCls)
   .siblings().removeClass(me._selectedCls);
  } else {
   startIndex = Math.min(columnStart, columnEnd);
   endIndex = Math.max(columnStart, columnEnd);
  }
  // 触发点击事件
  me.fire(&#39;cellClick&#39;, {
   // 分类id 
   categoryId: me._categoryIndexs[row],
   // 时间1
   startDate: me._dateRecords[startIndex].format(&#39;YYYY-MM-DD&#39;),
   // 日期2
   endDate: me._dateRecords[endIndex].format(&#39;YYYY-MM-DD&#39;),
   // 行索引
   rowIndex: row,
   // 列范围
   columnIndexs: (function (i, j) {
   var arr = [];
   while (i <= j) {
    arr.push(i++);
   }
   return arr;
   }(startIndex, endIndex))
  });
  row = columnStart = columnEnd = isMoveing = isDurringClick = false;
  });
 }
}
Nach dem Login kopieren

此过程要注意的问题是:mousedown 必须绑定在日历上,而 mouseup 和 mousemove 则不能绑定在日历上,具体原因已经写在上面代码注释中了。

另外需要注意,由于范围点击选择使用了 mousedown 和 mouseup 来模拟,那么日历内容区域中插入的数据部件的点击事件也要用 mousedown 和 mouseup 来模拟,因为 mouseup 触发比 click 早,如果使用 click ,会导致先触发日历上的日期点击或日期范围点击。

使用

此日历实现基于一个控件基类扩展而来,其必要功能仅为一套事件机制,可参考实现一套自定义事件机制

实测一下效果吧:

<p id="week-calendar" style="width:100%;height:80vh"></p>
<script>
var calendar = epctrl.init(&#39;WeekCalendar&#39;, {
 el: &#39;#week-calendar&#39;,
 categoryTitle: &#39;车辆&#39;,
 category: [{
 id: &#39;cate-1&#39;,
 name: &#39;法拉利&#39;,
 content: &#39;苏E00000&#39;
 }, {
 id: &#39;cate-2&#39;,
 name: &#39;Lamborghini&#39;,
 content: &#39;苏E00001&#39;
 }, {
 id: &#39;cate-3&#39;,
 name: &#39;捷豹&#39;,
 content: &#39;苏E00002&#39;
 }, {
 id: &#39;cate-4&#39;,
 name: &#39;宾利&#39;,
 content: &#39;苏E00003&#39;
 }, {
 id: &#39;cate-5&#39;,
 name: &#39;SSC&#39;,
 content: &#39;苏E00004&#39;
 }],
 events: {
 // 日期变化时触发
 dateChanged: function (e) {
  var data = {
  start: e.startDate,
  end: e.endDate,
  };
  // 获取数据并逐个添加到日历上
  getData(data).done(function (data) {
  $.each(data, function (i, item) {
   calendar.addWidget(item);
  });
  });
 },
 // 部件重叠时触发
 widgetOccupied: function (e) {
  // 冲突时禁止继续添加
  console.error(e.widgetData.categoryId + &#39;分类下id为&#39; + e.widgetData.id + &#39;的部件和现有部件有重叠,取消添加&#39;);
  e.cancel = true;
 }
 }
});
calendar.on(&#39;dateClick&#39;, function (e) {
 alert(JSON.stringify({
 &#39;开始时间&#39;: e.startDate,
 &#39;结束时间&#39;: e.endDate,
 &#39;分类id&#39;: e.categoryId,
 &#39;行索引&#39;: e.rowIndex,
 &#39;列索引范围&#39;: e.columnIndexs
 }, 0, 4));
});
</script>
Nach dem Login kopieren

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

在js中如何实现登录需要滑动验证

在js中如何实现判断文件类型大小

在vue中如何使用cdn优化

使用js如何实现各种排序方法

使用JavaScript如何实现猜拳游戏(详细教程)

如何使用node模块与npm包管理工具

Das obige ist der detaillierte Inhalt vonSo entwickeln Sie mit Javascript einen zweidimensionalen Kalender mit Wochenansicht. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!