...

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

Тема в разделе "Примеры решений и дополнительных модулей", создана пользователем k.sannikov, 13 июн 2023.

  1. k.sannikov

    k.sannikov Новичок

    Я думаю каждый сталкивался с проблемой, что на ячейки системной таблицы нет возможности навесить скрипт на изменение значения или добавить фильтр, например к столбцу с типом "Приложение".

    Предлагаю вам ознакомиться с решением этой проблемы при помощи использования виджета "Динамический список".

    По сути, под капотом, мы имеем ту же самую таблицу, которая будет выступать в роли источника данных.

    Итак, покажу на примере созданного мной раздела "Отпуска". В разделе есть приложение "Тип отпуска", "Сотрудник" и "Отпуска". В приложение отпуска добавим таблицу с детализацией - она будет являться источником для динамического списка и еще пару атрибутов.

    upload_2023-6-13_15-15-56.png

    Далее создадим виджет на уровне раздела, и продублируем переменные из таблицы, а также саму таблицу и, забегая наперед, переменную которая будет хранить ID доступных типов отпусков для текущего сотрудника:

    upload_2023-6-13_15-25-9.png

    Создадим шаблон виджета, добавим 4 колонки и кнопку для удаления:

    upload_2023-6-13_15-28-28.png

    Каждая строка таблицы будет являться копией этого виджета с данными, которые находятся в этой строке. Создаем необходимые нам скрипты на изменения значений, навешиваем фильтр на переменную с типом отпуска, чтобы сотрудник мог выбрать только те типы, которые за ним закреплены:

    Код:
    
    async function onInit(): Promise<void> {
        if (
    Context.data.vacation_types_id) {
            
    let idsstring[] = JSON.parse(Context.data.vacation_types_id).result
            Context
    .fields.type_vacation.data.setFilter(=> f.__id.in(ids));
        }
    }

    async function on_change_start(): Promise<void> {

        if (
    Context.data.end && Context.data.start) {
            if (
    Context.data.end.before(Context.data.start)) {
                
    Context.data.show_date_error true;
                
    Context.data.end undefined;
            } else {
                
    Context.data.show_date_error false;
            }

            
    //Скрипт для подсчета кол-ва дней в промежутке
            
    let duration Math.ceil(((Context.data.end!.asDatetime(new TTime(4000)).unix()) - (Context.data.start.asDatetime(new TTime(22000)).unix())) / (24 60 60))
            
    Context.data.days duration 1;
            
    let row Context.data.vacation_details!.find(=> f.id == Context.data.id_row);
            if (
    row) {
                
    row.days Context.data.days;
                
    row.start Context.data.start!;
                
    Context.data.vacation_details Context.data.vacation_details;
            }
        }
    }

    async function on_change_end(): Promise<void> {

        if (
    Context.data.end && Context.data.start) {
            if (
    Context.data.end.before(Context.data.start)) {
                
    Context.data.show_date_error true;
                
    Context.data.end undefined;
            } else {
                
    Context.data.show_date_error false;
            }

            
    //Скрипт для подсчета кол-ва дней в промежутке
            
    let duration Math.ceil(((Context.data.end!.asDatetime(new TTime(4000)).unix()) - (Context.data.start.asDatetime(new TTime(22000)).unix())) / (24 60 60))
            
    Context.data.days duration 1;
            
    let row Context.data.vacation_details!.find(=> f.id == Context.data.id_row);
            if (
    row) {
                
    row.days Context.data.days;
                
    row.end Context.data.end!;
                
    Context.data.vacation_details Context.data.vacation_details;
            }
        }
    }

    async function on_change_type_vacation(): Promise<void> {
        if (
    Context.data.type_vacation) {
            
    let row Context.data.vacation_details!.find(=> f.id == Context.data.id_row);
            if (
    row) {
                
    row.type_vacation Context.data.type_vacation
                Context
    .data.vacation_details Context.data.vacation_details;
            }
        }
    }

    async function deleted(): Promise<void> {
        for (
    let i Context.data.vacation_details!.length 1>= 0i--) {
            const 
    row Context.data.vacation_details![i];
            if (
    row.id == Context.data.id_row) {
                
    Context.data.vacation_details!.delete(i);
            }
        }
        
    Context.data.vacation_details Context.data.vacation_details;
    }
    Публикуем виджет и возвращаемся к форме создания отпуска. Добавляем на форму "Панель с заголовком", назовем ее "Детализация отпуска". Внутрь панели добавляем виджет "Динамический список" и в настройках выбираем таблицу из контекста приложения:

    upload_2023-6-13_15-33-55.png

    Внутрь динамического списка добавляем еще одну панель с заголовком, в поле "Заголовок" оставляем пробел. Это нужно для того чтобы наш динамический список выглядел более элегантно)). После чего в панель с заголовком без заголовка))) добавляем ранее созданный виджет и связываем поля с данными из таблицы - источника:

    upload_2023-6-13_15-36-53.png

    Выносим виджет "Кнопка", которая будет добавлять новую строку в таблицу и в конечном итоге должно получится следующее:

    [​IMG]

    Пишем скрипты, тут как говориться, кто на что горазд. Я написал небольшой онинит скрипт, который определяет текущего пользователя, ищет в справочнике сотрудников его карточку и записывает ID доступных для него отпусков. ID мы передаем во внешний виджет вместе с данными из таблицы.
    .
    Также, чтобы нам было проще понимать, какая именно строка из таблицы источника перед нами, генерируем 8 значный идентификатор.

    Ну и скрипт, который добавляет строку в таблицу источник, тут все просто.

    Код:
    
    async function onInit(): Promise<void> {
        
    Context.data.staff await System.users.getCurrentUser();

        
    //ищем текущего пользователя в справочнике сотрудников и записываем ID доступных типов отпусков
        
    let staff await Namespace.app.staff.search().where((fg) => g.and(
            
    f.__deletedAt.eq(null),
            
    f.user.eq(Context.data.staff!)
        )).
    first();
        if (
    staff && staff.data.vacation_types && staff.data.vacation_types.length 0) {
            
    let type_vacations staff.data.vacation_types;
            
    let ids type_vacations.map(=> f.id);

            
    //записываем в строку и передаем во внешний виджет
            
    ViewContext.data.vacation_types_id JSON.stringify({ resultids });
        }
    }

    async function addRow(): Promise<void> {
        
    //добавление строки в таблицу-источник
        
    let start_date_newTDate undefined undefined;
        if (
    Context.data.vacation_details && Context.data.vacation_details.length 0) {
            
    let last_row Context.data.vacation_details[Context.data.vacation_details.length 1];
            if (
    last_row.end) {
                
    start_date_new last_row.end.addDate(001);
            }
        }

        
    let row Context.data.vacation_details!.insert();
        if (
    start_date_new) {
            
    row.start start_date_new
        
    }

        
    let id ID_generation();
        
    row.id id;

        
    Context.data.vacation_details Context.data.vacation_details;

    }
    //генерируем ID строки для дальнейшего определения строки в таблице-источнике из внешнего виджета
    function ID_generation() {
        const 
    valid "abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ23456789!&?`~№;:^\|/'#$%&*=-+_)(}{][@";
        
    let length 8;
        
    let id '';
        while (
    length--) {
            
    id += valid.charAt(Math.floor(Math.random() * valid.length))
        }

        return 
    id
    }
    Чтобы динамический список запустился нужно, чтобы в таблице были какие-то данные, поэтому пишем ончейндж скрипт на изменение даты начала:

    Код:
    
    //при заполнении даты начала инициализируем динамический список, заполняя дату начала в первой строке
    async function on_change_start_date(): Promise<void> {
        
    console.log('on_change_start_date')

        
    let row Context.data.vacation_details!.insert();
        
    row.start Context.data.start!;

        
    let id ID_generation();
        
    row.id id;

        
    Context.data.vacation_details Context.data.vacation_details;
        
    ViewContext.data.show_details true;
    }
    Публикуем форму и идем создавать отпуск.



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

    Код:
    
    //Валидация
    async function validation(): Promise<ValidationResult> {
        const 
    result = new ValidationResult();

        
    //проверка на то, что все бязательные поля в детализации заполнены
        
    if (Context.data.vacation_details && Context.data.vacation_details.length 0) {
            for (
    let row of Context.data.vacation_details!) {
                if(!
    row.start || !row.end || !row.type_vacation){
                    
    result.addMessage('Заполните все обязательные поля в детализации отпуска.')
                    return 
    result
                
    }
            }

            
    Context.data.start Context.data.vacation_details[0].start;
            
    Context.data.end Context.data.vacation_details[Context.data.vacation_details.length 1].end;
            
    Context.data.amount_days Context.data.vacation_details.map(=> f.days).reduce((sum,el) => sum el0);
        }


        return 
    result
    }


    Выгруженный раздел с виджетом во вложениях, можно пощупать.

    Вложения:

    Последнее редактирование: 13 июн 2023