You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cordova应用点击保存后在Safari打开PDF的实现求助

问题:Cordova App中PDF生成后无法保存,需在Safari中打开PDF

我有一段HTML代码,已通过Cordova转换为App。网页端点击保存按钮可将页面内容转为PDF并保存,但Cordova生成的App中点击保存仅打开页面无保存选项,需实现点击保存后在Safari中打开PDF。已尝试多种方法未解决,已安装cordova-pdf-generator和cordova-plugin-inappbrowser插件,恳请提供帮助,谢谢!

原始代码

function saveData() {
    const customerData = {
        customerName: document.getElementById('customerName').value,
        orderNumber: document.getElementById('orderNumber').value,
        storeNumber: document.getElementById('storeNumber').value,
        date: document.getElementById('date').value,
        // intallerID: document.getElementById('intallerID').value,
        // intallerName: document.getElementById('intallerName').value,
    };
    const roomElement = document.getElementById(`room${windowSectionCounter}`);
    if (roomElement) {
        roomElement.value = roomElement.value.toUpperCase();
    }
    const windowDataList = [];
    const windowSections = document.querySelectorAll('windowInfoSection');
    windowSections.forEach((windowSection, index) => {
        // Add a check for null before accessing properties
        const roomElement = windowSection.querySelector('input[name="room"]');
        const quantityElement = windowSection.querySelector('quantity');
        const mountElement = windowSection.querySelector('mount');
        const widthElement = windowSection.querySelector('width');
        const heightElement = windowSection.querySelector('height');
        const depthElement = windowSection.querySelector('depth');

        if (roomElement && quantityElement && mountElement && widthElement && heightElement && depthElement) {
            const convertedWidth = convertDimensionsToFraction(parseFloat(widthElement.value));
            const convertedHeight = convertDimensionsToFraction(parseFloat(heightElement.value));

            const windowData = {
                room: roomElement.value,
                quantity: quantityElement.value,
                mount: mountElement.value,
                width: widthElement.value,
                height: heightElement.value,
                depth: depthElement.value,
            };
            windowDataList.push(windowData);
        } else {
            console.error('One or more elements in the window section are null.');
        }
    });

    console.log('Customer Data:', customerData);
    console.log('Window Data List:', windowDataList);
    // Generate PDF
    const pdfData = {
        customerData,
        windowDataList,
    };

    generatePDF(pdfData);
}    

function generatePDF(data) {
    const addButton = document.getElementById('addWindowButton');
    const saveButton = document.getElementById('saveButton');
    const clearButton = document.getElementById('clearButton');
    const commentButton = document.getElementById('commentButton');
    const removeWindowButton = document.getElementById('removeWindowButton');
    const removeCommentButton = document.getElementById('removeCommentButton');

    addButton.style.display = 'none';
    saveButton.style.display = 'none';
    clearButton.style.display = 'none';
    commentButton.style.display = 'none';
    removeWindowButton.style.display = 'none';
    removeCommentButton.style.display = 'none';

    // Extract customer name and order number from the data
    const customerName = data.customerData.customerName;
    const orderNumber = data.customerData.orderNumber;

    // Convert spaces to underscores and remove any special characters
    const sanitizedCustomerName = customerName.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '');
    const sanitizedOrderNumber = orderNumber.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '');

    // Generate PDF with customer name and order number in the filename
    html2pdf().from(document.body)
    .set({
        filename: `${sanitizedCustomerName}_${sanitizedOrderNumber}.pdf`,
        image: { type: 'jpeg', quality: 0.98 },
        // html2canvas: { scale: 1.5 }, // Reduced scale
        jsPDF: { unit: 'mm', format: 'a3', orientation: 'landscape' },
    })
    .toPdf()
    .get('pdf')
    .then(function (pdf) {
        const totalPages = pdf.internal.getNumberOfPages();
        pdf.setFontSize(10); // Set the font size for the page numbers
        pdf.setTextColor(150); // Set the text color for the page numbers
        for (let i = 1; i <= totalPages; i++) {
            pdf.setPage(i);
            pdf.text('Page ' + i + ' of ' + totalPages, pdf.internal.pageSize.getWidth() - 30, pdf.internal.pageSize.getHeight() - 5);
        }
    })
    .save()
    .then(() => {
        addButton.style.display = 'inline-block';
        saveButton.style.display = 'inline-block';
        clearButton.style.display = 'inline-block';
        commentButton.style.display = 'inline-block';
        removeWindowButton.style.display = 'inline-block';
        removeCommentButton.style.display = 'inline-block';
    })
    .catch((error) => {
        console.error('Error generating PDF:', error);
        addButton.style.display = 'inline-block';
        saveButton.style.display = 'inline-block';
        clearButton.style.display = 'inline-block';
        commentButton.style.display = 'inline-block';
        removeWindowButton.style.display = 'inline-block';
        removeCommentButton.style.display = 'inline-block';
    });
}

解决方案

1. 修改PDF生成逻辑,改用InAppBrowser在Safari打开

原代码中html2pdf().save()在Cordova iOS环境下无法触发系统保存弹窗,需改为生成Blob数据后,调用cordova-plugin-inappbrowser以系统浏览器(Safari)打开PDF,此时Safari会提供保存、打印等选项。

修改后的generatePDF函数

function generatePDF(data) {
    const addButton = document.getElementById('addWindowButton');
    const saveButton = document.getElementById('saveButton');
    const clearButton = document.getElementById('clearButton');
    const commentButton = document.getElementById('commentButton');
    const removeWindowButton = document.getElementById('removeWindowButton');
    const removeCommentButton = document.getElementById('removeCommentButton');

    addButton.style.display = 'none';
    saveButton.style.display = 'none';
    clearButton.style.display = 'none';
    commentButton.style.display = 'none';
    removeWindowButton.style.display = 'none';
    removeCommentButton.style.display = 'none';

    const customerName = data.customerData.customerName;
    const orderNumber = data.customerData.orderNumber;
    const sanitizedCustomerName = customerName.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '');
    const sanitizedOrderNumber = orderNumber.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '');
    const fileName = `${sanitizedCustomerName}_${sanitizedOrderNumber}.pdf`;

    html2pdf().from(document.body)
    .set({
        image: { type: 'jpeg', quality: 0.98 },
        jsPDF: { unit: 'mm', format: 'a3', orientation: 'landscape' },
    })
    .toPdf()
    .get('pdf')
    .then(function (pdf) {
        // 添加页码逻辑不变
        const totalPages = pdf.internal.getNumberOfPages();
        pdf.setFontSize(10);
        pdf.setTextColor(150);
        for (let i = 1; i <= totalPages; i++) {
            pdf.setPage(i);
            pdf.text('Page ' + i + ' of ' + totalPages, pdf.internal.pageSize.getWidth() - 30, pdf.internal.pageSize.getHeight() - 5);
        }
        // 生成PDF的Blob数据
        return pdf.output('blob');
    })
    .then(function(blob) {
        // 创建Blob本地URL
        const blobUrl = URL.createObjectURL(blob);
        // 调用InAppBrowser以系统浏览器(Safari)打开PDF
        cordova.InAppBrowser.open(blobUrl, '_system', 'location=yes');
        // 延迟释放URL,避免打开前被回收
        setTimeout(() => URL.revokeObjectURL(blobUrl), 10000);
    })
    .then(() => {
        // 恢复按钮显示
        addButton.style.display = 'inline-block';
        saveButton.style.display = 'inline-block';
        clearButton.style.display = 'inline-block';
        commentButton.style.display = 'inline-block';
        removeWindowButton.style.display = 'inline-block';
        removeCommentButton.style.display = 'inline-block';
    })
    .catch((error) => {
        console.error('Error generating PDF:', error);
        // 出错后也恢复按钮显示
        addButton.style.display = 'inline-block';
        saveButton.style.display = 'inline-block';
        clearButton.style.display = 'inline-block';
        commentButton.style.display = 'inline-block';
        removeWindowButton.style.display = 'inline-block';
        removeCommentButton.style.display = 'inline-block';
    });
}

2. 确认插件配置

检查config.xml是否已正确添加cordova-plugin-inappbrowser:

<plugin name="cordova-plugin-inappbrowser" spec="^5.0.0" />

3. 确保Cordova初始化完成

所有依赖Cordova插件的代码必须在deviceready事件触发后执行,比如按钮点击事件绑定:

document.addEventListener("deviceready", function() {
    // 绑定保存按钮点击事件
    document.getElementById('saveButton').addEventListener('click', saveData);
}, false);

内容的提问来源于stack exchange,提问作者user1742718

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 23:22:04