Cách tạo biểu đồ Gantt JavaScript Vanilla: Thêm tính năng chỉnh sửa tác vụ (Phần 2)

theanh

Administrator
Nhân viên
Bài viết này nhận được sự hỗ trợ nhiệt tình của những người bạn thân mến của chúng tôi tại Bryntum, những người tin tưởng mạnh mẽ vào web như một nền tảng ứng dụng và cung cấp các thành phần UI tiên tiến cùng các công cụ phát triển cho hơn 5000 công ty tại hơn 70 quốc gia. Cảm ơn bạn!



Trong Phần 1 của bài viết này, chúng tôi đã phát triển một thành phần web cho Biểu đồ Gantt tương tác. Bây giờ chúng tôi sẽ cải tiến thành phần Biểu đồ Gantt với một số khả năng tương tác để chỉnh sửa các công việc: các thanh công việc có thể thay đổi kích thước bằng cách kéo chuột và chúng tôi cũng triển khai hộp thoại chỉnh sửa có thể được sử dụng để sửa đổi ngày bắt đầu và ngày kết thúc của một công việc. Khi thực hiện như vậy, chúng tôi sẽ tiếp tục làm việc với Vanilla JS và các Thành phần Web. Cuối cùng, chúng ta sẽ xem xét một số thư viện JavaScript có thể đơn giản hóa đáng kể nỗ lực phát triển Biểu đồ Gantt đầy đủ chức năng.

Video sau đây cho thấy những gì chúng ta sẽ xây dựng trong bài viết này. Đầu tiên, chúng ta sẽ thêm một tay cầm kéo ở phía bên phải của mỗi công việc có thể được sử dụng để thay đổi kích thước thanh công việc (trong hình, nó được hiển thị dưới dạng một thanh dọc màu xám hẹp). Ở bước tiếp theo, chúng ta sẽ mở rộng thêm hành vi của các công việc để khi nhấp đúp vào thanh công việc, hộp thoại chỉnh sửa sẽ mở ra.

[*] Giao diện mới của Biểu đồ Gantt với các tính năng chỉnh sửa công việc
Để làm theo hướng dẫn trong bài viết này, bạn có thể sử dụng các tệp từ bài viết trước làm điểm khởi đầu. Bạn có thể tìm thấy mã nguồn của giải pháp hoàn thiện trong kho lưu trữ GitHub của tôi.

Vì mã chứa các mô-đun JavaScript nên bạn chỉ có thể chạy ví dụ từ máy chủ HTTP chứ không phải từ hệ thống tệp cục bộ. Để thử nghiệm trên PC cục bộ của bạn, tôi khuyên bạn nên sử dụng mô-đun live-server, bạn có thể cài đặt qua npm.

Ngoài ra, bạn có thể thử ví dụ tại đây trực tiếp trong trình duyệt của mình mà không cần cài đặt.

Thành phần web dành cho công việc​

Trước khi mở rộng chức năng của công việc, chúng tôi sẽ chuyển mã nguồn liên quan đến công việc vào một thành phần riêng có tên GanttJob.

Thành phần này chứa các phương thức thiết lập và lấy dữ liệu cho một đối tượng công việc và cho mức thu phóng đã chọn của trục thời gian của Biểu đồ Gantt (năm-tháng hoặc ngày). Nó hiển thị một công việc như một phần tử div đơn giản . Độ dài của thanh công việc được tính toán trong hàm render tùy thuộc vào mức thu phóng và thời lượng của công việc.

So với Phần 1 của bài viết này, tôi đã thay đổi một chút về kiểu dáng của các công việc.

Lưu ý: Kiểm tra một số thay đổi nhỏ trong kiểu CSS của thành phần VanillaGanttChart.
Mã:
const template = document.createElement('template'); template.innerHTML = ` @import "./styles/GanttJob.css";  `; xuất khẩu mặc định lớp GanttJob mở rộng HTMLElement { constructor() { super(); this.attachShadow({ chế độ: 'mở' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); } connectedCallback() { var jobElement = this.shadowRoot.querySelector(".job"); jobElement.id = this.id; jobElement.draggable = true; this._render(); } disconnectedCallback() { } update(){ this._render(); } _render(){ var jobElement = this.shadowRoot.querySelector(".job"); var d; if(this._level == "năm-tháng"){ d = this._dayDiff(this.job.start, this.job.end); }else{//_level = "ngày" d = this._hourDiff(this.job.start, this.job.end); } jobElement.style.width = `calc(${d*100}% + ${d}px)`; } _job; _level = "year-month"; set job(newValue){ this._job = newValue; this._render(); } get job(){ return this._job; } set level(newValue) this._level = newValue; } get level(){ return this._level; } //các hàm trợ giúp _dayDiff, _hourDiff; xem các tệp ví dụ}window.customElements.define('gantt-job', GanttJob);
Trong lớp YearMonthRenderer, bạn chỉ cần điều chỉnh hàm initJobs ngay bây giờ. Thay vì một phần tử div đơn giản, phần tử tùy chỉnh gantt-job được chèn vào lưới Gantt cho mỗi công việc từ mảng jobs. Nó được khởi tạo bằng các thuộc tính id, joblevel. Hơn nữa, không có thay đổi nào đối với việc triển khai kéo và thả các công việc trong lưới Gantt.

Những thay đổi tương tự được áp dụng cho lớp DateTimeRenderer.
Mã:
var initJobs = function(){ this.jobs.forEach(job => { var date_string = formatDate(job.start); var ganttElement = shadowRoot.querySelector(`div[data-resource="${job.resource}"][data-date="${date_string}"]`); if(ganttElement){ var jobElement = document.createElement("gantt-job"); jobElement.id = job.id; jobElement.job = job; jobElement.level = "year-month"; ganttElement.appendChild(jobElement); jobElement.ondragstart = function(ev){ ev.dataTransfer.setData("job", ev.target.id); }; } });}.bind(this);

Làm cho công việc có thể thay đổi kích thước​

Người dùng có thể tăng hoặc giảm thời lượng của công việc bằng cách kéo cạnh ngoài bên phải của thanh công việc. Hơn nữa, chỉ có thể thay đổi thời lượng theo các bước tương ứng với kích thước của các phân đoạn thời gian trong chế độ xem hiện đang hoạt động. Điều này có nghĩa là nếu mức năm-tháng được đặt, thì thời lượng chỉ có thể thay đổi theo các bước là ngày đầy đủ và nếu mức ngày được đặt, thì thời lượng chỉ có thể thay đổi theo các bước là giờ đầy đủ.

Đầu tiên, một tay cầm kéo được thêm vào bên phải của thanh công việc bằng CSS (xem tệp styles/GanttJob.css).
Mã:
.job::after { content: ''; background-color: #646965; position: absolute; right: 0; width: 4px; height: 100%; cursor: ew-resize;}
Để làm cho một công việc có thể thay đổi kích thước, có thể sử dụng chiến lược sau:
  1. Việc thay đổi kích thước bắt đầu khi người dùng nhấp vào tay cầm kéo của một công việc. Điều này có nghĩa là chúng ta phải phản hồi sự kiện mousedown trong biểu đồ Gantt.
  2. Từ thời điểm này trở đi, thanh công việc sẽ tăng hoặc giảm khi người dùng di chuyển chuột sang trái hoặc phải. Hành vi này có thể được kiểm soát thông qua sự kiện mousemove.
  3. Việc thay đổi kích thước sẽ hoàn tất ngay khi người dùng nhả nút chuột một lần nữa: sau khi sự kiện mouseup xảy ra.
Có thể bạn sẽ nghĩ ngay đến việc thêm trình lắng nghe mousedown vào thanh công việc trong thành phần GanttJob. Tuy nhiên, tùy thuộc vào số lượng công việc trong Biểu đồ Gantt, điều này có thể dẫn đến số lượng trình lắng nghe rất lớn, có thể ảnh hưởng đến hiệu suất của thành phần.

Do đó, trong trường hợp này, tôi sử dụng khái niệm ủy quyền sự kiện. Thay vì cài đặt trình lắng nghe mousedown riêng cho mỗi công việc, chúng tôi cài đặt chính xác một trình lắng nghe cho phần tử cha chung. Trong trường hợp của chúng tôi, đây là phần tử div có ID gantt-container, chứa Biểu đồ Gantt đầy đủ.

Bạn có thể thêm mã sau vào tệp YearMonthRenderer.js:
Mã:
var makeJobsResizable = function(){ if(checkElements()){ var container = shadowRoot.querySelector("#gantt-container"); container.addEventListener("mousedown", _handleMouseDown, false); } }.bind(this); var _handleMouseDown = function(e){ if(e.target.tagName == "GANTT-JOB"){ this.selectedJobElement = e.target; if(this.selectedJobElement.isMouseOverDragHandle(e)){ this.selectedJob = this.jobs.find(j => j.id == e.target.id); document.addEventListener("mousemove", _handleMouseMove, false); document.addEventListener("mouseup", _handleMouseUp, false); //sử dụng this để vô hiệu hóa hành vi kéo và thả trong chế độ thay đổi kích thước e.preventDefault(); } }}.bind(this);
Trình xử lý mousedown kiểm tra xem người dùng đã nhấp vào phần tử GanttJob hay chưa. Nếu đúng như vậy, vẫn phải kiểm tra xem người dùng đã nhấp vào tay cầm kéo hay chưa. Chỉ khi đó, trình lắng nghe mousemovemouseup mới được cài đặt trong suốt thời gian diễn ra hoạt động thay đổi kích thước.

Trong tệp GanttJob.js, hàm isMouseOverDragHandle được thêm vào:
Mã:
isMouseOverDragHandle = function(e){ var panel = this.shadowRoot.querySelector(".job"); var current_width = parseInt(getComputedStyle(panel, '').width); if (e.offsetX >= (current_width - this._HANDLE_SIZE)) { return true; } return false;}.bind(this);//phải khớp với cài đặt chiều rộng của tay cầm kéo trong tệp "GanttJob.css" _HANDLE_SIZE = 4;
Các hàm xử lý còn lại hiện được thêm vào tệp YearMonthRenderer.js.

Trong trình xử lý _handleMouseMove, trước tiên chúng ta cần kiểm tra xem con trỏ chuột hiện đang ở phần nào của Biểu đồ Gantt. gantt-item tương ứng cho chúng ta biết ngày kết thúc mới của công việc thông qua thuộc tính data-date. Bằng cách gọi hàm update của phần tử GanttJob đã chọn, chúng ta cập nhật độ dài của thanh công việc.

Như bạn có thể thấy trong hàm render của thành phần GanttJob, độ dài chỉ được sửa đổi theo các bước tương ứng với kích thước của các phần thời gian của Biểu đồ Gantt.

Trong trình xử lý _handleMouseUp, lựa chọn công việc sẽ bị xóa và trình lắng nghe mousemove sẽ bị loại bỏ.
Mã:
var _handleMouseMove = function(ev){ var gantt_item = getGanttElementFromPosition(ev.x, ev.y); if(this.selectedJob && this.selectedJobElement){ this.selectedJob.end = new Date(gantt_item.getAttribute("data-date")); this.selectedJobElement.update(); } }.bind(this); var _handleMouseUp = function(){ this.selectedJob = null; this.selectedJobElement = null; document.removeEventListener("mousemove", _handleMouseMove, false); }.bind(this); var getGanttElementFromPosition = function(x, y){ //elementsFromPoint: trả về một mảng gồm tất cả các phần tử tại tọa độ x,y var items = shadowRoot.elementsFromPoint(x, y); //Có thể con trỏ chuột nằm ngay phía trên tay cầm kéo hoặc phía trên thanh công việc. Do đó, chúng ta phải lặp qua mảng cho đến khi tìm được gantt_item nằm phía sau nó var gantt_item = items.find(item => item.classList.contains("gantt-row-item")); return gantt_item; } this.selectedJob = null; this.selectedJobElement = null;
Ở bước cuối cùng, chúng ta gọi hàm makeJobsResizable ở cuối hàm initJobs. Chúng tôi cũng sử dụng hàm hiện có clear trong tệp YearMonthRenderer.js để xóa các trình lắng nghe mousedownmouseup (xem các tệp mẫu).
Mã:
//xem tệp “YearMonthRenderer.js” trong các tệp ví dụ của bài viết nàyvar initJobs = function(){ … makeJobsResizable();}.bind(this);
Tất cả các bước được hiển thị ở trên cũng được áp dụng cho tệp DateTimeRenderer.js.

Làm cho công việc có thể chỉnh sửa​

Ngày bắt đầu và ngày kết thúc của công việc cũng có thể được sửa đổi thông qua hộp thoại chỉnh sửa. Với mục đích này, trước tiên chúng tôi phát triển thành phần web của riêng mình cho hộp thoại có tên GanttJobDialog.

Các thuộc tính sau được gán cho hộp thoại từ bên ngoài:
  • job cho đối tượng công việc cần chỉnh sửa;
  • level cho mức thu phóng hiện tại của dòng thời gian (năm-tháng hoặc ngày);
  • xPosyPos cho tọa độ xy của vị trí góc trên bên trái của hộp thoại, liên quan đến vị trí của thanh công việc được liên kết.
Trong hàm render, các phần tử biểu mẫu thích hợp để điều chỉnh ngày bắt đầu và ngày kết thúc được tạo tùy thuộc vào mức thu phóng. Các đầu vào kiểu ngày được sử dụng khi mức được đặt thành năm-tháng và các đầu vào kiểu ngày-giờ-địa phương được sử dụng khi mức là ngày. Hơn nữa, các giá trị của đầu vào được khởi tạo bằng dữ liệu hiện tại của công việc.

Trình xử lý cho nút lưu của hộp thoại có các trách nhiệm sau:
  • ngày bắt đầu và ngày kết thúc mới được đọc từ các trường đầu vào và được gán cho đối tượng công việc;
  • một CustomEvent có tên sự kiện lưu được kích hoạt để thông báo cho người gọi hộp thoại (ở đây là thành phần GanttJob) rằng dữ liệu công việc đã được thay đổi;
  • hộp thoại trở nên vô hình.
Trình xử lý cho nút hủy của hộp thoại sẽ kích hoạt một CustomEvent có tên sự kiện hủy để thông báo cho người gọi hộp thoại rằng không có dữ liệu nào bị thay đổi.
Mã:
const template = document.createElement('template'); template.innerHTML = ` @import "./styles/GanttJob.css";   [HEADING=4]Chỉnh sửa tác vụ[/HEADING]  
 Start  
 
 End  
 
   
  `; export default class GanttJobDialog extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(template.content.cloneNode(true)); } _job; _xPos; _yPos; _level = "năm-tháng"; connectedCallback() this._render(); } disconnectedCallback() { this.shadowRoot.querySelector("#cancel_button").removeEventListener("click", this._handleCancel); this.shadowRoot.querySelector("#save_button").removeEventListener("nhấp chuột", this._handleSave); document.removeEventListener("nhấp chuột", this._handleClickOutside); } _render(){ var dialogElement = this.shadowRoot.querySelector("hộp thoại"); dialogElement.style.left = this._xPos+"px"; dialogElement.style.top = this._yPos+"px"; if(this.level == "năm-tháng"){ this.shadowRoot.querySelector("#start_input").type = "ngày"; this.shadowRoot.querySelector("#end_input").type = "ngày"; this.shadowRoot.querySelector("#start_input").giá trị = `${this.job.start.getFullYear()}-${this.zeroPad(this.job.start.getMonth()+1)}-${this.zeroPad(this.job.start.getDate())}`; this.shadowRoot.querySelector("#end_input").giá trị = `${this.job.end.getFullYear()}-${this.zeroPad(this.job.end.getMonth()+1)}-${this.zeroPad(this.job.end.getDate())}`; }else{ this.shadowRoot.querySelector("#start_input").kiểu = "datetime-local"; this.shadowRoot.querySelector("#end_input").kiểu = "datetime-local"; this.shadowRoot.querySelector("#start_input").giá trị = `${this.job.start.getFullYear()}-${this.zeroPad(this.job.start.getMonth()+1)}-${this.zeroPad(this.job.start.getDate())}T${this.zeroPad(this.job.start.getHours())}:00`; this.shadowRoot.querySelector("#end_input").giá trị = `${this.job.end.getFullYear()}-${this.zeroPad(this.job.end.getMonth()+1)}-${this.zeroPad(this.job.end.getDate())}T${this.zeroPad(this.job.end.getHours())}:00`; } this.shadowRoot.querySelector("#cancel_button").addEventListener("nhấp", this._handleCancel); this.shadowRoot.querySelector("#save_button").addEventListener("nhấp chuột", this._handleSave); document.addEventListener("nhấp chuột", this._handleClickOutside); } _handleCancel = hàm(e){ var dialogElement = this.shadowRoot.querySelector("hộp thoại"); this.dispatchEvent(new CustomEvent("hủy")); dialogElement.style.visibility = "ẩn"; }.bind(this); _handleSave = hàm(e){ var dialogElement = this.shadowRoot.querySelector("hộp thoại"); this.job.start = ngày mới(this.shadowRoot.querySelector("#start_input").giá trị); this.job.end = ngày mới(this.shadowRoot.querySelector("#end_input").giá trị); this.dispatchEvent(new CustomEvent("lưu")); dialogElement.style.visibility = "hidden"; }.bind(this); //khi nhấp bên ngoài hộp thoại, hộp thoại cũng sẽ bị hủy _handleClickOutside = function(e){ var dialogElement = this.shadowRoot.querySelector("dialog"); //chúng ta cần kiểm tra xem cú nhấp được kích hoạt bên trong hay bên ngoài hộp thoại var items = this.shadowRoot.elementsFromPoint(e.offsetX, e.offsetY); var close = true; items.forEach(item => { if(item.tagName == “DIALOG”){ close = false; return; } }); if(close){ this.dispatchEvent(new CustomEvent("cancel")); dialogElement.style.visibility = "hidden"; } }.bind(this); set job(newValue){ this._job = newValue; this._render(); } get job(){ return this._job; } set xPos(newValue){ this._xPos = newValue; this._render(); } set yPos(newValue){ this._yPos = newValue; this._render(); } set level(newValue){ this._level = newValue; } get level(){ return this._level; } zeroPad(n){return n { panel.style.zIndex = 100; dialogElement.remove(); }); dialogElement.addEventListener("save", () => { this.update(); panel.style.zIndex = 100; dialogElement.remove(); this.dispatchEvent(new CustomEvent("editjob")); });}.bind(this);
Quay lại tệp YearMonthRenderer.js, chúng ta phải mở rộng hàm initJobs một lần nữa. Đầu tiên, một trình xử lý cho sự kiện editjob được định nghĩa để đảm bảo rằng công việc được chèn lại đúng vị trí trong Biểu đồ Gantt trong trường hợp thời gian bắt đầu thay đổi. Thứ hai, một lệnh gọi đến makeJobsEditable được đặt ở cuối hàm.
Mã:
var initJobs = function{ ... jobElement.ondragstart = function(ev){ ev.dataTransfer.setData("job", ev.target.id); }; jobElement.addEventListener("editjob", (ev) =>{ var date_string = formatDate(job.start); var gantt_item = shadowRoot.querySelector(`div[data-resource="${job.resource}"][data-date="${date_string}"]`); gantt_item.appendChild(jobElement); }); ... makeJobsResizable(); makeJobsEditable();}
Tất cả các bước được hiển thị cũng được áp dụng cho tệp DateTimeRenderer.js.

Các thư viện đặc biệt cho biểu đồ Gantt và lập lịch​

Với các cải tiến về mã được trình bày trong phần thứ hai của bài viết này, Biểu đồ Gantt đã trở nên tương tác hơn và thân thiện với người dùng hơn một chút. Tuy nhiên, vẫn còn một vài điều chưa được giải quyết. Ví dụ, khi thả một công việc, phía bên trái của thanh công việc (tức là thời gian bắt đầu) sẽ nhảy đến ô gantt ngay bên dưới con trỏ chuột. Thay vào đó, công việc phải được đặt chính xác vào ô mà phía bên trái của thanh trỏ đến bất kể vị trí của con trỏ chuột trên thanh công việc.

Ngoài ra, chúng ta cần thêm một vài cải tiến nữa giúp tương tác với Biểu đồ Gantt mượt mà hơn, chẳng hạn như điều chỉnh linh hoạt biểu đồ theo kích thước màn hình hoặc định vị linh hoạt các hộp thoại chỉnh sửa tùy thuộc vào không gian có sẵn.

Thành phần biểu đồ hiện tại có thể được định cấu hình thành Biểu đồ Gantt và trình lập lịch tài nguyên. Nếu được sử dụng làm Biểu đồ Gantt, một số quy tắc vẫn phải được triển khai. Ví dụ, trong trường hợp này, một công việc không được di chuyển từ hàng này sang hàng tiếp theo. Ngoài ra, cần có nhiều tùy chọn cấu hình cụ thể hơn cho Biểu đồ Gantt, chẳng hạn như phụ thuộc trình tự giữa các công việc hoặc tổ chức phân cấp các tác vụ thành các tác vụ con.

Người dùng có thể mong đợi nhiều tính năng hơn nữa trong Biểu đồ Gantt chuyên nghiệp để sử dụng trong sản xuất:
  • sắp xếp lại các hàng trong cây WBS của các tác vụ và tác vụ con;
  • lọc, sắp xếp, chỉnh sửa ô nội tuyến;
  • theo dõi các thay đổi dữ liệu;
  • Phóng to các chế độ xem khác nhau với mức độ chi tiết thời gian khác nhau (ngày so với tháng so với năm);
  • phụ thuộc giữa các tác vụ;
  • thời gian không làm việc;
  • chú giải công cụ
  • .. và nhiều hơn nữa.
Vì vậy, nếu bạn muốn phát triển thêm Biểu đồ Gantt hoặc thành phần lập lịch của riêng mình, đây là một số ý tưởng về tính năng xem xét.

Tuy nhiên, nếu bạn đang tìm kiếm giải pháp biểu đồ Gantt mà bạn có thể dễ dàng tích hợp vào ứng dụng web của mình, thì có thể đáng để xem qua các thư viện Gantt JavaScript của bên thứ ba hiện có. Các thư viện như vậy chứa các thành phần tinh vi được xây dựng sẵn giúp bạn tiết kiệm rất nhiều công sức phát triển và duy trì tiện ích Gantt của riêng mình.

Chúng ta hãy cùng xem ba ví dụ về các thư viện biểu đồ Gantt chuyên nghiệp.

Biểu đồ Gantt JavaScript của Syncfusion​

Việc xử lý các thư viện Biểu đồ Gantt thường rất thoải mái. Thông thường, chỉ cần một tệp JavaScript là đủ để định cấu hình biểu đồ theo sở thích của bạn và điền dữ liệu vào đó.

Ví dụ, hãy xem tệp index.js của dự án trong bài viết này, nơi chúng tôi khởi tạo Biểu đồ Gantt bằng dữ liệu cần thiết của công việc và tài nguyên. Thành phần VanillaGanttChart của chúng tôi chỉ được nhập vào đầu tệp. Đây là cách nó hoạt động với các thư viện có sẵn: sau khi nhập các tập lệnh và mô-đun cần thiết, bạn có thể bắt đầu nhập dữ liệu của mình vào Biểu đồ Gantt ngay lập tức hoặc cấu hình Biểu đồ Gantt theo ý muốn của mình.

Hãy cùng xem Biểu đồ Gantt JavaScript của Syncfusion. Đây là một công cụ thương mại "để hiển thị và quản lý các tác vụ phân cấp với các chi tiết về dòng thời gian".

Trong phần "bắt đầu" của tài liệu của công cụ, bạn sẽ học cách khởi tạo Biểu đồ Gantt đơn giản với một vài tùy chọn cơ bản (ví dụ: các tùy chọn để chỉnh sửa tác vụ, lọc, sắp xếp và xác định mối quan hệ tác vụ). Biểu đồ Gantt đơn giản đầu tiên có thể trông như thế này với Syncfusion:



Dựa trên điều này, chức năng của biểu đồ có thể được mở rộng hơn nữa.

Biểu đồ Gantt JavaScript của Bryntum​

Một ví dụ khác đáng để xem xét là Bryntum Thư viện Gantt, "một bộ biểu đồ Gantt siêu nhanh và có thể tùy chỉnh hoàn toàn."

Sau khi tải xuống bản dùng thử miễn phí của thư viện, bạn sẽ nhận được một thư mục xây dựng với các tệp CSS và JavaScript để tạo biểu đồ Gantt tương tác. Bạn có thể tích hợp các tệp này vào ứng dụng web của mình và sau đó cấu hình ngay lập tức biểu đồ riêng của bạn. Một hướng dẫn bắt đầu đơn giản cung cấp phần giới thiệu nhanh về thành phần này. Ví dụ, biểu đồ cơ bản có thể trông như thế này với thư viện Bryntum Gantt:



Bạn sẽ học được nhiều điều về nhiều tùy chọn tùy chỉnh trong tài liệu đầy đủ về Bryntum Gantt. Bạn cũng sẽ khám phá cách công cụ này có thể được tích hợp với các khuôn khổ phổ biến như Angular, React, Vue và nhiều khuôn khổ khác hoặc cách tổ chức việc tải và lưu dữ liệu (quản lý dữ liệu CRUD).

phần ví dụ cung cấp tổng quan trực quan về các tính năng khác nhau của Bryntum Gantt.



Họ cũng cung cấp Bryntum Scheduler — một thư viện để lập kế hoạch tài nguyên.

Biểu đồ Gantt JavaScript của Webix​

Với Webix Gantt, một thư viện Gantt thương mại khác có chức năng phong phú đã có sẵn. Các bước đơn giản để cài đặt, tạo và cấu hình biểu đồ Gantt được ghi lại chi tiết.

Bạn có thể dùng thử công cụ này trong bản demo tương tác toàn màn hình:


Kết luận​

Biểu đồ Gantt là hình ảnh trực quan có giá trị cho quản lý dự án, lập kế hoạch và tổ chức nhiệm vụ. Có nhiều cách để tích hợp Biểu đồ Gantt vào ứng dụng web. Trong hai bài viết trước, chúng tôi đã xây dựng Biểu đồ Gantt tương tác từ đầu và khi làm như vậy, chúng tôi đã học được rất nhiều về lưới CSS, Thành phần Web và sự kiện JavaScript. Nếu bạn có những yêu cầu phức tạp hơn, bạn nên xem xét các thư viện JS thương mại, thường rất mạnh mẽ.

Đọc thêm​

  • Tạo số ngẫu nhiên duy nhất trong JavaScript bằng cách sử dụng các tập hợp
  • Thành phần web so với thành phần khung: Sự khác biệt là gì?
  • Truyền phát video thích ứng với Dash.js trong React
  • Chế độ chặt chẽ: Tại sao trình duyệt tạo ra kết quả hiệu suất khác nhau
 
Back
Bên trên