«Front-end Skills» — Реализация функции экспорта

JavaScript Vue.js
«Front-end Skills» — Реализация функции экспорта

Эта статья используется для записи реализации функции экспорта в различных бизнес-сценариях, встречающихся в проекте. Добро пожаловать лайк и избранное

1. Экспорт спины возврата

Этот вид экспорта мой любимый, бэкэнд более добросовестный, а фронтенд очень легкий. Конечно, в этом сценарии есть две ситуации.

  • ссылка с доменным именем
if (res.code == 200) {
    window.location.href = res.data;
}
  • ссылка без домена

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

    Создайте файл config.js и файл config.js.example в общей папке статических ресурсов.Файл config.js загружается в git.Среда разработки может настроить доменное имя серверной части.В производственной среде , эксплуатация и обслуживание В нем настраивается доменное имя бэкенда, потому что оно было загружено на git, и конфигурация эксплуатации и обслуживания не будет перезаписана при обновлении кода. Файл config.js.example является примером, в котором рассказывается о том, как настраивать работу и техническое обслуживание.

    config.js имеет то же содержимое, что и config.js.example.

    /*
    配置文件示列:
    配置文件路径 public/config.js
    */
    window.apiConfig = {
        baseUrl: '后端的域名',
    };
    

    Затем импортируйте config.js из public/index.html.

    <script>
        var script = document.createElement('script');
        var num = Math.floor(Math.random() * 10000);
        script.src = 'config.js?a=' + num;
        document.getElementsByTagName('head')[0].appendChild(script);
        script = document.getElementById('scriptConfig');
        script.parentNode.removeChild(script);
        script = null;
    </script>
    

    Наконец, используйте это

    if (res.code == 200) {
        window.location.href = window.apiConfig.baseUrl+res.data;
    }
    

Во-вторых, серверная часть возвращает экспорт бинарных данных.

  • Во-первых, нам нужно настроить axios, потому что тип данных ответа сервера по умолчанию — json, который нужно изменить на blob.
    export function export(data){
        return service.get('接口地址',{
            params:data,
            responseType:'blob'
        })
    }
    
  • затем используйтеnew Blob()для обработки двоичных данных, создания файла, а затем использованияcreateObjectURL()После создания ссылки используйте ссылку для ее автоматической загрузки. Давайте инкапсулируем метод и повесим его на цепочку прототипов Vue.
const install = function(Vue,opts){
     * 处理二进制数据导出
     * @param blob 二进制流
     * @param name 文件名
     */
    Vue.prototype.exportExcels = function(blob,name){
        // type 为需要导出的文件类型,此处为xls表格类型
        const file = new Blob([blob], { type: 'application/vnd.ms-excel' });
        // 兼容不同浏览器的URL对象
        const url = window.URL || window.webkitURL || window.moxURL;
        // 创建下载链接
        const downloadHref = url.createObjectURL(file);
        // 创建a标签并为其添加属性
        let downloadLink = document.createElement('a');
        downloadLink.setAttribute('href', downloadHref);
        downloadLink.setAttribute('download', name);
        //将a标签添加到body中
        document.body.appendChild(downloadLink);
        // 触发a标签的点击,自动下载
        downloadLink.click();
        //下载完成后移除a标签
        document.body.removeChild(downloadLink);
        //释放下载链接
        url.revokeObjectURL(downloadHref);
    }
}
export default{
    install
}
  • вnew Blob()Первый параметр — массив, каждый из которых — бинарный поток, а второй параметр — необязательный атрибут, гдеtypeАтрибут представляет собой MIME-тип файла, который определяется серверной частью.

    Обычно используемые типы MIME:

    суффикс MIME-имя
    *.csv text/csv
    *.doc application/msword
    *.dot application/msword
    *.xls application/vnd.ms-excel
    *.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet

3. Добавьте параметры в адрес интерфейса экспорта, чтобы напрямую открыть загрузку.

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

window.location.href = '导出接口地址'?user='lhy'&date='2020-05';

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

Поскольку этот сценарий заключается в непосредственном открытии адреса интерфейса экспорта для загрузки, использовать метод post немного сложно, поэтому в настоящее время нам нужно использовать HTML.<form>теги и объекты формы DOM для разрешения.

Мы инкапсулируем компонент для достижения.

<template>
    <form :action="action" :target="target" :method="method" ref="exports">
        <template v-if="data.length">
            <input type="hidden" autocomplete="off" v-for="(item,i) in data" :name="item.name" :value="item.value"/>
        </template>
        <input type="hidden" autocomplete="off" readonly name="token" :value="token"/>
    </form>
</template>
<script>
    export default {
        name: 'formExport',
        props: {
            action: {
                type: String,
                default: '',
            },
            target: {
                type: String,
                default: '_blank',
            },
            method: {
                type: String,
                default: 'post',
            },
            token:{
                type: String,
                default: '',
            },
            data: {
                type: Array,
                default() {
                    return [];
                }
            }
        },
        methods: {
            submit() {
                return new Promise((resoleve,reject) =>{
                    if (this.token) {
                        this.$refs.exports.submit();
                        resolv()
                    }else{
                        reject()
                    }
                }
            }
        }
    }
</script>

Документация по компонентам

  • параметр
    параметр иллюстрировать Типы необязательное значение По умолчанию
    action Обязательно, адрес интерфейса экспорта String
    target Указывает, где открыть адрес интерфейса экспорта String _blank: открыть в новом окне
    _self: открыть в текущем окне
    _blank
    method метод запроса String post/get post
    token Требуется, аутентификация String
    data Обязательный, параметры передаются на сервер
    {имя: имя параметра, значение: значение параметра}
    String
  • метод
    название события иллюстрировать параметр обратного вызова
    submit представить форму Объект обещания
  • Пример
    <template>
        <formExport ref="export" :action="exportData.url" :data="exportData.url" :token="exportData.token"></formExport>
        <el-button @click="handleExport">导出</el-button>
    </template>
    <script>
        export default {
            data(){
                return{
                    exportData:{
                        url:'导出接口地址',
                        data:{
                            user:'lds',
                            page:1,
                            pageSize:20,
                            statTime:'2020-04',
                            endTime:'2020-05'
                        },
                        token:'12334f'
                    }
                }
            },
            components:{
                formExport: () =>import('./formExport.vue')
            },
            methods:{
                handleExport(){
                    setTimeout(() => {
                        this.$refs.export.submit();
                    }, 500);
                }
            }
        }
    </script>
    

4. Серверная часть возвращает только данные в формате JSON, а передняя часть генерирует загрузку в формате Excel.

Этого можно добиться с помощью двух плагинов xlsx и file-saver. Где xlsx предназначен для создания файла Excel, а файл-сохранение — для сохранения и загрузки файла Excel.

  • Установите плагины xlsx и File-Saver с помощью npm, выполните команду
npm install xlsx --save
npm install file-saver --save
  • Внести в сервисную папкуExport2Excel.js, инкапсулируйте методы в подключаемых модулях xlsx и сохранения файлов в этом скрипте.

  • Давайте инкапсулируем метод и повесим его на цепочку прототипов Vue.

    • документация по параметрам
    параметр иллюстрировать Типы По умолчанию необязательное значение Пример
    header данные заголовка таблицы array ['Клиент первого уровня', 'клиент подачи заявки', 'счет Alipay', 'сумма снятия', 'заявитель', 'время подачи заявки']
    data Табличные данные array [{},{}]
    filename Имя файла Excel string 'excle1'
    opition Дополнительная конфигурация object {}
    • документация по параметрам
    параметр иллюстрировать Типы По умолчанию необязательное значение Пример
    filterVal Фильтровать данные таблицы array ['id','name']
    multiHeader Данные заголовка таблицы, за исключением последней строки данных заголовка таблицы, являются двумерными данными. Если их недостаточно, используйте '' для их заполнения. Это допустимо только в том случае, если bookType имеет значение xlsx или xls. array [['серийный номер', 'информация о клиенте', '', '', '', ''], ['', 'имя клиента', 'информация о снятии средств', '', '', '']]
    merges Правило объединения заголовков таблиц действует только в том случае, если bookType имеет значение xlsx или xls. array ['A1:A3', 'B1:F1', 'B2:B3', 'C2:F2']
    autoWidth Адаптируется ли содержимое таблицы к ширине boolean true true/false
    bookType Создать тип файла string xlsx xlsx/xls/csv
const install = function(Vue,opts){
    /**
    * json数据生成Excel并下载
    * @param header 表格头数据
    * @param data 表格数据
    * @param filename Excel文件名称
    * @param opition 额外配置
    */
   Vue.prototype.downloadExcels = function (header, data, filename, opition) {
       let defaultOpition = {
           filterVal: [],
           multiHeader: [],
           merges: [],
           autoWidth: true,
           bookType: 'xlsx',
       }
       if (header && Object.prototype.toString.call(header) != '[object Array]') {
           throw new Error('header请传入数组');
       }
       if (data && Object.prototype.toString.call(data) != '[object Array]') {
           throw new Error('data请传入数组');
       }
       if (opition && Object.prototype.toString.call(opition) == '[object Object]') {
           defaultOpition = Object.assign({}, defaultOpition, opition);
       }
       if (Object.prototype.toString.call(defaultOpition.filterVal) != '[object Array]') {
           throw new Error('filterVal请传入数组');
       }
       if (Object.prototype.toString.call(defaultOpition.multiHeader) != '[object Array]') {
           throw new Error('multiHeader请传入数组');
       }
       if (Object.prototype.toString.call(defaultOpition.merges) != '[object Array]') {
           throw new Error('merges请传入数组');
       }
       const formatJson = function (filterVal, jsonData) {
           if (filterVal.length == 0) {
               return jsonData;
           } else {
               return jsonData.map(v => filterVal.map(j => v[j]));
           }
       }
       data = formatJson(defaultOpition.filterVal, data);
       if (data[0].length > header.length) {
           throw new Error('data中每项数据长度大于头部长度');
       }
       defaultOpition['data'] = data;
       defaultOpition['header'] = header;
       defaultOpition['filename'] = filename;
       import('./Export2Excel').then(res => {
           res.export_json_to_excel(defaultOpition);
       })
   }
}
export default{
   install
}
  • Пример

handleExport() {
    const header = ['一级客户', '申请客户', '支付宝账号', '提现金额', '申请人', '申请时间'];
    const option = {
        bookType: 'xlsx',
        filterVal: ['firstCustomName', 'custom_name', 'withdrawals_bank', 'withdrawals_amount', 'do_username', 'add_time'],
        multiHeader: [
            ['序号', '客户信息', '', '', '', ''],
            ['', '客户姓名', '提现信息', '', '', '']
        ],
        merges: ['A1:A3', 'B1:F1', 'B2:B3', 'C2:F2']
    }
    this.downloadExcels(header, this.tableData, '审核记录', option)
},

5. Экспорт через PDF

Этот экспорт подходит для визуальных сценариев данных

  • Введите скрипт для создания PDF в index.html.jspdf.js.
  • Добавьте сценарий скриншота в index.html.html2canvas.js.
  • Давайте инкапсулируем метод и повесим его на цепочку прототипов Vue.
const install = function(Vue,opts){
     * 将页面导出成pdf文件
     * @param id 要生成PDF文件DOM区域的id
     * @param fileName 导出的文件名称
     * @param height 导出的pdf高度不够,需要设置额外高度,默认80
     */
    Vue.prototype.exportPDF= function(id, fileName, height = 80){
        //html2canvas只截取dom的可视区域,将dom的可视区域设置大解决导出视图不全的问题
        document.getElementById(id).ownerDocument.defaultView.innerHeight = document.getElementById(id).scrollHeight + height;
        html2canvas(document.getElementById(id), {
            scale: 2,//按比例增加分辨率 (2=双倍).
            dpi: 1080,//导出pdf清晰度 将分辨率提高到特定的DPI(每英寸点数)
            background: "#fff", //背景设为白色(默认为黑色)
            onrendered: function (canvas) {
                let contentWidth = canvas.width;
                let contentHeight = canvas.height;
    
                //一页pdf显示html页面生成的canvas高度;
                //a4纸的尺寸[595.28,841.89]
                let pageHeight = contentWidth / 592.28 * 841.89;
                //未生成pdf的html页面高度
                let leftHeight = contentHeight;
                //pdf页面偏移
                let position = 0;
    
                //html页面生成的canvas在pdf中图片的宽高
                let imgWidth = 595.28;
                let imgHeight = 592.28 / contentWidth * contentHeight;
    
                let pageData = canvas.toDataURL('image/jpeg', 1.0);
                let pdf = new jsPDF('', 'pt', 'a4');
    
                //有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)
                //当内容未超过pdf一页显示的范围,无需分页
                if (leftHeight < pageHeight) {
                    pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight);
                } else {
                    while (leftHeight > 0) {
                        pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight);
                        leftHeight -= pageHeight;
                        position -= 841.89;
    
                        //避免添加空白页
                        if (leftHeight > 0) {
                            pdf.addPage();
                        }
                    }
                }
                pdf.save(fileName);
            }
        )
    }
}
export default{
    install
}