Это руководство описывает процесс настройки диаграммы Ганта с использованием backend на Ruby on Rails. В примере используется Ruby 2.4.1, Rails 5.1.3 и MySQL. Предполагается, что вы уже установили необходимые зависимости. Если нет, сначала ознакомьтесь с официальными руководствами.
Если вы работаете с другой технологией, доступны другие варианты интеграции:
Вы также можете ознакомиться с демо на GitHub.
Для начала создайте новый проект Rails с MySQL в качестве базы данных:
rails new gantt-app -d mysql
Начните с создания контроллера и страницы по умолчанию для приложения. Перейдите в папку проекта и создайте новый контроллер с действием index
:
cd gantt-app
rails generate controller gantt index
Вы должны увидеть подтверждение о создании новых файлов.
Обновите конфигурацию маршрутизации в config/routes.rb
, чтобы установить действие index
нового контроллера в качестве маршрута по умолчанию:
Rails.application.routes.draw do
root :to => "gantt#index"
end
Запустите сервер Rails:
rails server
Откройте http://localhost:3000/
в браузере. Вы должны увидеть пустую страницу, что указывает на работу приложения. Теперь давайте добавим диаграмму Ганта.
Чтобы включить диаграмму Ганта, измените файл макета, добавив yield
внутри тега <head>
. Это позволит добавить файлы dhtmlxGantt на страницу:
<!DOCTYPE html>
<html>
<head>
<title>dhtmlxGantt</title>
<%= stylesheet_link_tag 'application', media:'all','data-turbolinks-track' => true %>
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
<%= yield(:head) %>
<%= csrf_meta_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
Затем обновите представление gantt/index
, чтобы включить диаграмму Ганта:
<% content_for :head do %>
<%= stylesheet_link_tag 'https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.css' %>
<%= javascript_include_tag 'https://cdn.dhtmlx.com/gantt/edge/dhtmlxgantt.js' %>
<% end %>
<div id="gantt_here" style='width:100%; height:800px;'></div>
<script>
gantt.init("gantt_here");
</script>
Файлы Ганта загружаются с CDN. Для разработки вы можете использовать исходные файлы, включенные в пакет загрузки.
Откройте http://localhost:3000/
в вашем браузере снова. Теперь вы увидите диаграмму Ганта, где вы можете добавлять и изменять задачи, но функциональность сохранения еще недоступна. Следующий шаг включает создание моделей для возможности сохранения.
Поскольку мы используем MySQL, убедитесь, что настройки подключения к базе данных в config/database.yml
правильные:
development:
adapter: mysql2
encoding: utf8
host: localhost
database: gantt-app
username: root
password:
Теперь создайте модели для задач и связей.
Создайте модель для задач со следующими свойствами:
rails generate model Task \
text:string \
start_date:datetime \
duration:integer \
parent:integer \
progress:decimal
Для связей используйте эту более короткую команду:
rails generate model Link \
source:integer \
target:integer \
link_type:string:limit1
Объект связи Ганта требует свойства с именем type
для хранения типа связи. Поскольку type
зарезервирован в ActiveRecord, мы будем использовать link_type
и обрабатывать сопоставление в контроллере.
Выполните миграцию для обновления базы данных:
rake db:migrate
Добавьте некоторые тестовые данные:
Откройте консоль Rails:
rails c
Добавьте задачи и связи:
Task.create :text=>"Task 1", :start_date=>"2015-10-25", :duration=>2, :progress=>0;
Task.create :text=>"Task 2", :start_date=>"2015-10-27", :duration=>3, :progress=>0.5;
Link.create :source=>1, :target=>2, :link_type=>"0";
Выйдите из консоли:
exit
Далее, давайте загрузим и сохраним данные в диаграмме Ганта с помощью контроллеров.
С моделями и базой данных, подготовленными, следующий шаг - загрузка данных в диаграмму Ганта. dhtmlxGantt ожидает данные в формате JSON. Добавьте новое действие в GanttController
для чтения, форматирования и вывода данных:
class GanttController < ApplicationController
def index
end
def data
tasks = Task.all
links = Link.all
render :json => {
:data => tasks.map { |task| {
:id => task.id,
:text => task.text,
:start_date => task.start_date.to_formatted_s(:db),
:duration => task.duration,
:progress => task.progress,
:parent => task.parent,
:open => true
}},
:links => links.map { |link| {
:id => link.id,
:source => link.source,
:target => link.target,
:type => link.link_type
}}
}
end
end
Добавьте маршрут для этого действия в routes.rb
:
Rails.application.routes.draw do
root :to => "gantt#index"
scope '/api' do
get "/data", :to => "gantt#data"
end
end
На стороне клиента вызовите это действие с помощью метода gantt.load
:
gantt.config.date_format = "%Y-%m-%d %H:%i:%s";
gantt.init("gantt_here");
gantt.load("/api/data");
Конфигурация date_format
гарантирует, что формат даты соответствует формату сервера.
Запустите сервер и откройте http://localhost:3000/
в браузере. Вы должны увидеть диаграмму Ганта, заполненную данными из базы данных. Однако изменения пока не будут сохранены. Давайте решим эту проблему дальше.
dhtmlxGantt может отправлять изменения, сделанные пользователем, в RESTful API на backend для сохранения. Подробности о протоколе доступны здесь.
Начнем с включения отправки изменений на стороне клиента:
gantt.config.date_format = "%Y-%m-%d %H:%i:%s";
gantt.init("gantt_here");
gantt.load("/api/data");
var dp = new gantt.dataProcessor("/api");
dp.init(gantt);
dp.setTransactionMode("REST");
Далее создайте контроллеры для задач и связей и реализуйте необходимые действия.
Создайте контроллер для задач:
rails generate controller task --no-helper --no-assets --no-view-specs
Поскольку этому контроллеру не требуются представления, флаги --no-*
используются для пропуска ненужных файлов.
Реализуйте действия для создания, обновления и удаления задач:
class TaskController < ApplicationController
protect_from_forgery
def update
task = Task.find(params["id"])
task.text = params["text"]
task.start_date = params["start_date"]
task.duration = params["duration"]
task.progress = params["progress"] || 0
task.parent = params["parent"]
task.save
render :json => { :action => "updated" }
end
def add
task = Task.create(
:text => params["text"],
:start_date => params["start_date"],
:duration => params["duration"],
:progress => params["progress"] || 0,
:parent => params["parent"]
)
render :json => { :action => "inserted", :tid => task.id }
end
def delete
Task.find(params["id"]).destroy
render :json => { :action => "deleted" }
end
end
Несколько примечаний:
- Действие get
не нужно, так как данные загружаются через gantt#data
.
- Свойство progress
по умолчанию равно 0
, если не инициализировано клиентом.
- Действие для создания новых элементов возвращает ID записи в базе данных клиенту.
Обновите конфигурацию маршрутов:
Rails.application.routes.draw do
root :to => "gantt#index"
scope '/api' do
get "/data", :to => "gantt#data"
post "/task", :to => "task#add"
put "/task/:id", :to => "task#update"
delete "/task/:id", :to => "task#delete"
end
end
Тот же подход используется для связей.
Чтобы начать, создайте контроллер для связей, используя следующую команду:
rails generate controller link --no-helper --no-assets --no-view-specs
Вот пример того, как может выглядеть реализация:
app/controllers/link_controller.rb
class LinkController < ApplicationController
protect_from_forgery
def update
link = Link.find(params["id"])
link.source = params["source"]
link.target = params["target"]
link.link_type = params["type"]
link.save
render :json => {:action => "updated"}
end
def add
link = Link.create(
:source => params["source"],
:target => params["target"],
:link_type => params["type"]
)
render :json => {:action => "inserted", :tid => link.id}
end
def delete
Link.find(params["id"]).destroy
render :json => {:action => "deleted"}
end
end
Далее настройте маршруты для этих действий:
config/routes.rb
Rails.application.routes.draw do
root :to => "gantt#index"
scope '/api' do
get "/data", :to => "gantt#data"
post "/task", :to => "task#add"
put "/task/:id", :to => "task#update"
delete "/task/:id", :to => "task#delete"
post "/link", :to => "link#add" put "/link/:id", :to => "link#update" delete "/link/:id", :to => "link#delete" end
end
На этом этапе, если вы запустите свое приложение, у вас будет интерактивная диаграмма Ганта на базе Rails и MySQL.
Для получения дополнительной информации о dhtmlxGantt ознакомьтесь с нашими руководствами.
Диаграмма Ганта на стороне клиента поддерживает перестановку задач с помощью перетаскивания. Если вы хотите использовать эту функцию, вам нужно будет сохранить порядок задач в базе данных. Общие сведения об этом процессе можно найти здесь.
Давайте добавим эту функциональность в приложение.
Чтобы пользователи могли переставлять задачи в интерфейсе, обновите конфигурацию Ганта в представлении Index:
app/views/gantt/index.html.erb
gantt.config.order_branch = true;gantt.config.order_branch_free = true;
gantt.init("gantt_here");
Чтобы хранить порядок задач, добавьте поле sortorder
в модель. Вы можете либо создать новую модель, либо изменить существующую.
Чтобы создать новую модель:
rails generate model Task \
text:string \
start_date:datetime \
duration:integer \
parent:integer \
progress:decimal \
sortorder:integer
Чтобы изменить существующую модель:
rails generate migration add_sortorder_to_tasks sortorder:integer
sortorder
в файл миграции:class AddSortorderToTasks < ActiveRecord::Migration[5.1]
def change
add_column :tasks, :sortorder, :integer, :default=>0
end
end
rake db:migrate
data
возвращает задачи, отсортированные по столбцу sortorder
:app/controllers/gantt_controller.rb
class GanttController < ApplicationController
def index
end
def data
tasks = Task.all
links = Link.all
render :json=>{
:data => tasks.order(:sortorder).map{|task|{ :id => task.id,
:text => task.text,
:start_date => task.start_date.to_formatted_s(:db),
:duration => task.duration,
:progress => task.progress,
:parent => task.parent,
:open => true
}},
:links => links.map{|link|{
:id => link.id,
:source => link.source,
:target => link.target,
:type => link.link_type
}}
}
end
end
sortorder
при добавлении новых задач:app/controllers/task_controller.rb
class TaskController < ApplicationController
...
def add
maxOrder = Task.maximum("sortorder") || 0
task = Task.create(
:text => params["text"],
:start_date=> params["start_date"],
:duration => params["duration"],
:progress => params["progress"] || 0,
:parent => params["parent"],
:sortorder => maxOrder + 1 )
render :json => {:action => "inserted", :tid => task.id}
end
end
app/controllers/task_controller.rb
class TaskController < ApplicationController
protect_from_forgery
def update
task = Task.find(params["id"])
task.text = params["text"]
task.start_date = params["start_date"]
task.duration = params["duration"]
task.progress = params["progress"] || 0
task.parent = params["parent"]
task.save
if(params['target']) Task.updateOrder(task.id, params['target']) end
render :json => {:action => "updated"}
end
...
end
app/models/task.rb
class Task < ApplicationRecord
def self.updateOrder(taskId, target)
nextTask = false
targetId = target
if(target.start_with?('next:'))
targetId = target['next:'.length, target.length]
nextTask = true;
end
if(targetId == 'null')
return
end
targetTask = self.find(targetId)
targetOrder = targetTask.sortorder
if(nextTask)
targetOrder += 1
end
self.where("sortorder >= ?", targetOrder).
update_all('sortorder = sortorder + 1')
task = self.find(taskId)
task.sortorder = targetOrder
task.save
end
end
Имейте в виду, что Gantt не включает встроенную защиту от угроз, таких как SQL-инъекции, XSS или CSRF-атаки. Обеспечение безопасности вашего приложения лежит на разработчиках, занимающихся backend. Дополнительные сведения можно найти здесь.
Если задачи и связи не отображаются на странице после выполнения шагов, обратитесь к руководству по устранению неполадок в Устранение проблем с интеграцией бэкенда для помощи в выявлении и решении проблем.
Теперь ваша диаграмма Ганта полностью функциональна. Вы можете найти полный код на GitHub. Не стесняйтесь клонировать или загружать его для своих проектов.
Для получения дополнительных функций исследуйте наши руководства или ознакомьтесь с учебными пособиями по интеграции Gantt с другими backend-фреймворками.
К началу