В этом руководстве описывается, как интегрировать dhtmlxGantt в Salesforce Lightning Web Component.
Если вы работаете с другой технологией, ниже представлены другие варианты интеграции:
Процесс включает использование Salesforce CLI для создания Lightning Web Component и его деплоя в организацию Salesforce. Для более удобной разработки рекомендуется установить Salesforce Extension Pack в Visual Studio Code.
Полный исходный код размещён на GitHub.
Также доступен видеоурок, демонстрирующий создание диаграммы Gantt с помощью Salesforce LWC.
Убедитесь, что Salesforce CLI установлен. Если нет, воспользуйтесь этой инструкцией для установки.
Если у вас ещё нет аккаунта, создайте бесплатную учётную запись разработчика, зарегистрировавшись. Подробнее — в этом руководстве.
В Salesforce воспользуйтесь строкой поиска слева для поиска и выбора Dev Hub:
На открывшейся странице настроек включите функцию Dev Hub:
Затем создайте директорию для вашего проекта Salesforce DX:
$ mkdir ~/salesforce
Сгенерируйте проект Salesforce DX с помощью CLI:
$ cd ~/salesforce
$ sfdx project generate -n gantt-salesforce-app
target dir = C:\Users\User\salesforce
create gantt-salesforce-app\config\project-scratch-def.json
create gantt-salesforce-app\README.md
create gantt-salesforce-app\sfdx-project.json
create gantt-salesforce-app\.husky\pre-commit
create gantt-salesforce-app\.vscode\extensions.json
create gantt-salesforce-app\.vscode\launch.json
create gantt-salesforce-app\.vscode\settings.json
create gantt-salesforce-app\force-app\main\default\lwc\.eslintrc.json
create gantt-salesforce-app\force-app\main\default\aura\.eslintrc.json
create gantt-salesforce-app\scripts\soql\account.soql
create gantt-salesforce-app\scripts\apex\hello.apex
create gantt-salesforce-app\.eslintignore
create gantt-salesforce-app\.forceignore
create gantt-salesforce-app\.gitignore
create gantt-salesforce-app\.prettierignore
create gantt-salesforce-app\.prettierrc
create gantt-salesforce-app\jest.config.js
create gantt-salesforce-app\package.json
Перейдите в только что созданную папку проекта:
$ cd gantt-salesforce-app
Используйте Web Server Flow для авторизации в организации:
$ sfdx org login web -d
Successfully authorized ... with org ID ...
Далее обновите файл конфигурации проекта (sfdx-project.json), установив параметр "sfdcLoginUrl" на "My Domain URL" вашей организации. Найти этот URL можно на странице настроек "My Domain". Например:
gantt-salesforce-app/sfdx-project.json
"sfdcLoginUrl" : "https://xbs2-dev-ed.my.salesforce.com"
Создайте Scratch Org с помощью следующей команды:
$ sfdx org create scratch -f config/project-scratch-def.json -d
Creating Scratch Org...
RequestId: 2SR5j0000006JhCGAU
(https://xbsoftware2-dev-ed.my.salesforce.com/2SR5j0000006JhCGAU)
OrgId: 00DH40000000s0D
Username: test-tc0telfqhudt@example.com
✓ Prepare Request
✓ Send Request
✓ Wait For Org
✓ Available
✓ Authenticate
✓ Deploy Settings
Done
Your scratch org is ready.
Чтобы использовать библиотеку, её необходимо загрузить в Salesforce как Static Resource. Откройте ваш scratch org:
$ sfdx org open
Перейдите на вкладку "Static Resources" и нажмите кнопку "New":
Укажите понятное имя (например, "dhtmlxgantt7111"), загрузите ZIP-архив с файлами библиотеки (dhtmlxgantt.js и dhtmlxgantt.css), и установите Cache Control в значение "Public" для повышения производительности. Затем сохраните изменения.
Теперь библиотека dhtmlxGantt доступна внутри Salesforce.
Основные компоненты dhtmlxGantt — это задачи (Tasks) и связи (Links). Практичный способ их хранения — сохранять их свойства в формате JSON в Salesforce. Начните с создания пользовательских объектов для задач и связей. Перейдите в Object Manager, затем выберите "Create" и выберите "Custom Object":
Назовите объект задачи, например, GanttTask или GanttTasks.
Убедитесь, что имя записи соответствует имени объекта, например:
Object Name: GanttTask => Record Name: GanttTask Name
Сохраните новый объект.
Затем откройте вкладку "Fields & Relationships" и нажмите "New" для добавления полей:
Выберите тип данных "Number" и продолжайте.
Назовите поле "Duration". Это поле будет содержать сериализованные в JSON свойства задачи. Нажимайте "Next" до появления кнопки "Save & New".
Примите настройки по умолчанию, нажимая "Next", пока не сможете сохранить или добавить новое поле.
Создайте поле "Parent" с типом данных "Text".
Проходите шаги, нажимая "Next", до появления кнопки "Save & New".
Добавьте поле "Progress", выбрав тип данных "Number".
Продолжайте нажимать "Next", пока не сможете сохранить или добавить следующее поле.
Создайте поле "Start Date" с типом данных "Date/Time".
Пройдите по умолчанию все шаги до кнопки "Save".
В результате поля вашего объекта должны выглядеть так:
Начните с открытия Object Manager и выберите "Create", затем "Custom Object":
Назовите объект ссылок как GanttLink/GanttLinks.
Убедитесь, что имя записи соответствует имени объекта, например:
Object Name: GanttLink => Record Name: GanttLink Name
Далее создайте необходимые поля.
Добавьте поле "Source" и выберите "Text" в качестве Data Type.
Нажимайте "Next" (оставляя настройки по умолчанию), пока не появится кнопка "Save & New".
Добавьте поле "Target" с типом данных "Text".
Нажимайте "Next" (принимая настройки по умолчанию), пока не станет доступна кнопка "Save & New".
Добавьте поле "Type", также с типом данных "Text".
Нажимайте "Next" (принимая значения по умолчанию), пока не появится кнопка "Save".
В итоге должно получиться следующее:
Для создания Lightning Web Component выполните следующую команду:
$ sfdx lightning generate component --type lwc -n gantt -d force-app/main/default/lwc
target dir =
C:\Users\User\source\salesforce\gantt-salesforce-app\force-app\main\default\lwc
create force-app\main\default\lwc\gantt\gantt.js
create force-app\main\default\lwc\gantt\gantt.html
create force-app\main\default\lwc\gantt\gantt.js-meta.xml
Обновите определение компонента в gantt.js-meta.xml, чтобы сделать его доступным в Lightning App Builder:
force-app/main/default/lwc/gantt/gantt.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>54.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__AppPage</target>
</targets>
<targetConfigs>
<targetConfig targets="lightning__AppPage">
<property name="height" label="Height" type="Integer" default="800" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Откройте gantt.html и вставьте следующий код:
force-app/main/default/lwc/gantt/gantt.html
<template>
<div class="thegantt" lwc:dom="manual" style='width: 100%;'></div>
</template>
В gantt.js добавьте следующий код:
force-app/main/default/lwc/gantt/gantt.js
/* eslint-disable guard-for-in */
/* eslint-disable no-undef */
import { LightningElement, api } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
import { loadStyle, loadScript } from "lightning/platformResourceLoader";
import { createRecord, updateRecord, deleteRecord } from "lightning/uiRecordApi";
// Static resources
import GanttFiles from "@salesforce/resourceUrl/dhtmlxgantt7111";
// Controllers
import getTasks from "@salesforce/apex/GanttData.getTasks";
function unwrap(fromSF) {
const data = fromSF.tasks.map((a) => ({
id: a.Id,
text: a.Name,
start_date: a.Start_Date__c,
duration: a.Duration__c,
parent: a.Parent__c,
progress: a.Progress__c,
type: a.Task_Type__c,
}));
const links = fromSF.links.map((a) => ({
id: a.Id,
source: a.Source__c,
target: a.Target__c,
type: a.Type__c
}));
return { data, links};
}
export default class GanttView extends LightningElement {
static delegatesFocus = true;
@api height;
ganttInitialized = false;
renderedCallback() {
if (this.ganttInitialized) {
return;
}
this.ganttInitialized = true;
Promise.all([
loadScript(this, GanttFiles + "/dhtmlxgantt.js"),
loadStyle(this, GanttFiles + "/dhtmlxgantt.css")
])
.then(() => {
this.initializeUI();
})
.catch((error) => {
this.dispatchEvent(
new ShowToastEvent({
title: "Error loading Gantt",
message: error.message,
variant: "error"
})
);
});
}
initializeUI() {
const root = this.template.querySelector(".thegantt");
root.style.height = this.height + "px";
//uncomment the following line if you use the Enterprise or Ultimate version
//const gantt = window.Gantt.getGanttInstance();
gantt.templates.parse_date = (date) => new Date(date);
gantt.templates.format_date = (date) => date.toISOString();
gantt.init(root);
getTasks().then((d) => {
const chartData = unwrap(d);
gantt.parse({
tasks: chartData.data,
links: chartData.links
});
});
///↓↓↓ saving changes back to SF backend ↓↓↓
gantt.createDataProcessor({
task: {
create: (data) => {
console.log("createTask",data);
const insert = {
apiName: "GanttTask__c",
fields: {
Name: data.text,
Start_Date__c: data.start_date,
Duration__c: data.duration,
Parent__c: String(data.parent),
Progress__c: data.progress
}
};
gantt.config.readonly = true; // suppress changes
// until saving is complete
return createRecord(insert).then((res) => {
gantt.config.readonly = false;
return { tid: res.id, ...res };
});
},
update: (data, id) => {
console.log("updateTask",data);
const update = {
fields: {
Id: id,
Name: data.text,
Start_Date__c: data.start_date,
Duration__c: data.duration,
Parent__c: String(data.parent),
Progress__c: data.progress
}
};
return updateRecord(update).then(() => ({}));
},
delete: (id) => {
return deleteRecord(id).then(() => ({}));
}
},
link: {
create: (data) => {
const insert = {
apiName: "GanttLink__c",
fields: {
Source__c: data.source,
Target__c: data.target,
Type__c: data.type
}
};
return createRecord(insert).then((res) => {
return { tid: res.id };
});
},
update: (data, id) => {
const update = {
apiName: "GanttLink__c",
fields: {
Id: id,
Source__c: data.source,
Target__c: data.target,
Type__c: data.type
}
};
return updateRecord(update).then(() => ({}));
},
delete: (id) => {
return deleteRecord(id).then(() => ({}));
}
}
});
}
}
Далее создайте класс, который будет обеспечивать связь между Lightning компонентом и моделью данных.
$ sfdx apex generate class -n GanttData -d force-app/main/default/classes
target dir =
C:\Users\User\salesforce\gantt-salesforce-app\force-app\main\default\classes
create force-app\main\default\classes\GanttData.cls
create force-app\main\default\classes\GanttData.cls-meta.xml
После создания класса откройте GanttData.cls и добавьте следующий код:
force-app/main/default/classes/GanttData.cls
public with sharing class GanttData {
@RemoteAction
@AuraEnabled(cacheable=true)
public static Map<String, Object> getTasks() {
// fetching the Records via SOQL
List<GanttTask__c> Tasks = new List<GanttTask__c>();
Tasks = [SELECT Id, Name, Start_Date__c, Duration__c,
Parent__c FROM GanttTask__c];
List<GanttLink__c> Links = new List<GanttLink__c>();
Links = [SELECT Id, Type__c, Source__c, Target__c FROM GanttLink__c];
Map<String, Object> result = new Map<String, Object>{
'tasks' => Tasks, 'links' => Links };
return result;
}
}
Выполните команду для получения исходников из Scratch Org в ваш проект:
$ sfdx project retrieve start
Затем задеплойте исходники обратно в Scratch Org:
$ sfdx project deploy start
Откройте "Lightning App Builder" и создайте новую Lightning Page.
Выберите “App Page” и укажите имя страницы и макет.
Кастомный компонент Gantt должен быть доступен для новой страницы. Добавьте его в любой раздел и сохраните.
Активируйте страницу.
Сохраните изменения.
Откройте страницу приложения. Она будет доступна через app launcher по поиску Gantt.
Если всё настроено верно, на Lightning Page появится простой пример Gantt.
Сам Gantt не содержит встроенных средств защиты от угроз, таких как SQL-инъекции, XSS или CSRF-атаки. За безопасность приложения отвечают разработчики, внедряющие его. Подробнее см. в соответствующей статье. Salesforce предоставляет мощные инструменты для защиты ваших данных и приложений. Вы также можете адаптировать подход к безопасности под структуру и требования вашей организации. Дополнительные рекомендации смотрите в Salesforce Security Guide. Информация по защите Lightning компонентов доступна здесь.
Если вы выполнили все шаги, но диаграмма Gantt не отображает задачи и связи на странице, обратитесь к статье Решение проблем с интеграцией бэкенда. В ней описаны методы диагностики и устранения типовых проблем.
Когда Gantt полностью настроен, вы можете ознакомиться с полным кодом на GitHub, где его можно клонировать или скачать для поддержки ваших проектов.
Также рекомендуем изучить руководства по различным функциям Gantt или туториалы по интеграции Gantt с другими backend-фреймворками.
К началу