引入依赖:

npm install html2canvas --save

插件版本:

 vue2版代码展示:

          <div class="poster" id="postHtml" ref="postHtml"></div>

import html2canvas from "html2canvas";



methods: {
    /** 将html元素转换成海报图片 */
    createPoster() {
      const name = `商城二维码${this.organization_group_name}`;
      const posterHtml = this.$refs.postHtml;
      // 生成海报
      console.log("生成海报", posterHtml, name);
      const domObj = posterHtml;
      console.log(domObj);
      html2canvas(domObj, {
        allowTaint: true,
        useCORS: true,
        width: 200,
        height: 200,
        scrollY: -18,
        scrollX: -0,
        onclone(doc) {}
      }).then(canvas => {
        // document.body.appendChild(canvas)
        console.log("结果", domObj, canvas);
        this.downloadQRImg(canvas, name);
      });
    },
    /** 根据URL下载图片 */
    downloadQRImg(canvas, name) {
      /** 新Image对象,可以理解为DOM */
      let image = new Image();
      /** 解决跨域 Canvas 污染问题 */
      image.setAttribute("crossOrigin", "anonymous");
      // image.onload = function() {
      /** canvas.toDataURL 返回的是一串Base64编码的URL,指定格式 PNG */
      let imgUrl = canvas.toDataURL("image/png");
      console.log(imgUrl);
      image.src = imgUrl;
      /** 生成一个a元素,并创建一个单击事件 */
      let a = document.createElement("a");
      a.download = name || "photo"; // 设置图片名称
      a.href = imgUrl; // 将生成的URL设置为a.href属性
      a.setAttribute("id", "myLink");
      document.body.appendChild(a);
      console.log("链接", a);
      this.exportCodeConfirm();
      // }
    },
    /** 导出/下载 二维码 */
    exportCodeConfirm() {
      setTimeout(() => {
        let event = new MouseEvent("click");
        /** 触发a的单击事件 */
        document.getElementById("myLink").dispatchEvent(event);
      }, 0);
    }}

vue3版本保存公众号页面为图片

<template>
    <div class="flex-page">
        <div ref="postHtml" class="flex-page-content">
            <div class="primary-text">
                <primary-title :offset-bottom="-0.2" title="会诊总结"></primary-title>
                <div v-if="openMarkName" class="save-img"><span class="save-page" @click="createPoster()"></span></div>
            </div>
            <div class="summary-box">
                <template v-if="visitType === 'preVisited'">
                    <div class="summary-title">肿瘤类型</div>
                    <div class="summary-info">{{ preSummary.tumorTypeName || '无' }}</div>
                    <div class="summary-title">临床分期</div>
                    <div class="summary-info">{{ preSummary.clinicalStages || '无' }}</div>
                    <div class="summary-title">本次主诉</div>
                    <div class="summary-info">{{ preSummary.chiefComplaint || '无' }}</div>
                    <div class="summary-title">主任建议</div>
                    <div class="summary-info">{{ preSummary.suggestion || '无' }}</div>
                    <div class="summary-title">建议解读</div>
                    <div class="summary-info">{{ preSummary.suggestionDetail || '无' }}</div>
                </template>
                <template v-else-if="summary.length > 0">
                    <div class="summary-desc">结合各专家意见如下</div>
                    <div class="summary-content">{{ summary[0].reasonName }}</div>
                    <div class="summary-img-box">
                        <template v-for="(media, i) in summary[0].mediaList">
                            <van-image v-if="media.mediaCategoryId === IMG_CATEGORY_ID" :key="i" :src="media.mediaUrl" :radius="6" class="summary-img" />
                        </template>
                    </div>
                    <template v-for="(media, i) in summary[0].mediaList">
                        <a v-if="media.mediaCategoryId === PDF_CATEGORY_ID" :key="i" :href="media.mediaUrl" class="summary-pdf">会诊总结.pdf</a>
                    </template>
                </template>
            </div>
            <div class="mark-name">
                <img v-if="summary[0]?.isSignatured && summary[0]?.providerSignatureUrl" :src="summary[0]?.providerSignatureUrl" alt="" />
                <span v-else>{{ summary[0]?.providerName }}</span>
            </div>
        </div>
    </div>
</template>

<script lang="ts">
import { defineComponent, Ref, ref, onMounted, nextTick } from 'vue';
import { PrimaryTitle } from '@components';
import { Image as VanImage } from 'vant';
import { commonUtils } from '@utils';
import html2canvas from 'html2canvas';
import visitProviderService from '@api/visit/visit-provider.service';
import visitConsulationService from '@api/visit/visit-consulation.service';
import VisitConsulationSummaryModel from '@models/visit/visit-consulation-summary.model';
import ConsultationVisitNoteModel from '@models/consultation/consultation-visit-note.model';
import { configIdMap } from '@constants';
import configurationService from '@api/configuration.service';

export default defineComponent({
    components: {
        PrimaryTitle,
        'van-image': VanImage,
    },
    props: {
        visitId: {
            type: String,
            required: true,
        },
        visitType: {
            type: String,
            required: false,
            default: 'preVisited',
        },
    },
    setup: (props) => {
        const postHtml = ref();
        const openMarkName = ref<boolean>(false);
        const preSummary: Ref<VisitConsulationSummaryModel> = ref(new VisitConsulationSummaryModel());
        const summary: Ref<ConsultationVisitNoteModel[]> = ref([]);
        // 获取会诊总结
        const getConsulationSummary = () => {
            if (props.visitType === 'preVisited') {
                visitProviderService
                    .getVisitSummary(props.visitId)
                    .then((res) => {
                        preSummary.value = res;
                    })
                    .catch((err) => {
                        commonUtils.showTipMessage(err?.responseText || '请求失败');
                    });
            } else {
                visitConsulationService
                    .getConsulationVisitNote(props.visitId, '39')
                    .then((res) => {
                        summary.value = res;
                    })
                    .catch((err) => {
                        commonUtils.hideLoading();
                        commonUtils.showTipMessage(err?.responseText || '请求失败');
                    });
            }
        };
        // 图片转为base64编码
        function image2Base64(img: HTMLImageElement) {
            // eslint-disable-next-line prefer-const, no-var
            let canvas = document.createElement('canvas'); // 一定要设置为let,不然图片不显示
            canvas.width = img.width;
            canvas.height = img.height;
            const ctx = canvas.getContext('2d');
            ctx!.drawImage(img, 0, 0, img.width, img.height);
            const dataURL = canvas.toDataURL('image/png');
            return dataURL;
        }

        /** 导出/下载 二维码 */
        const exportCodeConfirm = () => {
            setTimeout(() => {
                const event = new MouseEvent('click');
                /** 触发a的单击事件 */
                document.getElementById('myLink')!.dispatchEvent(event);
            }, 0);
        };
        /** 根据URL下载图片 */
        const downloadQRImg = (canvas: any, name: string) => {
            /** 新Image对象,可以理解为DOM */
            const image = new Image();
            /** 解决跨域 Canvas 污染问题 */
            // image.setAttribute('crossOrigin', 'anonymous');
            // image.onload = function() {
            /** canvas.toDataURL 返回的是一串Base64编码的URL,指定格式 PNG */
            const imgUrl = canvas.toDataURL('image/png');
            image.src = imgUrl;
            /** 生成一个a元素,并创建一个单击事件 */
            const a = document.createElement('a');
            a.download = name || 'photo'; // 设置图片名称
            a.href = imgUrl; // 将生成的URL设置为a.href属性
            a.setAttribute('id', 'myLink');
            // 开发者工具查看生成的链接href的值是否是一个完整的base64格式图片
            // console.log('标签', a);
            document.body.appendChild(a);
            exportCodeConfirm();
            // }
        };
        /** 将html元素转换成海报图片 */
        const createPoster = async () => {
            const name = `会诊总结`;
            const posterHtml = postHtml.value;
            // }, 1000);

            // 生成图片
            const domObj = posterHtml;
            await nextTick();
            // 确保页面渲染完毕
            setTimeout(() => {
                html2canvas(domObj, {
                    allowTaint: true,
                    imageTimeout: 500,
                    useCORS: true,
                    scrollY: -18,
                    scrollX: -0,
                    height: domObj.clientHeight,
                }).then((itsCanvas) => {
                    // document.body.appendChild(itsCanvas);
                    downloadQRImg(itsCanvas, name);
                });
            }, 1000);
        };
        const getEnableConsultationRecordFileInfo = async () => {
            const info = await configurationService.getConfigurationList([configIdMap.ENABLE_CONSULTATION_RECORD_FILE]);
            openMarkName.value = info[configIdMap.ENABLE_CONSULTATION_RECORD_FILE];
        };
        onMounted(async () => {
            await getConsulationSummary();
            getEnableConsultationRecordFileInfo();
            // 默认执行,加载时间较长不放在点击事件中,达到正常保存页面中的在线图片链接的效果
            nextTick(() => {
                setTimeout(() => {
                    const imgTag = document.querySelectorAll('img');
                    // 获取base64编码
                    let base64 = '';
                    // 将页面的img的src值改为base64格式
                    imgTag.forEach((item, i) => {
                        const img = new Image();
                        img.setAttribute('crossOrigin', 'anonymous');
                        // 图片链接地址后增加随机数防止缓存
                        img.src = `${imgTag[i].src}&v=${Math.random()}`;
                        img.onload = () => {
                            base64 = image2Base64(img);
                            imgTag[i].setAttribute('src', base64);
                        };
                    });
                }, 3000);
            });
        });
        return {
            summary,
            preSummary,
            IMG_CATEGORY_ID: '301200001',
            PDF_CATEGORY_ID: '301200004',
            postHtml,
            createPoster,
            commonUtils,
            openMarkName,
        };
    },
});
</script>

<style lang="scss" scoped>
.summary-box {
    padding: 0 2rem 2rem 2rem;
}
.summary-title {
    font-size: 1.5rem;
    font-weight: 400;
    color: $icon-color;
    margin-bottom: 1.4rem;
}
.summary-info {
    background: $bg-text-color;
    border-radius: 0.4rem;
    padding: 1rem;
    color: $grey-color;
    font-size: 1.4rem;
    font-weight: 400;
    margin-bottom: 1.6rem;
}
.summary-desc {
    color: $arrow-color;
    font-size: 1.4rem;
    font-weight: 400;
    margin: 0 0 1.2rem 0.8rem;
}
.summary-content {
    margin: 0 0 2rem 1rem;
    color: $text-color;
    font-size: 1.4rem;
    font-weight: 400;
    li {
        line-height: 3rem;
        list-style: none;
    }
}
.summary-img-box {
    display: flex;
    flex-wrap: wrap;
    padding-bottom: 1rem;
    .summary-img {
        width: 7.4rem;
        height: 7.4rem;
        margin-right: 0.8rem;
    }
}
.summary-pdf {
    font-size: 1.5rem;
    font-weight: 400;
    color: var(--font-color-primary);
    margin-left: 0.4rem;
}
.save-img {
    height: 4.5rem;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    color: $arrow-color;
    span {
        display: inline-block;
        width: 11.9rem;
        height: 4.5rem;
        background: url('../../assets/images/icons/save_page.png') no-repeat;
        background-size: 100%;
    }
}
.flex-page-content {
    overflow-y: visible !important ;
}
.flex-page {
    height: auto;
}
.mark-name {
    padding: 0 2rem 0 2rem;
    img {
        width: 11rem;
        height: 11rem;
    }
    span {
        font-size: 1.7rem;
        font-family: PingFang SC-Bold, PingFang SC;
        font-weight: 400;
        color: #2b2b2b;
        line-height: 3rem;
    }
}
.primary-text {
    width: 100%;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1.5rem 0 1rem 2rem;
}
</style>

Logo

前往低代码交流专区

更多推荐