Onlyoffice и Р7 офис: макросы на Javascript (туториал)

от автора

Всем привет, я – Виталий Квитковский, руководитель программистов в небольшой государственной компании. В этом туториале я расскажу про базовые возможности написания макросов в программе «Р7 офис» – российском пакете, являющемся ответвлением Onlyoffice. Я пробовал запускать код в обеих программах, он работает одинаково. Есть некоторые отличия в деталях интерфейса, но в целом базовые функции работают одинаково.

Главная причина, почему мне так нравятся макросы в этих пакетах – я веб-разработчик, а эти макросы пишутся на Javascript 🙂 Они, разумеется, не будут работать в Microsoft Office и других версиях офиса, но макросы чаще создаются для упрощения работы того, кто редактирует файл, а не для тех, кто потом будет смотреть на результат, поэтому для меня этого было вполне достаточно. В этой статье я также ограничусь макросами для таблиц, поскольку большая часть работы в нашей компании происходит именно с Excel-файлами.

В сети уже есть туториал, однако он больше построен по принципу «что можно сделать с данными с помощью нашего API». А я хотел бы построить эту статью по принципу «какие базовые микрозадачи возникают в процессе решения большинства задач и как их решать». Также мы порадуемся доступности большинства функционала классического Javascript на примере работы со строками, объект Math и даже fetch.

Предварительная подготовка

Для того, чтобы нам удобно было разрабатывать макросы, нам понадобится отладчик. Чтобы запускать программу с работающим отладчиком по умолчанию, нужно добавить ключ запуска —ascdesktop-support-debug-info. Например, в Windows это можно сделать в свойствах ярлыка на «Р7 офис», указав во вкладке «Ярлык» в поле «Объект»

"C:\Program Files\R7-Office\Editors\DesktopEditors.exe" --ascdesktop-support-debug-info

Теперь запустите программу, найдите в верхнем меню самую правую вкладку «Плагины», а в ней нажмите кнопку «Макросы». (В Onlyoffice версии 8.2.2, актуальной на 1 декабря 2024 года, кнопка «Макросы» перенесена во вкладку «Вид»). Здесь вы можете создавать макросы и запускать их. К сожалению, окно с кодом загораживает большую часть экрана, для того, чтобы посмотреть, как макрос повлиял на содержимое, зачастую приходится закрыть окно с кодом нажатием кнопки «Ок» и затем снова вернуться в него нажатием на «Макрос».

Тем не менее, отладочную информацию можно получить и без этого. Просто нажав F1 при открытом окне макроса, вы увидите Chrome dev tools! А это значит, что наши любимые console.log и console.dir выведут нам всю полезную отладочную информацию. Именно для работы Chrome dev tools нам и понадобился специальный параметр при запуске.

При создании макроса вам сразу предлагается заготовка для запуска, и остается написать только тело функции. Напишем в консоли Hello world!

(function() {     console.log('Hello world!'); })();

Далее его можно выполнить по нажатию кнопки «Выполнить» в «Р7 офис» или кнопки play в Onlyoffice.

Работа с ячейками

Ячейки достать достаточно легко. Если вам нужна ячейка B3 на текущем активном листе, то ее можно получить с помощью

let cell = Api.GetActiveSheet().GetRange("B3");

Однако для целей перебора ячеек нам нужны номера строк и столбцов. Давайте научимся оперировать с ними.

let cell = Api.GetActiveSheet().GetRangeByNumber(2, 1);

Это работа с той же ячейкой. Здесь сначала идет строка, потом столбец, и нумерация начинается с нуля (то есть для ячейки A1 мы бы писали 0, 0). При этом чтобы получить из текущей ячейки ее индекс строки и индекс столбца, есть функции GetCol и GetRow. Что характерно, они возвращают номера столбца и строки, как если бы они нумеровались с единицы. Поэтому для получения текущей ячейки из них надо будет вычесть 1:

let cell = Api.GetActiveSheet().GetRange("B3"); let indexCol = cell.GetCol() - 1; let indexRow = cell.GetRow() - 1; let sameCell = Api.GetActiveSheet().GetRangeByNumber(indexCol, indexRow);

Вам это понадобится в тот момент, когда, например, нужно будет, начиная с текущей ячейки, перебрать несколько ячеек, допустим, вниз, и сделать их фон желтым:

let sheet = Api.GetActiveSheet(); let cell = sheet.GetRange("B3"); let indexCol = cell.GetCol() - 1; let indexRow = cell.GetRow() - 1; let color = Api.CreateColorFromRGB(255, 255, 200); for (let i = indexRow; i < indexRow + 5; i++) {     sheet.GetRangeByNumber(i, indexCol).SetFillColor(color); }

Если же вам для вставки в формулу нужно получить имя ячейки, то это делается с помощью метода GetAddress:

let cell = Api.GetActiveSheet().GetRangeByNumber(2, 1); console.log(cell.GetAddress());

даст вам B3. Если вам нужно добавить в формулу символ доллара (фиксация строки/столбца ячейки при протягивании), то нужно указывать первые два параметра как true (доллар нужен) или false (не нужен, по умолчанию). cell.GetAddress(true, false) даст B$3, а cell.GetAddress(false, true)$B3. Если вам нужен ввод формулы в стиле R1C1, то указывайте третьим параметром 'xlR1C1':

let cell = Api.GetActiveSheet().GetRangeByNumber(2, 1); console.log(cell.GetAddress(false, false, 'xlR1C1')); //выведет R[2]C[1]

Наконец, если вам нужен полный путь до ячейки с учетом названия книги, то за это отвечает четвертый параметр, надо поставить его в true:

let cell = Api.GetActiveSheet().GetRangeByNumber(2, 1); console.log(cell.GetAddress(false, false, 'xlA1', true)); //выведет //[Книга1.xlsx]'Лист1'!B3

Формат вывода номера ячейки по умолчанию называется xlA1, но в принципе вы можете вписать в качестве параметра что угодно, кроме xlR1C1 (даже true), это сработает.

Если вам нужно будет только название листа, от названия книги легко избавиться с помощью стандартных функций Javascript.

Если вы оперируете ячейками на другом листе, в выводе команды GetAddress названия листа все равно не будет (если только 4-й параметр не указан как true).

let cell = Api.GetSheet('Лист1').GetRange('B1'); cell.SetValue(cell.GetAddress()); //выведет B1

Работа с содержимым ячеек и стилями

В этом разделе будет предполагаться, что для всех примеров мы уже выполнили

let cell = Api.GetActiveSheet().GetRange("B3");

Если вы хотите получить значение ячейки, воспользуйтесь методом GetValue:

console.log(cell.GetValue());

Если вам нужна формула как строка – это метод GetFormula:

console.log(cell.GetFormula());

Если вам нужно сохранить данные в ячейку – воспользуйтесь SetValue. Формулы тоже сохраняются через SetValue, если первым символом значения является =. Названия функций в формуле можно вводить как на английском, так и на русском языках (по крайней мере, если при установке программы был выбран русский язык).

cell.SetValue('234'); cell.SetValue('=MIN(C3:C10)'); cell.SetValue('=МИН(C3:C10)');

Если вы хотите ввести в одну ячейку много строк текста, то их можно разделять через \n, но для того, чтобы отображение стало корректным, нужно встать на эту ячейку и нажать кнопку «Перенос строк», разрешив перенос строк, либо нажать дабл-клик на эту ячейку и сохранить ее. Иначе текст будет отображаться слепленным.

cell.SetValue("2\n\n3\n\n4");

Так произойдет, даже если вы получите значение из другой ячейки с множеством строк с помощью GetValue. При этом с полученным значением можно будет работать, считая, что разрывы строк – это \n, например:

let sheet = Api.GetActiveSheet(); let multilineText = sheet.GetRange("H14").GetValue(); let rowsArray = multilineText.split("\n"); console.log(rowsArray); // будет массивом с набором строк oWorksheet.GetRange("H15").SetValue(multilineText); //после выполнения макроса //нужно будет на ячейе нажать «перенос строк», чтобы она отображалась корректно

Если вам нужно внести одинаковое значение во много ячеек, то можно в GetRange указать диапазон. Чаще это используется для одинакового форматирования блока целиком:

let cells = Api.GetSheet('Лист1').GetRange('B1:D40'); //здесь и далее cells.SetValue(‘123’);

Теперь давайте разберемся с форматированием.

cells.SetBold(); //установить ячейкам жирный шрифт, с параметром false – наоборот, убрать жирный шрифт cells.SetItalic(false); //аналогично для курсива cells.SetUnderline(); //и для подчеркнутого текста cells.SetStrikeout(true); //для зачеркивания нужно указать true cells.SetNumberFormat("0.00"); //два знака после запятой; больше примеров в документации https://api.onlyoffice.com/docs/office-api/usage-api/spreadsheet-api/apirange/setnumberformat/ cells.SetFontName('Courier New'); //шрифт cells.SetFontSize(14); //размер шрифта, пунктов //значения – top | center | bottom cells.SetAlignVertical('bottom'); //вертикальное выравнивание //значения – left | center | right | justify cells.SetAlignHorizontal('justify'); //горизонтальное выравнивание

Если мы хотим работать с высотой строки или шириной столбца, то это лучше делать через лист:

let sheet = Api.GetActiveSheet(); sheet.SetRowHeight(12, 20); //нумерация с нуля, высота в пикселях sheet.SetColumnWidth(1, 30); //нумерация с нуля, ширина в более крупных единицах, точно установить не удалось

Но можно и через ячейку:

let cell = Api.GetActiveSheet().GetRange("B3"); cell.SetRowHeight(20); cell.SetColumnWidth(30);

Набор данных как таблица

Если вы хотите работать с блоком данных как с таблицей (строка заголовков, фильтр, сортировка, вам понадобится метод FormatAsTable. При этом рекомендую предварительно отформатировать данные под нужный формат, например:

let sheet = Api.GetSheet('Лист1'); let cells = sheet.GetRange('H7:I40'); // Диапазон с числовыми данными cells.SetNumberFormat("0.000"); // Форматируем как число с тремя знаками после запятой Api.GetSheet('Лист1').FormatAsTable('G6:I40'); // В верхней строке у нас заголовки, в столбце G – строки, не требующие специального форматирования

Цвета и границы

В Onlyoffice работа с цветами возможна либо по их RGB-коду, либо через предустановленные цвета. Для того чтобы получить цвет по RGB-наполнению, нам нужно вызвать метод

let color = Api.CreateColorFromRGB(255, 255, 200);

Первый параметр – уровень красного, второй – зеленого, третий – синего (от 0 до 255).

Альтернативный вариант – выбрать из предустановленных цветов. Там, помимо очевидных вроде yellow и cyan, достаточно большой список, который можно найти на странице https://support.r7-office.ru/using-api-document-builder/global-2/, поискав по слову PresetColor.

let color = Api.CreateColorByName("dodgerBlue");

Методы CreateRGBColor, CreatePresetColor и CreateSchemeColor нужны для рисования фигур, для текста и ячеек они не подойдут. Пример можно посмотреть по ссылке https://api.onlyoffice.com/docs/office-api/usage-api/spreadsheet-api/api/createpresetcolor/ , а возможные значения для метода CreateSchemeColor – по предыдущей ссылке, поискав по слову SchemeColorId.

Дальше с помощью цвета можно устанавливать цвет текста ячеек или их фона:

let cells = Api.GetSheet('Лист1').GetRange('B1:D40'); cells.SetFillColor(color); //для фона cells.SetFontColor(color); //для текста

Установка толщины и цвета границ диапазона производится следующим способом:

let cells = Api.GetSheet('Лист1').GetRange('B1:D40'); let color = Api.CreateColorFromRGB(255, 255, 200); cells.SetBorders('Left', 'Thin', color);

Варианты значений для первого параметра SetBorders – обратите внимание, что они пишутся с большой буквы, и что Left, Right, Top и Bottom означают только внешние границы диапазона – это, помимо них, еще InsideHorizontal, InsideVertical (все внутренние горизонтальные или вертикальные границы), а также DiagonalDown и DiagonalUp (перечеркивание ячейки снизу вверх или сверху вниз).

Варианты значений для второго параметра SetBorders:
Double | Hair | DashDotDot | DashDot | Dotted | Dashed | Thin | MediumDashDotDot | SlantDashDot | MediumDashDot | MediumDashed | Medium | Thick
Я чаще всего пользуюсь Thick. К сожалению, мне так и не удалось заставить заработать значение None для этого параметра, чтобы удалить границы.

Работа Javascript-функций

Очень многие возможности нативного Javascript работают в полном объеме. Например, объектом Math можно воспользоваться, чтобы сгенерировать случайный цвет, на котором должно быть видно черный текст. Собственно, это практически любой цвет, у которого каждый из параметров RGB больше 155. Давайте напишем функцию генерации такого цвета. Ее можно расположить над кодом макроса:

function rand() {      return 155 + Math.round(100 * Math.random()); }  function generateColor() {     return Api.CreateColorFromRGB(rand(), rand(), rand()); }

Далее, если мы хотим получить данные из какого-нибудь внешнего источника, то fetch также прекрасно работает, а если мы получаем оттуда json, то его можно распарсить с помощью JSON.parse. Например, получим курсы валют с сайта, загружающего их с сайта ЦБ:

function getCurrencies() {     fetch('https://www.cbr-xml-daily.ru/daily_json.js').then(response => {         let src = JSON.parse(response.value);                  let start = 2;         for (let chr in src.Valute) {             let arCourses = src.Valute[chr];             getCell('A' + start).SetValue(chr);             getCell('B' + start).SetValue(arCourses.Value);             getCell('C' + start).SetValue(arCourses.Previous);             start++;         }     });  }

Работа вне окна макроса

Осталось еще одно небольшое неудобство. Окно макроса, как правило, нависает над таблицей (и его нельзя унести на другой экран), да и в целом некоторые макросы хочется запускать через комбинацию клавиш или клик куда-нибудь. И это почти возможно (с некоторым ограничением).

Реализуется это также с помощью стандартного функционала Javascript:

addEventListener("keyup", function(event) {     if(event.ctrlKey) {         let code = event.keyCode;         if (code == 53) { // Ctrl+5             someFunction(); //какая-то ваша функция         }         if (code == 54) { // Ctrl+6             someOtherFunction(); //какая-то другая ваша функция         }          }  });

Если нам понадобится узнать, какой сейчас активный лист или ячейка, это всегда можно узнать с помощью

Api.GetActiveSheet().GetName(); //имя активного листа Api.GetActiveSheet().GetActiveCell().GetAddress(); //имя активной ячейки

Также вы можете повесить событие на клик по ячейке. Как раз определение имени активной ячейки нам здесь пригодится.

addEventListener("click", function(event) {     let cellName = Api.GetActiveSheet().GetActiveCell().GetAddress(false, false, 'xlA1');     if (cellName === 'B3') {         someFunction();     }     if (cellName === 'F3') {         someOtherFunction();     }  });

Проблема, однако, в том, что хитрый Onlyoffice не обновляет данные, измененные таким образом, пока не будет изменена хотя бы одна ячейка в таблице. Делать редактирование ячейки на каждую комбинацию клавиш выглядит как-то очень глупо, и я нашел пару «лайфхаков», одну для «клавиатурщиков», другую – для «мышечников». Если вы предпочитаете не снимать рук с клавиатуры, то сразу после комбинации клавиш, которую вы нажали, вам нужно нажать F9 – пересчет всех формул на листе, и обновления отобразятся. Если вам больше нравится работать мышкой, то вы можете нажать ту из кнопок выравнивания по вертикали в верхней панели, которая уже и так нажата.

Соответственно, инструктируя тех, кто будет пользоваться вашим макросом, вы можете просто сразу комбинировать эти действия, например, функция someFunction запускается через комбинацию клавиш Ctrl+5 и F9, или же по нажатию на ячейку B3 (позаботьтесь, чтобы там была имитация кнопки с подписью) и потом на кнопку выравнивания текста по вертикали.

Я полагаю, что эта проблема должна решаться, если сделать из макроса плагин; думаю, об этом напишут в комментариях. Но плагины достойны отдельной статьи.

Автозапуск

Довольно очевидно, что макрос, запускающий addEventListener, должен запускаться автоматически при открытии файла. За это отвечает кнопка «автозапуск» в редакторе макросов, и автозапускаемые макросы помечены значком (А). При открытии файла Onlyoffice спрашивает, запустить ли автозапускаемые макросы.

К сожалению, иногда в макросах бывают ошибки. Если ваши макросы перестали запускаться, или если вдруг вы отредактировали макрос, а он не изменился, вам нужно закрыть и заново открыть программу.

Если вы поставили галочку «Всегда запускать макросы на входе», а в макросе появилась ошибка, то система не даст вам редактировать макросы. В этом случае отключение автоматического автозапуска макросов находится в пункте меню «Файл – Дополнительные параметры – Настройки макросов» (для возврата к варианту по умолчанию выберите «Показывать уведомления»).

Заключение

Разумеется, этот туториал посвящен только базовым возможностям настройки макросов для таблиц в Onlyoffice и «Р7 офис». Вот еще несколько полезных ссылок:

Спасибо за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/863100/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *