Save all currently accumulated repository changes as a backup snapshot for Gitea so no local work is lost.
468 lines
22 KiB
Markdown
468 lines
22 KiB
Markdown
# 🎨 UX ДИЗАЙН: СОЗДАНИЕ И РЕДАКТИРОВАНИЕ ШАБЛОНОВ
|
||
|
||
## 🎯 ВОПРОС: ГДЕ РЕДАКТИРОВАТЬ ШАБЛОНЫ?
|
||
|
||
---
|
||
|
||
## 📋 ВАРИАНТЫ РЕШЕНИЯ
|
||
|
||
### **ВАРИАНТ 1: ГИБРИДНЫЙ ПОДХОД (РЕКОМЕНДУЕТСЯ) ✅**
|
||
|
||
**Создание шаблона:**
|
||
- В CRM интерфейсе
|
||
- Можно создать пустой шаблон или загрузить существующий файл
|
||
|
||
**Редактирование шаблона:**
|
||
- **Содержимое (DOCX)** → в Nextcloud через OnlyOffice
|
||
- **Метаданные и маппинг** → в CRM интерфейсе
|
||
|
||
**Преимущества:**
|
||
- ✅ Удобно редактировать DOCX в OnlyOffice (богатый редактор)
|
||
- ✅ Метаданные в CRM (логика рядом с данными)
|
||
- ✅ Гибкость - можно редактировать где удобно
|
||
|
||
---
|
||
|
||
### **ВАРИАНТ 2: ТОЛЬКО В NEXTCLOUD**
|
||
|
||
**Создание/редактирование:**
|
||
- Все в Nextcloud
|
||
- В CRM только настройка маппинга
|
||
|
||
**Недостатки:**
|
||
- ❌ Нужно переключаться между системами
|
||
- ❌ Маппинг отдельно от шаблона
|
||
|
||
---
|
||
|
||
### **ВАРИАНТ 3: ТОЛЬКО В CRM**
|
||
|
||
**Создание/редактирование:**
|
||
- Все в CRM
|
||
- Загрузка файла через интерфейс CRM
|
||
|
||
**Недостатки:**
|
||
- ❌ Нет удобного редактора DOCX в CRM
|
||
- ❌ Дублирование файлов (в CRM и Nextcloud)
|
||
|
||
---
|
||
|
||
## 🏆 РЕКОМЕНДУЕМЫЙ ПОДХОД: ГИБРИДНЫЙ
|
||
|
||
---
|
||
|
||
## 📝 СЦЕНАРИЙ 1: СОЗДАНИЕ НОВОГО ШАБЛОНА
|
||
|
||
### **В CRM интерфейсе:**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Создание шаблона документа │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Название шаблона: [________________] │
|
||
│ │
|
||
│ Модуль: [Project ▼] │
|
||
│ │
|
||
│ Файл шаблона: │
|
||
│ ○ Создать пустой шаблон │
|
||
│ ○ Загрузить существующий файл │
|
||
│ [Выбрать файл...] │
|
||
│ │
|
||
│ Имя файла в Nextcloud: [pretenziya.docx] │
|
||
│ │
|
||
│ [Сохранить и настроить маппинг] │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Что происходит:**
|
||
|
||
1. **Если "Создать пустой шаблон":**
|
||
- CRM создает пустой DOCX через PHPWord
|
||
- Загружает в Nextcloud `/Templates/pretenziya.docx`
|
||
- Создает запись в `vtiger_doctemplate`
|
||
- Открывает шаблон в OnlyOffice для редактирования
|
||
|
||
2. **Если "Загрузить существующий файл":**
|
||
- Пользователь выбирает файл
|
||
- CRM загружает в Nextcloud `/Templates/`
|
||
- Создает запись в `vtiger_doctemplate`
|
||
- Переходит к настройке маппинга
|
||
|
||
---
|
||
|
||
## 📝 СЦЕНАРИЙ 2: РЕДАКТИРОВАНИЕ ШАБЛОНА
|
||
|
||
### **В CRM интерфейсе (детальный вид шаблона):**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Шаблон: Претензия [Редактировать] │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Модуль: Project │
|
||
│ Файл: pretenziya.docx │
|
||
│ │
|
||
│ [📝 Редактировать содержимое в OnlyOffice] │
|
||
│ → Откроет файл в Nextcloud для редактирования │
|
||
│ │
|
||
│ ──────────────────────────────────────────────────── │
|
||
│ │
|
||
│ Маппинг переменных: │
|
||
│ │
|
||
│ Переменная шаблона → Поле модуля │
|
||
│ ┌─────────────────┐ ┌──────────────────────┐ │
|
||
│ │ CLIENT_NAME │ → │ $PROJECT_PROJECTNAME$│ │
|
||
│ └─────────────────┘ └──────────────────────┘ │
|
||
│ │
|
||
│ ┌─────────────────┐ ┌──────────────────────┐ │
|
||
│ │ DATE │ → │ $PROJECT_CREATEDTIME$ │ │
|
||
│ └─────────────────┘ └──────────────────────┘ │
|
||
│ │
|
||
│ [+ Добавить переменную] │
|
||
│ │
|
||
│ ──────────────────────────────────────────────────── │
|
||
│ │
|
||
│ [💾 Сохранить маппинг] │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Кнопка "Редактировать содержимое":**
|
||
- Открывает файл в OnlyOffice через Nextcloud
|
||
- Пользователь редактирует DOCX
|
||
- Сохраняет в Nextcloud
|
||
- CRM автоматически подхватывает изменения
|
||
|
||
---
|
||
|
||
## 🔧 РЕАЛИЗАЦИЯ
|
||
|
||
### **1. Создание пустого шаблона**
|
||
|
||
```php
|
||
class DocTemplate_Template_Model {
|
||
|
||
/**
|
||
* Создать пустой шаблон в Nextcloud
|
||
*/
|
||
public function createEmptyTemplate($filename) {
|
||
// 1. Создать пустой DOCX через PHPWord
|
||
$phpWord = new \PhpOffice\PhpWord\PhpWord();
|
||
$section = $phpWord->addSection();
|
||
$section->addText('Шаблон документа');
|
||
|
||
$tempFile = tempnam(sys_get_temp_dir(), 'template_') . '.docx';
|
||
$writer = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007');
|
||
$writer->save($tempFile);
|
||
|
||
// 2. Загрузить в Nextcloud через WebDAV
|
||
$this->uploadToNextcloud($tempFile, $filename);
|
||
|
||
// 3. Удалить временный файл
|
||
unlink($tempFile);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Загрузка файла в Nextcloud
|
||
*/
|
||
private function uploadToNextcloud($localFile, $filename) {
|
||
$nextcloudUrl = 'https://office.clientright.ru:8443';
|
||
$username = 'admin';
|
||
$password = 'office';
|
||
$path = '/Templates/';
|
||
|
||
$webdavUrl = $nextcloudUrl . '/remote.php/dav/files/' .
|
||
$username . $path . $filename;
|
||
|
||
$ch = curl_init($webdavUrl);
|
||
curl_setopt($ch, CURLOPT_PUT, true);
|
||
curl_setopt($ch, CURLOPT_INFILE, fopen($localFile, 'r'));
|
||
curl_setopt($ch, CURLOPT_INFILESIZE, filesize($localFile));
|
||
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
|
||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||
|
||
curl_exec($ch);
|
||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
if ($httpCode !== 201 && $httpCode !== 204) {
|
||
throw new Exception("Ошибка загрузки в Nextcloud");
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **2. Загрузка существующего файла**
|
||
|
||
```php
|
||
class DocTemplate_Edit_View {
|
||
|
||
public function process(Vtiger_Request $request) {
|
||
if ($request->has('file_upload')) {
|
||
$uploadedFile = $_FILES['template_file'];
|
||
|
||
// 1. Валидация файла
|
||
if ($uploadedFile['type'] !== 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') {
|
||
throw new Exception("Только DOCX файлы");
|
||
}
|
||
|
||
// 2. Загрузить в Nextcloud
|
||
$filename = $request->get('filename');
|
||
$this->uploadToNextcloud($uploadedFile['tmp_name'], $filename);
|
||
|
||
// 3. Создать запись в CRM
|
||
$templateModel = new DocTemplate_Template_Model();
|
||
$templateModel->set('templatename', $request->get('templatename'));
|
||
$templateModel->set('filename', $filename);
|
||
$templateModel->set('module', $request->get('module'));
|
||
$templateModel->save();
|
||
|
||
// 4. Редирект на редактирование маппинга
|
||
header("Location: index.php?module=DocTemplate&view=Edit&record=" . $templateModel->getId());
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **3. Редактирование содержимого (открытие в OnlyOffice)**
|
||
|
||
```php
|
||
class DocTemplate_EditContent_Action {
|
||
|
||
public function process(Vtiger_Request $request) {
|
||
$templateId = $request->get('record');
|
||
$template = DocTemplate_Template_Model::getInstanceById($templateId);
|
||
|
||
// Получить URL для открытия в OnlyOffice
|
||
$nextcloudUrl = 'https://office.clientright.ru:8443';
|
||
$filename = $template->getFilename();
|
||
$path = $template->getNextcloudPath();
|
||
|
||
// URL для открытия в OnlyOffice
|
||
$onlyOfficeUrl = $nextcloudUrl . '/apps/files/?dir=' .
|
||
urlencode($path) .
|
||
'&openfile=' . urlencode($filename);
|
||
|
||
// Или через прямой вызов OnlyOffice
|
||
$onlyOfficeUrl = $nextcloudUrl . '/apps/onlyoffice/' .
|
||
urlencode($path . $filename);
|
||
|
||
header("Location: " . $onlyOfficeUrl);
|
||
exit;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **4. Интерфейс редактирования маппинга**
|
||
|
||
**JavaScript:**
|
||
|
||
```javascript
|
||
DocTemplate = {
|
||
|
||
/**
|
||
* Показать редактор маппинга
|
||
*/
|
||
showMappingEditor: function(templateId, module) {
|
||
// 1. Получить список полей модуля (как PDFMaker)
|
||
AppConnector.request({
|
||
module: 'DocTemplate',
|
||
action: 'GetModuleFields',
|
||
module_name: module
|
||
}).then(function(response) {
|
||
// 2. Показать модальное окно с редактором маппинга
|
||
DocTemplate.showMappingDialog(response.result.fields, templateId);
|
||
});
|
||
},
|
||
|
||
/**
|
||
* Диалог редактирования маппинга
|
||
*/
|
||
showMappingDialog: function(fields, templateId) {
|
||
var html = '<div class="mapping-editor">' +
|
||
'<table class="table">' +
|
||
'<thead><tr><th>Переменная шаблона</th><th>Поле модуля</th><th></th></tr></thead>' +
|
||
'<tbody id="mapping-rows"></tbody>' +
|
||
'</table>' +
|
||
'<button onclick="DocTemplate.addMappingRow()">+ Добавить</button>' +
|
||
'</div>';
|
||
|
||
// Показать модальное окно
|
||
// При сохранении → вызвать SaveMapping
|
||
},
|
||
|
||
/**
|
||
* Сохранение маппинга
|
||
*/
|
||
saveMapping: function(templateId, mapping) {
|
||
AppConnector.request({
|
||
module: 'DocTemplate',
|
||
action: 'SaveMapping',
|
||
template_id: templateId,
|
||
mapping: JSON.stringify(mapping)
|
||
}).then(function(response) {
|
||
if (response.success) {
|
||
Vtiger_Helper_Js.showPnotify('Маппинг сохранен');
|
||
}
|
||
});
|
||
}
|
||
};
|
||
```
|
||
|
||
---
|
||
|
||
## 🎨 UI КОМПОНЕНТЫ
|
||
|
||
### **1. Список шаблонов (List View)**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Шаблоны документов [+ Создать] │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Название │ Модуль │ Файл │ Действия│
|
||
│ ───────────────────────────────────────────────────── │
|
||
│ Претензия │ Project │ pretenziya.docx │ [✏️][👁️]│
|
||
│ Исковое заявл. │ Project │ isk.docx │ [✏️][👁️]│
|
||
│ Соглашение │ Contacts │ soglashenie.docx │ [✏️][👁️]│
|
||
│ │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
### **2. Детальный вид шаблона**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Претензия [Редактировать] [×] │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Модуль: Project │
|
||
│ Файл: pretenziya.docx │
|
||
│ Путь: /Templates/ │
|
||
│ │
|
||
│ [📝 Редактировать содержимое в OnlyOffice] │
|
||
│ │
|
||
│ ──────────────────────────────────────────────────── │
|
||
│ │
|
||
│ Маппинг переменных: │
|
||
│ │
|
||
│ ┌──────────────────────────────────────────────────┐ │
|
||
│ │ Переменная │ Поле модуля │ [×] │ │
|
||
│ ├──────────────────────────────────────────────────┤ │
|
||
│ │ CLIENT_NAME │ $PROJECT_PROJECTNAME$ │ [×] │ │
|
||
│ │ DATE │ $PROJECT_CREATEDTIME$ │ [×] │ │
|
||
│ │ AMOUNT │ $PROJECT_CF_1885$ │ [×] │ │
|
||
│ └──────────────────────────────────────────────────┘ │
|
||
│ │
|
||
│ [+ Добавить переменную] │
|
||
│ │
|
||
│ [💾 Сохранить] │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
### **3. Диалог добавления переменной**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ Добавить переменную │
|
||
├─────────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ Переменная шаблона: │
|
||
│ [CLIENT_NAME________________] │
|
||
│ │
|
||
│ Поле модуля: │
|
||
│ [Выбрать поле ▼] │
|
||
│ ├─ Прямые поля │
|
||
│ │ ├─ $PROJECT_PROJECTNAME$ │
|
||
│ │ ├─ $PROJECT_CREATEDTIME$ │
|
||
│ │ └─ ... │
|
||
│ ├─ Связанные модули │
|
||
│ │ ├─ $CONTACTS_LASTNAME$ │
|
||
│ │ └─ ... │
|
||
│ └─ Функции │
|
||
│ ├─ formatDate($PROJECT_CREATEDTIME$) │
|
||
│ └─ ... │
|
||
│ │
|
||
│ [Отмена] [Добавить] │
|
||
│ │
|
||
└─────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 🔄 АЛЬТЕРНАТИВНЫЙ ПОДХОД: ВСТРОЕННЫЙ РЕДАКТОР
|
||
|
||
### **Вариант: Редактирование прямо в CRM**
|
||
|
||
Если нужен встроенный редактор в CRM (без перехода в Nextcloud):
|
||
|
||
```php
|
||
// Использовать OnlyOffice Document Server API
|
||
// Встроить редактор в iframe
|
||
|
||
class DocTemplate_EditContent_View {
|
||
|
||
public function process(Vtiger_Request $request) {
|
||
$templateId = $request->get('record');
|
||
$template = DocTemplate_Template_Model::getInstanceById($templateId);
|
||
|
||
// Получить URL для OnlyOffice Document Server
|
||
$onlyOfficeUrl = $this->getOnlyOfficeEditorUrl($template);
|
||
|
||
$viewer = $this->getViewer($request);
|
||
$viewer->assign('ONLYOFFICE_URL', $onlyOfficeUrl);
|
||
$viewer->assign('TEMPLATE', $template);
|
||
$viewer->view('EditContent.tpl', 'DocTemplate');
|
||
}
|
||
}
|
||
```
|
||
|
||
**Template:**
|
||
```html
|
||
<iframe src="{$ONLYOFFICE_URL}" width="100%" height="800px"></iframe>
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ РЕКОМЕНДАЦИИ
|
||
|
||
### **Для создания шаблона:**
|
||
1. ✅ В CRM интерфейсе
|
||
2. ✅ Можно создать пустой или загрузить файл
|
||
3. ✅ Автоматическая загрузка в Nextcloud
|
||
|
||
### **Для редактирования содержимого:**
|
||
1. ✅ В Nextcloud через OnlyOffice (богатый редактор)
|
||
2. ✅ Кнопка "Редактировать содержимое" открывает в новой вкладке
|
||
3. ✅ После сохранения в Nextcloud - изменения сразу доступны
|
||
|
||
### **Для редактирования маппинга:**
|
||
1. ✅ В CRM интерфейсе
|
||
2. ✅ Визуальный редактор с выбором полей
|
||
3. ✅ Поддержка формата PDFMaker (`$MODULE_FIELDNAME$`)
|
||
|
||
---
|
||
|
||
## 🚀 ГОТОВ РЕАЛИЗОВАТЬ!
|
||
|
||
**Какой вариант тебе больше нравится?**
|
||
|
||
1. **Гибридный** (содержимое в Nextcloud, маппинг в CRM) - РЕКОМЕНДУЮ ✅
|
||
2. **Только в CRM** (встроенный редактор)
|
||
3. **Только в Nextcloud** (маппинг отдельно)
|