Javascript-форум (https://javascript.ru/forum/)
-   Events/DOM/Window (https://javascript.ru/forum/events/)
-   -   Перенос данных из таблицы на одной странице в форму на другой. (https://javascript.ru/forum/events/51710-perenos-dannykh-iz-tablicy-na-odnojj-stranice-v-formu-na-drugojj.html)

SokDobriy 16.11.2014 22:29

Перенос данных из таблицы на одной странице в форму на другой.
 
Я наверное опишу то, что я хочу сделать полностью, а потом опишу конкретную задачу.
Итак, есть отчёт в бумажном виде в формате таблицы. Я сканирую файнридером этот документ. На выходе получается html страница с обычной таблицей. Эти данные из таблицы нужно заполнять в форму на сайте сторонней CRM.
Задача: Автоматизировать процесс заполнения формы на стороннем сайте, изначально забирая эти данные из другой страницы, которая формируется файн ридером.
Я лично пока не могу понять в какую сторону черпать знания, что бы передавать данные со страницы на страницу. Думаю в сторону юзерскрипта.
Буду очень признателен, если найдётся добрый человек и напишет пошаговую инструкцию того что нужно делать, а так же в какую сторону копать.

Aetae 16.11.2014 22:37

Юзерскрипт - верное решение.

Юзерскриптом либо добавляете на страницу input:file(если результат скана у вас на компьютере), либо кроссдоменным запросом вытягиваете его с вашего сайта.
Дальше чистый javascript: берём текст из полей таблицы и вставляем в поля ввода.

Чтобы говорить конкретно - нужны и конкретные примеры.

SokDobriy 16.11.2014 23:43

Вложений: 1
Не совсем понял зачем добавлять инпут файл.
Из конкретных вещей прикладываю архив. В нём finereader_table это пример того, что получается на выходе у файнридера. Остальное это сохранённая страница CRM. Смысл в том, что в ней нужно будет прокликивать это дерево с категориями товара, потому что колонка справа подгружается аяксом. Потом нужно брать из третьей колонки таблицы модель товара, искать её на странице CRM и вставлять в инпуты колличество товара и цену.

SokDobriy 16.11.2014 23:46

На счёт автоматического прокликивания скриптом, то это я ещё спрошу. Конечно хотелось бы реализовать, но это не главное. Пока что, и если вручную кликать и автоматизировать процесс подстановки данных, уже счастье.

Aetae 17.11.2014 16:33

Примерно так должен выглядеть юзерскрипт(без прокликивания, ибо там многое зависит от раелизации):
var d = document;
var text = 'textContent' in d.body ? 'textContent' : 'innerText';

var forEach = Array.prototype.forEach; 
var hash = {};


var fixedDiv = d.createElement('div');
fixedDiv.style.cssText = 'position:fixed;top:0;right:0;background:#fff;border:1px solid;z-index:99999';

var input = fixedDiv.appendChild( d.createElement('input') );
input.type = 'button';
input.onclick = readPage;
input.value = 'Считать артикулы со страницы.';

input = fixedDiv.appendChild( d.createElement('label') );
input.innerHTML = 'Загрузить таблицу:';

input = input.appendChild( d.createElement('input') );
input.type = 'file';
input.onchange = readTable;

input = fixedDiv.appendChild( d.createElement('input') );
input.type = 'button';
input.onclick = setData;
input.value = 'Внести данные из таблицы на страницу.';

var tableDiv = fixedDiv.appendChild( d.createElement('div') );

d.body.appendChild(fixedDiv)
     
function readPage(){
    hash = {}; //очищаем список артикулов полученый с текущей страницы
    forEach.call( d.querySelectorAll('.CoolGridViewTable.table td:first-child'), function(td){
        //заполняем список артикулов текущей страницы
        hash[td[text].trim()] = td.parentNode;
    })
    console.log(hash)
}

function readTable(){
    var reader = new FileReader();
    reader.onload = function(event) {
        //считываем таблицу из файла
        var contents = event.target.result.match(/<table[\s\S]+<\/table>/);
        if(!contents) return console.error("Нет таблицы.");

        //преобразуем в код и отображаем
        tableDiv.innerHTML = contents[0];
    };

    reader.onerror = function(event) {
        console.error("Файл не может быть прочитан! код " + event.target.error.code);
    };

    reader.readAsText(this.files[0]);
}

function setData(){
    //проходимся по всем рядам загруженной таблицы
    forEach.call( tableDiv.querySelectorAll('td:nth-of-type(3)'), function(td){
        var rowTo = hash[td[text].trim()], //получаем ряд таблицы на странице с таким артикулом
            rowFrom = td.parentNode; 
  
        if( rowTo ) { //если ряд с таким артикулом существует
            var qty = parseFloat(rowFrom.cells[3][text]), //получаем кол-во в виде текста и преобразуем в число
                price = parseFloat(rowFrom.cells[4][text].replace(/[,\s]+/g, '')); //получаем цену в виде текста, очищаем от запятых и преобразуем в число

            rowTo.cells[1].click(); //кликаем по клетке дабы появились поля ввода

            rowTo.querySelector('input[name="qty"]').value = qty; //вносим кол-во
            rowTo.querySelector('input[name="price"]').value = price;  //вносим цену

            rowTo.querySelector('a').click();  //кликаем по первой ссылке 

            rowFrom.parentNode.removeChild(rowFrom); //удаляем из загруженной таблицы отработанную строку(если какие строки остались - значит такой артикул не найден)
        }
    })
}

Могут быть всякие подводные камни связанные с задержками.
По хорошему конечно надо просить это всё сделать разработчиков ваших, через внутреннюю систему.)

SokDobriy 28.11.2014 00:42

Aetae,
Вы просто гуру скрипта! Мой вам низкий поклон и спасибо что откликнулись!
Задача немного упростилась. Мой друг будет обрабатывать таблицу с помощью PHP на сервере и выдавать файлом js в котором будут сформированы готовые объекты с информацией из таблички. Будет что-то вроде небольшой базы данных.
Я написал скрипт, который будет сверять модели из этой базы с моделями на сайте црм, куда будут заноситься данные, и вставлять их в инпуты, а потом нажимать кнопку отправить.
Всё хорошо, да всё никак не могу понять как сделать правильно задержку нажатия ссылки "отправить". Долго копал в сторону setTimeout. Но она, как я понял создаёт отдельный поток, поэтому цикл будет продолжаться и заполнять поля. И во-вторых, почему-то setTimeout выполняется мгновенно, какое время я бы не поставил. Я не знаю почему.
То есть как то надо останавливать цикл на время скажем 3 секунды потом нажимать на "отправить" и продолжать цикл. Но как?
Привожу код:
function massiv() {       
 var get_td = document.getElementsByTagName('td');
  for (var i=0; i < get_td.length; i++) {
   for (item in catalog) {
    if (get_td[i].firstChild != undefined && catalog[item].model==get_td[i].firstChild.nodeValue && catalog[item].sold == true) {
    get_td[i].nextSibling.click();
    get_td[i].nextSibling.nextSibling.getElementsByTagName('div')[1].getElementsByTagName('input')[0].value=catalog[item].sold;
    get_td[i].nextSibling.nextSibling.getElementsByTagName('div')[1].getElementsByTagName('input')[1].value=catalog[item].price;
    get_td[i].nextSibling.nextSibling.getElementsByTagName('div')[1].getElementsByTagName('a')[0].click();
    };
  };
};

Aetae 28.11.2014 03:11

SokDobriy, не использовать цикл, если нужна задержка.
function massiv() {      
    var get_td = document.getElementsByTagName('td'), out = [];
    for (var i=0; i < get_td.length; i++) {
        for (item in catalog) {
            if (get_td[i].firstChild != undefined && catalog[item].model==get_td[i].firstChild.nodeValue && catalog[item].sold == true) {
                out.push([
                    get_td[i].nextSibling,
                    [
                        get_td[i].nextSibling.nextSibling.getElementsByTagName('div')[1].getElementsByTagName('input')[0], 
                        catalog[item].sold
                    ],
                    [
                        get_td[i].nextSibling.nextSibling.getElementsByTagName('div')[1].getElementsByTagName('input')[1], 
                        catalog[item].price
                    ],
                    get_td[i].nextSibling.nextSibling.getElementsByTagName('div')[1].getElementsByTagName('a')[0]
                ])
            };
        };
    };
    var i = out.length;
    (function re(){
        if(!i--) return; 
        out[i][0].click();
        out[i][1][0].value = out[i][1][1];    
        out[i][2][0].value = out[i][2][1];    
        out[i][3].click();
        setTimeout(re, 3000)
    }())
};
Примерно так. Можно само собой и в один проход, но мне лень думать над логикой рекурсии.)

P.S. Вообще на вашем месте яб изучил код самой crm и обращался напрямую к её функциям.

SokDobriy 28.11.2014 04:02

Aetae,
Вы спаситель!

SokDobriy 28.11.2014 04:04

Aetae,
А почему нельзя остановить цикл, а потом продолжить? Мне просто для себя что бы понять.

Aetae 28.11.2014 04:41

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

SokDobriy 28.11.2014 17:03

Aetae,
Я понял. Просто мне в какой-то момент стали приходить мысли, что используя setTimeout можно организовать эту многопоточность. Но это так, просто мысль. И ещё, пока искал решения, узнал про label, но так и не понял как они работают. А есть ещё breake и continue. И как то это надо всё кинуть в котел, добавить крыльев летучей мыши и перемешать. Но я как-то особо об этом не думал.

SokDobriy 28.11.2014 17:30

Aetae,
Можно ещё к вам вопрос?
Просто подумалось, что можно сохранять файлы файнридером, допустим в определенную папку. И что бы из этой папки скрипт сразу или по нажатию кнопки, закидывали файл на сервер, для обработки PHP скриптом.
Собственно вопрос, как?
Ну, то есть инпут файл понятно (хотя может и вообще что-то другое?). Что делать дальше и в какую сторону думать, не понятно.

Aetae 28.11.2014 18:07

Если делать всё вручную то php сервер будет лишним звеном.)
FineReader вроде ПО корпоративного уровня, по идее там должна быть поддержка загрузки на сервер(накрайняк отправки по почте). Если и нет то можно подцепить папку с сервака и грузить туда.

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

В современных браузерах есть API WebWorker позволяющий таки запускать ещё парочку отдельных потоков и общаться с ними посредством событий. В самых современных есть поддержка генераторов и yield. Но не нужно воевать с языком, его нужно понять.

kostyanet 29.11.2014 13:58

Цитата:

Сообщение от SokDobriy
На выходе получается html страница с обычной таблицей.

Не получается, вы получаете. ФР умеет сохранять куда угодно, в cvs например. Из которого стандартно все засасывается в любую mysql таблицу. У вас не в скриптах дело, а в workflow уродском.

SokDobriy 02.12.2014 04:59

Aetae,
Задача немного поменялась. Как обычно это бывает :)
Теперь нужно объеденить данные из нескольких таблиц. И я это сделал. Но вот в чём проблема. Обрабатываю два файла FileReader'oм, при завершении им загрузки, там же создаю <table> и засовываю всё туда. Со вторым файлом тоже самое. И, проблема в том, что хоть убей не могу потом обратиться к этим таблицам. То есть, он вероятно создаёт их после того, как я к ним обращаюсь, что бы я не делал. Всё это дело висит в addEventListener.

Aetae 02.12.2014 05:07

Честно говоря ничего не понял.

kostyanet 03.12.2014 17:18

Все нормально, когда у него будет 20 таблиц в пакете, он допрет поменять формат с html на csv.

SokDobriy 06.12.2014 05:01

Оказалось, что файнридер может сохранить все сканы в один html. В общем-то задачу решил. Всё работает. Данные засовываются в объект и к ним добавляется всё, что нужно из второго отчёта. Потом это дело находит и заполняет нужные поля.
С csv я не знаком. Разбираться ещё и с этим, не очень хотелось себе голову заморачивать. Но, за наводку спасибо.
Большое спасибо Aetae! Очень-очень приочень помогли! Приятно, что остались ещё отзывчивые люди.

kostyanet 06.12.2014 15:53

Цитата:

Сообщение от SokDobriy
С csv я не знаком

cvs это формат данных, в отличии от html, который - документ со всем барахлом.

Цитата:

Сообщение от SokDobriy
Оказалось, что файнридер может сохранить все сканы в один html

Зашибись. Я прилагал усилия найти чек-бокс сохранять все сканы в отдельные одноименные файлы - оказалось он прямо в диалоге сохранения, а у вас в один файл не писалось. Чудеса умолчаний.


Часовой пояс GMT +3, время: 02:30.