更新记录

1.0.1(2025-04-07) 下载此版本

添加vue2示例

1.0.0(2025-04-01) 下载此版本

首次发布,支持uniapp(vue2,vue3)


平台兼容性

Vue2 Vue3
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
app-vue × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序 鸿蒙元服务
× × × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari
× × × × ×

基于lime-painter的富文本解析器(typescript) - uni-app

如何使用

安装

支持Uniapp Vue3,如果想用Vue2也是可行的(需要改变语法格式) ,可能还有些缺陷 您也可以修改源码进行改动

基于原作者:陌上华年 lime-painter,开发的扩展插件;使用前请记得插件市场先安装下载 lime-painter ;lime-painter-richtext 是基于 lime-painter 的富文本解析器

您也可以选择直接使用npm安装这个插件,npm安装的方式会更方便一些

npm install lime-painter-richtext

如果是下载这个插件,请将下载的压缩包文件解压以后 放入您自己的文件夹 注意导入路径

使用(Vue3)

<template>
    <view style="padding: 8px;">
        <l-painter :board="poster" ref="painter" @progress="getPosterProgress"/>
    </view>
</template>

<script lang="ts" setup>
    import { ref } from 'vue';

    // 导入lime-painter组件
    import LPainter from '@/uni_modules/lime-painter/components/l-painter/l-painter';
    // 从node_modules安装lime-painter-richtext插件,导入使用
    import HtmlParser from "lime-painter-richtext";

    const painter = ref<InstanceType<typeof LPainter> | null>(null);

    const htmlContent = `
      <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/static/index/logo.png" style="width:200rpx;height:200rpx;z-index:10;position:absolute" />
      <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/29_174321117967e74aab66586.png" style="object-fit:cover;object-position:50% 50%;width:750rpx;height:1200rpx;border-radius:12rpx" />
       <div style="display:flex;align-items:center;justify-content:space-between;background-color:#F9FCFF;padding:20rpx 32rpx;border-radius:40rpx 40rpx 0 0;position:fixed;bottom:0">
         <div style="display:flex;align-items:center;">
            <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/13_174186170967d2b34dbca24.png" style="width:160rpx;height:160rpx;border-radius:50%" />
            <div style="margin-left:20rpx;">
              <div style="font-size:32rpx;font-weight:bold">昵称</div>
              <div style="font-size:24rpx;color:#FF5310;margin-top:10rpx">邀请用户成为我的客户</div>
            </div>
         </div>
         <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/29_174321640667e75f166de46.png" style="width:120rpx;height:120rpx;border-radius:50%" />
       </div>
    `;

    // 海报数据
    const poster = ref({
        css: {
            backgroundColor: "#ffffff",
        },
        views: []
    });

    // 解析html
    const jsonResult = new HtmlParser().parseHtmlToJson(htmlContent);

    poster.value.views = jsonResult;

    // 获取海报生成进度
    const getPosterProgress = (progress: number) => {
        console.log('海报进度', progress);
    };

    // 生成图片
    const handleTask = () => {
        painter.value.canvasToTempFilePathSync({
            fileType: "jpg",
            // 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
            pathType: 'url',
            quality: 1,
            success: (res) => {
                console.log(res.tempFilePath);
                // 非H5 保存到相册
                uni.saveImageToPhotosAlbum({
                    filePath: res.tempFilePath,
                    success: function () {
                        console.log('save success');
                    }
                });
            },
        });
    };
</script>

<style></style>

使用Vue2

<template>
    <view style="padding: 8px;">
        <l-painter :board="poster" ref="painter" @progress="getPosterProgress"/>
    </view>
</template>

<script>
  export default {
    data() {
        return {
            htmlContent: `
              <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/static/index/logo.png" style="width:200rpx;height:200rpx;z-index:10;position:absolute" />
              <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/29_174321117967e74aab66586.png" style="object-fit:cover;object-position:50% 50%;width:750rpx;height:1200rpx;border-radius:12rpx" />
               <div style="display:flex;align-items:center;justify-content:space-between;background-color:#F9FCFF;padding:20rpx 32rpx;border-radius:40rpx 40rpx 0 0;position:fixed;bottom:0">
                 <div style="display:flex;align-items:center;">
                    <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/13_174186170967d2b34dbca24.png" style="width:160rpx;height:160rpx;border-radius:50%" />
                    <div style="margin-left:20rpx;">
                      <div style="font-size:32rpx;font-weight:bold">昵称</div>
                      <div style="font-size:24rpx;color:#FF5310;margin-top:10rpx">邀请用户成为我的客户</div>
                    </div>
                 </div>
                 <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/29_174321640667e75f166de46.png" style="width:120rpx;height:120rpx;border-radius:50%" />
               </div>
            `,
            poster: {
                css: {
                    backgroundColor: "#ffffff",
                },
                views: []
            }
        }
    },
    mounted() {
        // 解析html
        const jsonResult = new HtmlParser().parseHtmlToJson(this.htmlContent);
        this.poster.views = jsonResult;
    },
    methods:{
        getPosterProgress(progress) {
            console.log('海报进度', progress);
        },
        handleTask() {
            this.$refs.painter.canvasToTempFilePathSync({
                fileType: "jpg",
                // 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url
                pathType: 'url',
                quality: 1,
                success: (res) => {
                    console.log(res.tempFilePath);
                    // 非H5 保存到相册
                    uni.saveImageToPhotosAlbum({
                        filePath: res.tempFilePath,
                        success: function () {
                            console.log('save success');
                        }
                    });
                },
            });
        }
    }
</script>

<style></style>

关于objectToString函数,这是将对象转为字符串的函数

这是一个将style样式对象转为字符串的处理函数,您也可以不用这个函数;lime-painter本身也支持object style不过好像会有点小问题

 <script lang="ts" setup>

const nicknameStyle = {
    fontSize: '32rpx',
    fontWeight: 'bold',
}

 const htmlContent = `
      <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/static/index/logo.png" style="width:200rpx;height:200rpx;z-index:10;position:absolute" />
      <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/29_174321117967e74aab66586.png" style="object-fit:cover;object-position:50% 50%;width:750rpx;height:1200rpx;border-radius:12rpx" />
       <div style="display:flex;align-items:center;justify-content:space-between;background-color:#F9FCFF;padding:20rpx 32rpx;border-radius:40rpx 40rpx 0 0;position:fixed;bottom:0">
         <div style="display:flex;align-items:center;">
            <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/13_174186170967d2b34dbca24.png" style="width:160rpx;height:160rpx;border-radius:50%" />
            <div style="margin-left:20rpx;">
              <div :style="nicknameStyle">昵称</div>
              <div style="font-size:24rpx;color:#FF5310;margin-top:10rpx">邀请用户成为我的客户</div>
            </div>
         </div>
         <img src="https://ljx-1301875625.cos.ap-shanghai.myqcloud.com/gxfangchan/202503/29_174321640667e75f166de46.png" style="width:120rpx;height:120rpx;border-radius:50%" />
       </div>
    `;
 </script>
/**
 * 获取一个值的类型字符串。
 * @param value 要检测的值。
 * @returns 返回值的类型。
 */
export const getType = (value : any) : string => {
    const temp = Object.prototype.toString.call(value);
    const type = temp.replace(/\[/g, "").replace(/\]/g, "");
    const index = type.lastIndexOf(" ");
    const str = type.substring(index + 1, type.length);
    return str;
};

/**
 * 判断一个值是否有效(即不为 undefined、null 或空字符串)。
 * @param val 要检查的值。
 * @returns 如果值有效,则返回 true;否则返回 false。
 */
export const isEffective = (val : any) : boolean => {
 return val !== undefined && val !== null && val !== "";
};

/**
 * 驼峰转小写-连接
 * @param {string} str 需要转换的字符串
 * @param {string} mark 转换符号,默认为 "-"
 * @return {string} 转换后的字符串
 * @example toLine('borderRadius')  // 返回 border-radius
 */
export const toLine = (str: string = "", mark: string = "-"): string => {
  return str.replace(/([A-Z])/g, `${mark}$1`).toLowerCase();
};

/**
 * 去掉字符串的空格
 * @param {string} str 需要处理的字符串
 * @param {string} pos 去除空格的位置:both: 左右两边空格,left: 左边空格,right: 右边空格,all: 所有空格
 * @return {string} 返回去除空格后的字符串
 */
export const trim = (str: string, pos: string = "both"): string => {
  str = String(isEffective(str) ? str : "");
  switch (pos) {
    case "both":
      return str.trim();
    case "left":
      return str.replace(/^\s*/, "");
    case "right":
      return str.replace(/(\s*$)/g, "");
    case "all":
      return str.replace(/\s+/g, "");
    default:
      return str;
  }
};

/**
 * object 转 css 字符串
 * @param styleList 样式对象或字符串数组
 * @return {string} 返回字符串 style,兼容其它端
 * @example objectToString('width:10rpx', { height: '20rpx', zIndex: 2 }, 'border:2rpx')
 * @returns 'width:10rpx;height:20rpx;z-index:2;border:2rpx'
 */
export const objectToString = (...styleList : (string | Record<string, string | number>)[]) : string => {
    const styleObj : Record<string, string> = {};

    const addStyle = (name : string, value : string | number) => {
        name = trim(name);
        if (['String', 'Number'].includes(getType(value))) {
            value = trim(String(value));
            if (name && value) {
                styleObj[toLine(name)] = value;
            }
        }
    };

    styleList.forEach((style) => {
        const type = getType(style);
        if (type === 'String' && style) {
            const trimmedStyle = trim(style as string);
            if (trimmedStyle) {
                trimmedStyle
                    .replace(/^;+|;+$/, '')
                    .split(/;+/)
                    .map((str) => str.split(/:+/))
                    .forEach(([k, value]) => addStyle(k, value));
            }
        } else if (type === 'Object') {
            Object.entries(style as Record<string, string | number>).forEach(([k, v]) => addStyle(k, v));
        }
    });

    return Object.entries(styleObj)
        .map(([k, v]) => `${k}:${v}`)
        .join(';');
};

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问