Forráskód Böngészése

Merge branch 'master' of http://git.zthymaoyi.com/wangchao/businessCard

achao 2 éve
szülő
commit
a5fdd61bea

+ 4 - 0
unimall-admin-api/src/main/java/com/iotechn/unimall/admin/api/card/ICircleFriendsDetailService.java

@@ -41,6 +41,10 @@ public interface ICircleFriendsDetailService{
 	@HttpMethod(description = "删除",permissionName = "记录朋友圈互动信息管理")
 	public Boolean delete(@NotNull @HttpParam(name = "id", type = HttpParamType.COMMON, description = "")String id)throws ServiceException;
 
+	@HttpMethod(description = "取消点赞",permissionName = "记录朋友圈互动信息管理")
+	public Boolean cancelLike(@NotNull @HttpParam(name = "circleFriendsId", type = HttpParamType.COMMON, description = "朋友圈id")Long circleFriendsId,
+							  @HttpParam(name = "commonId", type = HttpParamType.COMMON, description = "发表人id") Long commonId)throws ServiceException;
+
 	@HttpMethod(description = "修改",  permissionName = "记录朋友圈互动信息管理")
 	public Boolean update(@NotNull @HttpParam(name = "circleFriendsDetail", type = HttpParamType.COMMON, description = "记录朋友圈互动信息") CircleFriendsDetail circleFriendsDetail)throws ServiceException;
 

+ 1 - 1
unimall-admin-api/src/main/java/com/iotechn/unimall/admin/api/card/ICircleFriendsInfoService.java

@@ -25,8 +25,8 @@ public interface ICircleFriendsInfoService{
 	@HttpMethod(description = "列表",  permissionName = "记录朋友圈信息管理")
 	public Page<CircleFriendsInfo> list(
 								@HttpParam(name = "circleId", type = HttpParamType.COMMON, description = "圈子id") Long circleId,
-								@HttpParam(name = "commonId", type = HttpParamType.COMMON, description = "朋友圈发布人id") Long commonId,
 								@HttpParam(name = "currentCommonId", type = HttpParamType.COMMON, description = "当前登录人id") Long currentCommonId,
+								@HttpParam(name = "commonId", type = HttpParamType.COMMON, description = "朋友圈发布人id") Long commonId,
 								@HttpParam(name = "head", type = HttpParamType.COMMON, description = "头像") String head,
 							@HttpParam(name = "nickname", type = HttpParamType.COMMON, description = "昵称") String nickname,
 							@HttpParam(name = "content", type = HttpParamType.COMMON, description = "朋友圈内容") String content,

+ 17 - 0
unimall-admin-api/src/main/java/com/iotechn/unimall/admin/api/card/impl/CircleFriendsDetailServiceImpl.java

@@ -3,6 +3,7 @@ package com.iotechn.unimall.admin.api.card.impl;
 import java.util.List;
 
 import com.iotechn.unimall.admin.api.card.ICircleFriendsDetailService;
+import com.iotechn.unimall.data.domain.CardExchangeInfo;
 import com.iotechn.unimall.data.domain.CircleFriendsDetail;
 import com.iotechn.unimall.data.mapper.CircleFriendsDetailMapper;
 import org.apache.ibatis.session.RowBounds;
@@ -90,6 +91,22 @@ public class CircleFriendsDetailServiceImpl implements ICircleFriendsDetailServi
 		return true;
 	}
 
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean cancelLike(Long circleFriendsId, Long commonId) throws ServiceException {
+		CircleFriendsDetail circleFriendsDetail = new CircleFriendsDetail();
+		circleFriendsDetail.setCircleFriendsId(circleFriendsId);
+		circleFriendsDetail.setCommonId(commonId);
+		circleFriendsDetail.setInteractionFlag("1");
+		circleFriendsDetail.setDeleteFlag(0l);
+		CircleFriendsDetail circleFriendsDetail1=circleFriendsDetailMapper.selectOne(circleFriendsDetail);
+		if (circleFriendsDetail1!=null){
+			circleFriendsDetail1.setDeleteFlag(1l);
+			circleFriendsDetailMapper.updateById(circleFriendsDetail1);
+		}
+		return true;
+	}
+
 	@Override
 	public Boolean update(CircleFriendsDetail circleFriendsDetail) throws ServiceException {
 		Date now = new Date();

+ 1 - 1
unimall-admin-api/src/main/java/com/iotechn/unimall/admin/api/card/impl/CircleFriendsInfoServiceImpl.java

@@ -113,7 +113,7 @@ public class CircleFriendsInfoServiceImpl implements ICircleFriendsInfoService {
 						.eq("interaction_flag","2"));
 				if(!CollectionUtils.isEmpty(circleFriendsDetailList1)){
 					circleFriendsInfo.setCircleFriendsDetailList1(circleFriendsDetailList1);
-					circleFriendsInfo.setCount(circleFriendsDetailList1.size());
+					circleFriendsInfo.setCount1(circleFriendsDetailList1.size());
 				}
 			}
 		}

+ 776 - 0
xiaochengxu/components/lime-painter/README.md

@@ -0,0 +1,776 @@
+# Painter 画板
+> uniapp 海报画板,可根据自身需求配置生成海报
+> [查看更多](http://liangei.gitee.io/limeui-docs/components/painter/) <br>
+> Q群:458377637 <br>
+> 群里有最新Demo
+<br>
+
+
+### 平台兼容
+
+| H5  | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App |
+| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- |
+| √   | √          | √         | 未测       | √          | √      | √   |
+
+<br>
+<br>
+
+### 引入
+
+```js
+import lPainter from '@/components/lime-painter/'
+export default {
+	components: {lPainter}
+}
+```
+
+<br>
+<br>
+
+### 代码演示
+#### 基本用法
+
+定一个画板对象,包含`width`、`height`、`background`,`views`为画板里的元素集,它是一个数组对象。<br>
+元素类型目前有`view`、`text`、`image`<br>
+css 对象里的位置都是相对于画板的绝对定位,支持`rpx`、`px`
+
+```html
+<l-painter :board="base"/>
+```
+
+```js
+export default {
+    data() {
+        return {
+            base: {
+                width: '686rpx',
+				height: '130rpx',
+				views: [
+					{
+						type: 'view',
+						css: {
+							left: '0rpx',
+							top: '0rpx',
+							background: '#07c160',
+							width: '120rpx',
+							height: '120rpx'
+						}
+					},
+                    {
+						type: 'view',
+						css: {
+							left: '180rpx',
+							top: '18rpx',
+							background: '#1989fa',
+							width: '80rpx',
+							height: '80rpx',
+							transform: 'transform: rotate(50deg)'
+						}
+					}
+				]
+            }
+        }
+    }
+}
+
+```
+
+<br><br>
+
+#### 圆角
+
+为可元素定一个圆角`radius`,支持`rpx`、`px`、`%`
+
+```html
+<l-painter :board="base"/>
+```
+
+```js
+export default {
+data() {
+    return {
+        base: {
+            width: '686rpx',
+            height: '130rpx',
+            views: [
+                {
+                    type: 'view',
+                    css: {
+                        left: '0rpx',
+                        top: '0rpx',
+                        background: '#07c160',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '16rpx 30rpx 10rpx 80rpx'
+                    }
+                },
+                {
+                    type: 'view',
+                    css: {
+                        left: '150rpx',
+                        top: '0rpx',
+                        background: '#1989fa',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '16rpx 60rpx'
+                    }
+                },
+                {
+                    type: 'view',
+                    css: {
+                        left: '300rpx',
+                        top: '0rpx',
+                        background: '#ff976a',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '50%'
+                    }
+                }
+            ]
+        }
+    }
+}
+}
+
+```
+
+<br><br>
+
+#### 描边投影
+
+为可元素定一个描边`border`和投影`shadow`,支持`rpx`、`px`
+
+```html
+<l-painter :board="base"/>
+```
+
+```js
+export default {
+data() {
+    return {
+        base: {
+            width: '686rpx',
+            height: '130rpx',
+            views: [
+                {
+                    type: 'view',
+                    css: {
+                        left: '10rpx',
+                        top: '10rpx',
+                        background: 'rgba(7,193,96,.1)',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '50%',
+                        border: '2rpx dashed rgb(7,193,96)'
+                    }
+                },
+                {
+                    type: 'view',
+                    css: {
+                        left: '150rpx',
+                        top: '10rpx',
+                        background: 'rgba(25,137,250,.4)',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '50%',
+                        shadow: '0 5rpx 10rpx rgba(25,137,250,.8)'
+                    }
+                },
+                {
+                    type: 'view',
+                    css: {
+                        left: '300rpx',
+                        top: '10rpx',
+                        background: 'rgba(255, 151, 106, .1)',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '50%',
+                        border: '2rpx solid #ff976a'
+                    }
+                }
+            ]
+        }
+    }
+}
+}
+
+```
+
+<br><br>
+
+#### 图片
+
+元素类型为`image`时,需要一个图片地址`src`,图片地址支持`相对路径`和`网络地址`<br>
+
+::: warning 温馨提示
+当为网络地址时,<br>
+H5:需要解决跨域问题或使用`isH5PathToBase64`可解决部分问题。 <br>
+小程序:需要配置 downloadFile 域名 <br>
+本插件全端支持 base64 图片 <br>
+:::
+<br>
+
+图片模式`mode`目前支持的有`aspectFill`、`aspectFit`、`scaleToFill`,默认为`scaleToFill`,`height`和`width`支持`auto`,当设置为`auto`时`mode`失效。
+
+
+```html
+<l-painter :board="base"/>
+```
+
+```js
+export default {
+data() {
+    return {
+        base: {
+            width: '686rpx',
+            height: '130rpx',
+            views: [
+                {
+                    type: 'image',
+                    src: '../../static/avatar-1.jpeg',
+                    css: {
+                        left: '0rpx',
+                        top: '0rpx',
+                        width: '120rpx',
+                        height: '120rpx'
+                    }
+                },
+                {
+                    type: 'image',
+                    src: '../../static/avatar-2.jpg',
+                    css: {
+                        left: '150rpx',
+                        top: '0rpx',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '16rpx'
+                    }
+                },
+                {
+                    type: 'image',
+                    src:'../../static/avatar-3.jpeg',
+                    css: {
+                        left: '300rpx',
+                        top: '0rpx',
+                        background: '#ff976a',
+                        width: '120rpx',
+                        height: '120rpx',
+                        radius: '50%'
+                    }
+                }
+            ]
+        }
+    }
+}
+}
+
+```
+
+<br><br>
+
+#### 文字
+
+元素类型`text`时,内容写在`text`里,支持`\n`换行符,css 的属性有字体大小`fontSize`、行高`lineHeight`、对齐`textAlign`、修饰`textDecoration`、粗细`fontWeight`、 宽度`width`、最大行数`maxLines`。
+设置了最大行数和宽度时,当文字内容超过会显示省略号。
+
+```html
+<l-painter :board="base"/>
+```
+
+```js
+export default {
+data() {
+    return {
+        base: {
+            width: '686rpx',
+            height: '500rpx',
+            views: [
+                {
+                    type: 'text',
+                    text: '左对齐,下划线\n无风才到地,有风还满空\n缘渠偏似雪,莫近鬓毛生',
+					// 可以指定关键字颜色
+					rules: {
+						word: ['到地'],
+						color: 'red'
+					},
+                    css: {
+                        left: '0rpx',
+                        top: '10rpx',
+                        fontSize: '28rpx',
+                        lineHeight: '36rpx',
+                        textDecoration: 'underline'
+                    }
+                },
+                {
+                    type: 'text',
+                    text: '居中,上划线\n无风才到地,有风还满空\n缘渠偏似雪,莫近鬓毛生',
+                    css: {
+                        left: '0rpx',
+                        top: '150rpx',
+                        fontSize: '28rpx',
+                        lineHeight: '36rpx',
+                        textAlign: 'center',
+                        textDecoration: 'overline'
+                    }
+                },
+                {
+                    type: 'text',
+                    text: '右对齐,中划线\n无风才到地,有风还满空\n缘渠偏似雪,莫近鬓毛生',
+                    css: {
+                        left: '0rpx',
+                        top: '290rpx',
+                        fontSize: '28rpx',
+                        lineHeight: '36rpx',
+                        textAlign: 'right',
+                        textDecoration: 'line-through',
+                    }
+                },
+                {
+                    type: 'text',
+                    text: '省略号\n明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。',
+                    rules: {
+                    	word: ['几时有'],
+                    	color: 'red'
+                    },
+					css: {
+                        left: '0rpx',
+                        top: '420rpx',
+                        fontSize: '28rpx',
+                        maxLines: 2,
+                        width: '686rpx',
+                        lineHeight: '36rpx'
+                    }
+                }
+            ]
+        }
+    }
+}
+}
+
+```
+<br><br>
+
+#### 二维码
+该方法需要[下载此链接的文件](https://gitee.com/liangei/lime-painter/blob/master/qrcode.js)覆盖插件目录的`qrcode.js`才生效。<br>
+考虑到不是很多人需要,文件又比较大。故出此下策。
+```html
+<l-painter :board="base" />
+```
+```js
+{
+	type: 'qrcode',
+	text: 'https://www.baidu.com',
+	css: {
+		left: '380rpx',
+		top: '230rpx',
+		width: '200rpx',
+		height: '200rpx',
+		color: '#1989fa',
+		backgroundColor: 'rgba(25,137,250,.1)',
+		border: '20rpx solid rgba(25,137,250,.1)',
+	}
+}
+
+```
+
+
+<br>
+
+#### 调用内部方法
+插件也允许通过`ref`获取内部方法来实现绘制和生成图片。
+需要设置画板的`width`和`height`
+```html
+<l-painter ref="painter" width="686rpx"  height="500rpx" />
+<image :src="path" />
+```
+
+```js
+export default {
+	data() {
+		return {
+			base: {
+				width: '686rpx',
+				height: '500rpx',
+				views: [
+					{
+						type: 'view',
+						css: {
+							fontSize: '28rpx',
+							lineHeight: '1.4em',
+						},
+						views: [
+							{
+								type: 'text',
+								text: '水调歌头',
+								css: {
+									display: 'block',
+									fontSize: '42rpx',
+									textAlign: 'center'
+								}
+							},
+							{
+								type: 'text',
+								text: '丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。',
+								css: {
+									display: 'block',
+									textIndent: '2em'
+								}
+							},
+							{
+								type: 'text',
+								text: '明月几时有?把酒问青天。不知天上宫阙,今夕是何年?我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间',
+								css: {
+									display: 'block',
+									color: 'red',
+									textIndent: '2em'
+								}
+							},
+							{
+								type: 'text',
+								text: '转朱阁,低绮户,照无眠。不应有恨,何事长向别时圆?人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟',
+								css: {
+									textIndent: '2em'
+								}
+							}
+						]
+					}
+				]
+			},
+			path: '',
+		}
+	},
+	methods: {
+		onRender() {
+			// 支持通过调用render传入参数
+			const painter = this.$refs.painter
+			painter.render(this.base)
+		},
+		canvasToTempFilePath() {
+			const painter = this.$refs.painter
+			// 支持通过调用canvasToTempFilePath方法传入参数 调取生成图片
+			painter.canvasToTempFilePath().then(res => this.path = res.tempFilePath)
+		}
+	}
+}
+```
+<br>
+
+#### 自定义
+有时画板提供的方法无法满足复杂个性的需要,可通过插件`ref`获取`ctx`来使用原生方法绘制。
+```html
+<l-painter ref="painter" width="686rpx"  height="500rpx" />
+<image :src="path" />
+```
+
+```js
+export default {
+	data() {
+		return {
+			path: '',
+		}
+	},
+	methods: {
+		async onCustom() {
+			const p = this.$refs.custom
+			// 自定义,使用原生和内部方法生成更个性的图形
+			await p.custom(async (ctx, draw) => {
+				// 原生方法的单位为px,如果想用rpx,请使用uni.upx2px(150)
+				// 绘制背景颜色
+				ctx.setFillStyle('#ff976a')
+				ctx.setShadow(5, 5, 50, '#ff976a')
+				ctx.beginPath()
+				ctx.arc(150, 150, 20, Math.PI, Math.PI * 1.5)
+				ctx.lineTo(150, 20)
+				ctx.arc(150, 0, 20, Math.PI * 0.5, Math.PI * 1)
+				ctx.lineTo(5, 0)
+				ctx.arc(0, 0, 20, Math.PI * 0, Math.PI * 0.5)
+				ctx.lineTo(0, 130)
+				ctx.arc(0, 150, 20, Math.PI * 1.5, Math.PI * 2)
+				ctx.lineTo(130, 150)
+				ctx.closePath()
+				ctx.fill()
+				// 绘制好形状后,调用draw的drawImage方法填充图片,图片会下载完成后绘制。无需自己下载。
+				await draw.drawImage('../../static/avatar-1.jpeg', {left: 0, top: 0, width:150, height:150})
+			})
+			await p.custom((ctx) => {
+				// 绘制背景颜色
+				ctx.setFillStyle('#07c160')
+				ctx.beginPath()
+				ctx.arc(280, 50, 50, 0, Math.PI * 2)
+				ctx.closePath()
+				ctx.fill()
+			})
+			// 单独绘制单个或多个元素的内部方法,
+			// 多个是是数组,单个是为对象
+			// 绘制二维码,由于二维码JS文件较大,也不是所有人需要。所以需自行下载覆盖。
+			await p.single({
+				type: 'qrcode',
+				text: 'https://www.baidu.com',
+				css: {
+					left: '380rpx',
+					top: '230rpx',
+					width: '200rpx',
+					height: '200rpx',
+					color: '#1989fa',
+					backgroundColor: 'rgba(25,137,250,.1)',
+					border: '20rpx solid rgba(25,137,250,.1)',
+					transform: 'rotate(45deg)'
+				}
+			})
+			await p.single({
+				type: 'text',
+				text: '床前明月光,疑是地上霜。\n举头望明月,低头思故乡。',
+				css: {
+					left: '0rpx',
+					top: '330rpx'
+				}
+			})
+			// 使用自定义时,要配合方法绘制,否则无法绘制。
+			await p.canvasDraw(true)
+		},
+		// 支持通过调用canvasToTempFilePath方法传入参数 调取生成图片
+		onCanvasToTempFilePath() {
+			const painter = this.$refs.custom
+			painter.canvasToTempFilePath({dpr:2}).then(res => this.path = res.tempFilePath)
+			
+		}
+	}
+}
+```
+
+<br><br>
+
+#### 提供一份海报样式案例
+
+是否生成图片:`isRenderImage` <br>
+生成图片成功:`success`返回一个图片临时地址。
+
+```html
+<l-painter
+  v-if="isShowPainter"
+  isRenderImage
+  custom-style="position: fixed; left: 200%;"
+  :board="base"
+  @success="path = $event"
+/>
+```
+
+```js
+export default {
+data() {
+    return {
+        base: {
+            width: '750rpx',
+            height: '1114rpx',
+            background: '#F6F7FB',
+            views: [
+                {
+                    type: 'view',
+                    css: {
+                        left: '40rpx',
+                        top: '144rpx',
+                        background: '#fff',
+                        radius: '16rpx',
+                        width: '670rpx',
+                        height: '930rpx',
+                        shadow: '0 20rpx 48rpx rgba(0,0,0,.05)'
+                    }
+                },
+                {
+                    type: 'image',
+                    src: '../../static/avatar-2.jpg',
+                    mode: 'widthFix',
+                    css: {
+                        left: '40rpx',
+                        top: '40rpx',
+                        width: '84rpx',
+                        height: '84rpx',
+                        radius: '50%',
+                        color: '#999'
+                    }
+                },
+                {
+                    type: 'text',
+                    text: '隔壁老王',
+                    css: {
+                        color: '#333',
+                        left: '144rpx',
+                        top: '40rpx',
+                        fontSize: '32rpx',
+                        fontWeight: 'bold'
+                    }
+                },
+                {
+                    type: 'text',
+                    text: '为您挑选了一个好物',
+                    css: {
+                        color: '#666',
+                        left: '144rpx',
+                        top: '90rpx',
+                        fontSize: '24rpx'
+                    }
+                },
+                {
+                    type: 'image',
+                    src: '../../static/goods.jpg',
+                    mode: 'widthFix',
+                    css: {
+                        left: '72rpx',
+                        top: '176rpx',
+                        width: '606rpx',
+                        height: '606rpx',
+                        radius: '12rpx'
+                    }
+                },
+                {
+                    type: 'text',
+                    text: '¥39.90',
+                    css: {
+                        color: '#FF0000',
+                        left: '66rpx',
+                        top: '812rpx',
+                        fontSize: '56rpx',
+                        fontWeight: 'bold'
+                    }
+                },
+                {
+                    type: 'text',
+                    text: '360儿童电话手表9X 智能语音问答定位支付手表 4G全网通20米游泳级防水视频通话拍照手表男女孩星空蓝',
+                    css: {
+                        maxLines: 2,
+                        width: '396rpx',
+                        color: '#333',
+                        left: '72rpx',
+                        top: '948rpx',
+                        fontSize: '36rpx',
+                        lineHeight: '50rpx'
+                    }
+                },
+                {
+                    type: 'image',
+                    src: '../../static/qr.png',
+                    mode: 'widthFix',
+                    css: {
+                        left: '500rpx',
+                        top: '864rpx',
+                        width: '178rpx',
+                        height: '178rpx'
+                    }
+                }
+            ]
+		}
+    }
+},
+methods: {
+    saveImage() {
+        this.isShowPopup = false
+        uni.saveImageToPhotosAlbum({
+            filePath: this.path,
+            success(res) {
+                uni.showToast({
+                    title: '已保存到相册',
+                    icon: 'success',
+                    duration: 2000
+                })
+            }
+        })
+    },
+}
+}
+
+```
+
+### API
+
+#### Props
+
+| 参数          | 说明         | 类型             | 默认值       |
+| ------------- | ------------ | ---------------- | ------------ |
+| board         | 画板对象     | <em>object</em>  | 参数请向下看 |
+| customStyle   | 自定义样式   | <em>string</em>  |              |
+| isRenderImage | 是否生成图片,在`@success`事件接收图片地址 | <em>boolean</em> | `false`      |
+| isH5PathToBase64 | H5端把网络图片转成base64,解决部分跨域问题 | <em>boolean</em> | `false`      |
+| isBase64ToPath | H5端把base64转成网络图片,应用场景不多考虑删除慎用 | <em>boolean</em> | `false`      |
+| sleep | 此参数是为解决图片渲染时不按JSON层次前后渲染。若有此情况请增大数值 | <em>number</em> | `33`  |
+| type | 只对微信小程序可有效 | <em>string</em> | `2d`  |
+| fileType | 生成图片的后缀类型 | <em>string</em> | `png`  |
+| pixelRatio | 生成图片的像素密度,默认为对应手机的像素密度 | <em>number</em> | ``  |
+| width | 画板的宽度,一般只用于通过内部方法时加上 | <em>number</em> | ``  |
+| height | 画板的高度  ,同上 | <em>number</em> | ``  |
+
+<br>
+
+#### Board
+
+| 参数       | 说明                               | 类型            |
+| ---------- | ---------------------------------- | --------------- |
+| width      | 画板的宽度                         | <em>string</em> |
+| height     | 画板的高度                         | <em>string</em> |
+| background | 画板的背景色,支持颜色渐变,但要写百分比及方向如:`background: 'linear-gradient(to right, #ff971b 0%, #ff5000 100%)'`                      | <em>string</em> |
+| views      | 画板的元素集,请向下看各元素的参数 | <em>object</em> |
+
+<br>
+
+#### 块 View
+
+| 参数 | 说明                                                                                                |
+| ---- | --------------------------------------------------------------------------------------------------- |
+| type | 元素类型`view`                                                                                      |
+| css  | 元素的样式,`top`、`left`、`width`、`height`、`background`、`radius`、`border`、`shadow` 、`transform`包含缩放: `scaleX(-1)`、旋转`rotate(50deg)`、倾斜`skewX(50deg)` |
+
+<br>
+
+#### 文本 text
+
+| 参数 | 说明                                                                                                                                                                        |
+| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| type | 元素类型`text`                                                                                                                                                              |
+| text | 文本内容                                                                                                                                                                    |
+| css  | 元素的样式,`top`、`left`、`fontSize`、`fontWeight`、`fontFamily`、`width`、`lineHeight`、`color`、`textDecoration`、`textAlign`:center, left, right、最大行数:`maxLines`、`transform` |
+| rules | 是一个对象 `Object`指定文字颜色, `word`关键词数组、`color`关键词颜色,目前只能定一种颜色。|
+				
+<br>
+
+#### 图片 image
+
+| 参数 | 说明                                                                       |
+| ---- | -------------------------------------------------------------------------- |
+| type | 元素类型`image`                                                            |
+| src  | 图片地址                                                                   |
+| css  | 元素的样式,`top`、`left`、`width`、`height`、`radius`、`border`、`shadow`、`transform` |
+
+<br>
+
+#### 二维码 qrcode
+
+| 参数 | 说明                                                                       |
+| ---- | -------------------------------------------------------------------------- |
+| type | 元素类型`qrcode`, 二维码。[需要自行下载覆盖](https://gitee.com/liangei/lime-painter/blob/master/qrcode.js)。                                                            |
+| text  | 二维码文本内容                                                                   |
+| css  | 元素的样式,`top`、`left`、`width`、`height`、`border`、`transform`、`backgroundColor`背景色、`color` 前景色|
+
+<br>
+
+#### 事件 Events
+
+| 事件名  | 说明         | 回调           |
+| ------- | ------------ | -------------- |
+| success | 生成图片成功,若使用了`isRenderImage`可以接收图片地址 |	event      |
+| fail    | 生成图片失败 | {error: error} |
+| done    | 绘制成功 |  |
+
+### 常见问题
+> 1、H5端使用网络图片需要解决跨域问题、或者添加`isH5PathToBase64`可解决部分问题。<br>
+> 2、小程序使用网络图片需要去公众平台增加下载白名单!二级域名也需要配!<br>
+> 3、H5端生成图片是base64,有时显示只有一半可以使用原生标签`<IMG/>`<br>
+> 4、发生保存图片倾斜变形或提示native buffer exceed size limit时,使用pixel-ratio="2"参数,降分辨率。<br>
+> 5、h5保存图片不需要调接口,提示用户长按图片保存。<br>
+> 6、IOS APP 请勿使用HBX2.9.3.20201014的版本!这个版本无法生成图片。<br>
+> 7、不生成图片或不绘制时,请查看是否把组件隐藏了!<br>
+> 8、APP端无成功反馈、也无失败反馈时,请更新基座和HBX。<br>
+> 9、微信小程序2D不支持真机调试,请使用真机预览方式。<br>
+>10、华为手机APP上无法生成图片,请使用HBX2.9.11++<br>
+>11、苹果微信7.0.20存在闪退和图片无法onload为微信bug,请到码云上升级本插件<br>
+### 打赏
+如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。<br>
+![输入图片说明](https://images.gitee.com/uploads/images/2020/1122/222521_bb543f96_518581.jpeg "微信图片编辑_20201122220352.jpg")

+ 78 - 0
xiaochengxu/components/lime-painter/canvas.js

@@ -0,0 +1,78 @@
+import { CHAR_WIDTH_SCALE_MAP } from './utils'
+const _expand = ctx => {
+	// #ifndef APP-PLUS
+	ctx._measureText = ctx.measureText
+	// #endif
+	return {
+		setFonts({fontFamily: ff = 'sans-serif', fontSize: fs = 14, fontWeight: fw = 'normal' , textStyle: ts = 'normal'}) {
+			// let ctx = this.ctx
+			// 设置属性
+			// #ifndef MP-TOUTIAO
+			// fw = fw == 'bold' ? 'bold' : 'normal'
+			// ts = ts == 'italic' ? 'italic' : 'normal'
+			// #endif
+			// #ifdef MP-TOUTIAO
+			fw = fw == 'bold' ? 'bold' : ''
+			ts =  ts == 'italic' ? 'italic' : ''
+			// #endif
+			ctx.font = `${ts} ${fw} ${fs}px ${ff}`;
+		},
+		// #ifndef APP-PLUS
+		measureText(text, fontSize) {
+			// app measureText为0需要累加计算0
+			return {
+				width: text.split("").reduce((widthScaleSum, char) => {
+				let code = char.charCodeAt(0);
+				let widthScale = CHAR_WIDTH_SCALE_MAP[code - 0x20] || 1;
+				return widthScaleSum + widthScale;
+			  }, 0) * fontSize
+			};
+		},
+		// #endif
+	}
+};
+export function expand(ctx) {
+	return Object.assign(ctx, _expand(ctx))
+}
+export function adaptor(ctx) {
+	return Object.assign(ctx, _expand(ctx), {
+		setStrokeStyle(val) {
+			ctx.strokeStyle = val;
+		},
+		setLineWidth(val) {
+			ctx.lineWidth = val;
+		},
+		setLineCap(val) {
+			ctx.lineCap = val;
+		},
+		setFillStyle(val) {
+			ctx.fillStyle = val;
+		},
+		setFontSize(val) {
+			ctx.font = String(val);
+		},
+		setGlobalAlpha(val) {
+			ctx.globalAlpha = val;
+		},
+		setLineJoin(val) {
+			ctx.lineJoin = val;
+		},
+		setTextAlign(val) {
+			ctx.textAlign = val;
+		},
+		setMiterLimit(val) {
+			ctx.miterLimit = val;
+		},
+		setShadow(offsetX, offsetY, blur, color) {
+			ctx.shadowOffsetX = offsetX;
+			ctx.shadowOffsetY = offsetY;
+			ctx.shadowBlur = blur;
+			ctx.shadowColor = color;
+		},
+		setTextBaseline(val) {
+			ctx.textBaseline = val;
+		},
+		createCircularGradient() {},
+		draw() {},
+	});
+}

+ 694 - 0
xiaochengxu/components/lime-painter/draw.js

@@ -0,0 +1,694 @@
+import { toPx, isNumber, getImageInfo  } from './utils'
+import { GD } from './gradient'
+import QR from './qrcode'
+let dtheight = 0
+export class Draw {
+	constructor(context, canvas, use2dCanvas = false, isH5PathToBase64 = false, sleep) {
+		this.ctx = context
+		this.canvas = canvas || null
+		this.use2dCanvas = use2dCanvas
+		this.isH5PathToBase64 = isH5PathToBase64
+		this.sleep = sleep
+	}
+	roundRect(x, y, w, h, r, fill = false, stroke = false, ) {
+		if (r < 0) return
+		const {ctx} = this
+		ctx.beginPath()
+		if(!r) {
+			ctx.rect(x, y, w, h)
+		} else if(typeof r === 'number' && [0,1,-1].includes(w - r * 2) &&  [0, 1, -1].includes(h - r * 2)) {
+			ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 2)
+		} else {
+			let {
+				borderTopLeftRadius: tl = r || 0,
+				borderTopRightRadius: tr = r || 0,
+				borderBottomRightRadius: br = r || 0,
+				borderBottomLeftRadius: bl = r || 0
+			} = r || {r,r,r,r}
+			// 右下角
+			ctx.arc(x + w - br, y + h - br, br, 0, Math.PI * 0.5)
+			ctx.lineTo(x + bl, y + h)
+			// 左下角
+			ctx.arc(x + bl, y + h - bl, bl, Math.PI * 0.5, Math.PI)
+			ctx.lineTo(x, y + tl)
+			// 左上角
+			ctx.arc(x + tl, y + tl, tl, Math.PI, Math.PI * 1.5)
+			ctx.lineTo(x + w - tr, y)
+			// 右上角
+			ctx.arc(x + w - tr, y + tr, tr, Math.PI * 1.5, Math.PI * 2)
+			ctx.lineTo(x + w, y + h - br)
+		}
+		ctx.closePath()
+		if (stroke) ctx.stroke()
+		if (fill) ctx.fill()
+	}
+	setTransform(box, {transform}) {
+		const {ctx} = this
+		let {
+			scaleX = 1,
+			scaleY = 1,
+			translateX = 0,
+			translateY = 0,
+			rotate = 0,
+			skewX = 0,
+			skewY = 0
+		} = transform || {}
+		let {
+			left: x,
+			top: y,
+			width: w,
+			height: h
+		} = box
+		translateX = toPx(translateX, w) || 0
+		translateY = toPx(translateY, h) || 0
+		ctx.scale(scaleX, scaleY)
+		ctx.translate(
+			w * (scaleX > 0 ? 1 : -1) / 2 + (x + translateX) / scaleX,  
+			h * (scaleY > 0 ? 1 : -1) / 2 + (y + translateY) / scaleY) 
+		
+		if(rotate) {
+			ctx.rotate(rotate * Math.PI / 180)
+		}
+		if(skewX || skewY) {
+			ctx.transform(1, Math.tan(skewY * Math.PI/180), Math.tan(skewX * Math.PI/180), 1 , 0, 0)
+		}
+	}
+	setBackground(bg , w, h) {
+		const {ctx} = this
+		if (!bg) {
+			// #ifndef MP-TOUTIAO || MP-BAIDU
+			ctx.setFillStyle('transparent')
+			// #endif
+			// #ifdef MP-TOUTIAO || MP-BAIDU
+			ctx.setFillStyle('rgba(0,0,0,0)')
+			// #endif
+		} else if(GD.isGradient(bg)) {
+			GD.doGradient(bg, w, h, ctx);
+		} else {
+			ctx.setFillStyle(bg)
+		}
+	}
+	setShadow({boxShadow: bs = []}) {
+		const {ctx} = this
+		if (bs.length) {
+			const [x, y, b, c] = bs
+			ctx.setShadow(x, y, b, c)
+		}
+	}
+	setBorder(box, style) {
+		const {ctx} = this
+		let {
+			left: x,
+			top: y,
+			width: w,
+			height: h
+		} = box
+		const {border, borderBottom, borderTop, borderRight, borderLeft, borderRadius: r, opacity = 1} = style;
+		const {
+			borderWidth : bw = 0,
+			borderStyle : bs,
+			borderColor : bc,
+		} = border || {}
+		const {
+			borderBottomWidth : bbw = bw,
+			borderBottomStyle : bbs = bs,
+			borderBottomColor : bbc= bc,
+		} = borderBottom || {}
+		const {
+			borderTopWidth: btw = bw,
+			borderTopStyle: bts = bs,
+			borderTopColor: btc = bc,
+		} = borderTop || {}
+		const {
+			borderRightWidth: brw = bw,
+			borderRightStyle: brs = bs,
+			borderRightColor: brc = bc,
+		} = borderRight || {}
+		const {
+			borderLeftWidth: blw = bw,
+			borderLeftStyle: bls = bs,
+			borderLeftColor: blc  = bc,
+		} = borderLeft || {}
+		
+		let {
+			borderTopLeftRadius: tl = r || 0,
+			borderTopRightRadius: tr = r || 0,
+			borderBottomRightRadius: br = r || 0,
+			borderBottomLeftRadius: bl = r || 0
+		} = r || {r,r,r,r}
+		if(!borderBottom && !borderLeft && !borderTop && !borderRight && !border) return;
+		const _borderType = (w, s, c) => {
+			// #ifndef APP-NVUE
+			if (s == 'dashed') {
+				// #ifdef MP
+				ctx.setLineDash([Math.ceil(w * 4 / 3), Math.ceil(w * 4 / 3)])
+				// #endif
+				// #ifndef MP
+				ctx.setLineDash([Math.ceil(w * 6), Math.ceil(w * 6)])
+				// #endif
+			} else if (s == 'dotted') {
+				ctx.setLineDash([w, w])
+			}
+			// #endif
+			ctx.setStrokeStyle(c)
+		}
+		const _setBorder = (x1, y1, x2, y2, x3, y3, r1, r2, p1, p2, p3,  bw, bs, bc) => {
+			ctx.save()
+			this.setOpacity(style)
+			this.setTransform(box, style)
+			ctx.setLineWidth(bw)
+			_borderType(bw, bs, bc)
+			ctx.beginPath()
+			ctx.arc(x1, y1, r1, Math.PI * p1, Math.PI * p2)
+			ctx.lineTo(x2, y2)
+			ctx.arc(x3, y3, r2, Math.PI * p2, Math.PI * p3)
+			ctx.stroke()
+			ctx.restore()
+		}
+	
+		if(border) {
+			ctx.save()
+			this.setOpacity(style)
+			this.setTransform(box, style)
+			ctx.setLineWidth(bw)
+			_borderType(bw, bs, bc)
+			this.roundRect(-w/2, -h/2, w, h, r, false, bc ? true : false)
+			ctx.restore()
+		}
+		x = -w/2
+		y = -h/2
+		if(borderBottom) {
+			_setBorder(x + w - br, y + h - br, x + bl, y + h, x + bl, y + h - bl, br, bl, 0.25, 0.5, 0.75, bbw, bbs, bbc)
+		}
+		if(borderLeft) {
+			// 左下角
+			_setBorder(x + bl, y + h - bl, x, y + tl, x + tl, y + tl, bl, tl, 0.75, 1, 1.25, blw, bls, blc)
+		}
+		if(borderTop) {
+			// 左上角
+			_setBorder(x + tl, y + tl, x + w - tr, y, x + w - tr, y + tr, tl, tr, 1.25, 1.5, 1.75, btw, bts, btc)
+		}
+		if(borderRight) {
+			// 右上角
+			_setBorder(x + w - tr, y + tr, x + w, y + h - br, x + w - br, y + h - br, tr, br, 1.75, 2, 0.25, btw, bts, btc)
+		}
+	}
+	setOpacity({opacity = 1}) {
+		this.ctx.setGlobalAlpha(opacity)
+	}
+	drawView(box, style) {
+		const {ctx} = this
+		const {
+			left: x,
+			top: y,
+			width: w,
+			height: h
+		} = box
+		let {
+			borderRadius = 0,
+			border,
+			borderTop,
+			borderBottom,
+			borderLeft,
+			borderRight,
+			color = '#000000',
+			backgroundColor: bg,
+			rotate,
+			shadow
+		} = style || {}
+		ctx.save()
+		this.setOpacity(style)
+		this.setTransform(box, style)
+		this.setShadow(style)
+		this.setBackground(bg, w, h)
+		this.roundRect(-w/2, -h/2, w, h, borderRadius, true, false)
+		ctx.restore()
+		this.setBorder(box, style)
+	}
+	async drawImage(img, box = {}, style = {}, custom = true) {
+		await new Promise(async (resolve, reject) => {
+			const {ctx} = this
+			const canvas = this.canvas
+			let {
+				borderRadius = 0,
+				mode,
+				padding = {},
+				backgroundColor: bg,
+			} = style
+			const {paddingTop: pt = 0, paddingLeft: pl= 0, paddingRight: pr= 0, paddingBottom: pb = 0} = padding
+			let {
+				left: x,
+				top: y,
+				width: w,
+				height: h
+			} = box
+			ctx.save()
+			if(!custom) {
+				this.setOpacity(style)
+				this.setTransform(box, style)
+				if(bg) {
+					this.setBackground(bg, w, h)
+				}
+				this.setShadow(style)
+				x = -w/2
+				y = -h/2
+				this.roundRect(x, y, w, h, borderRadius, borderRadius ? true : false, false)
+			}
+			ctx.clip()
+			const _modeImage = (img) => {
+				x += pl
+				y += pt
+				w = w - pl - pr
+				h = h - pt - pb
+				// 获得图片原始大小
+				let rWidth = img.width
+				let rHeight = img.height
+				let startX = 0
+				let startY = 0
+				// 绘画区域比例
+				const cp = w / h
+				// 原图比例
+				const op = rWidth / rHeight
+				if(!img.width) {
+					mode = 'scaleToFill'
+				}
+				switch(mode) {
+					case 'scaleToFill': 
+						ctx.drawImage(img.src, x, y, w, h);
+						break;
+					case 'aspectFit': 	
+						if(cp >= op) {
+							rWidth = h * op;
+							rHeight = h
+							startX = x + Math.round(w - rWidth) / 2 
+							startY = y
+						} else {
+							rWidth = w
+							rHeight = w / op;
+							startX = x
+							startY = y + Math.round(h - rHeight) / 2 
+						}
+						ctx.drawImage(img.src, startX, startY, rWidth, rHeight);
+						break;
+					case 'aspectFill': 
+						if (cp >= op) {
+							rHeight = rWidth / cp;
+							// startY = Math.round((h - rHeight) / 2)
+						} else {
+							rWidth = rHeight * cp;
+							startX = Math.round(((img.width || w) - rWidth) / 2)
+						}
+						// 百度小程序 开发工具 顺序有问题 暂不知晓真机
+						// #ifdef MP-BAIDU
+						ctx.drawImage(img.src, x, y, w, h, startX, startY, rWidth, rHeight)
+						// #endif
+						// #ifndef MP-BAIDU
+						ctx.drawImage(img.src, startX, startY, rWidth, rHeight, x, y, w, h)
+						// #endif
+						 break;
+					default: 
+						ctx.drawImage(img.src, x, y, w, h);
+				}
+			}
+			const _restore = () => {
+				ctx.restore()
+				this.setBorder(box, style)
+				setTimeout(() => {
+					resolve(true)
+				}, this.sleep)
+			}
+			const _drawImage = (img) => {
+				if (this.use2dCanvas) {
+					const Image = canvas.createImage()
+					Image.onload = () => {
+						img.src = Image
+						_modeImage(img)
+						_restore()
+					}
+					Image.onerror = () => {
+						console.error(`createImage fail: ${JSON.stringify(img)}`)
+						reject(new Error(`createImage fail: ${JSON.stringify(img)}`))
+					}
+					Image.src = img.src
+				} else {
+					_modeImage(img)
+					_restore()
+				}
+			}
+			
+			if(typeof img === 'string') {
+				const {path: src, width, height} = await getImageInfo(img, this.isH5PathToBase64)
+				_drawImage({src, width, height})
+			} else {
+				_drawImage(img)
+			}
+		})
+	}
+	drawText(text, box, style, rules) {
+	console.log(text, box, style, rules)
+		const {ctx} = this
+		let {
+			left: x,
+			top: y,
+			width: w,
+			height: h,
+			offsetLeft: ol = 0
+		} = box
+		let {
+			color = '#000000',
+			lineHeight = '1.4em',
+			fontSize = 14,
+			fontWeight,
+			fontFamily,
+			textStyle,
+			textAlign = 'left',
+			verticalAlign: va = 'top',
+			backgroundColor: bg,
+			maxLines,
+			display,
+			padding = {},
+			borderRadius = 0,
+			textDecoration: td
+		} = style
+		const {paddingTop: pt = 0, paddingLeft: pl = 0} = padding
+		lineHeight = toPx(lineHeight, fontSize)
+		if (!text) return
+		ctx.save()
+		this.setOpacity(style)
+		this.setTransform(box, style)
+		x = -w/2
+		y = -h/2
+		ctx.setTextBaseline(va)
+		ctx.setFonts({fontFamily, fontSize, fontWeight, textStyle})
+		ctx.setTextAlign(textAlign)
+		
+		if(bg) {
+			this.setBackground(bg, w, h)
+			this.roundRect(x, y, w, h, borderRadius, 1, 0)
+		 }
+		 if(display && display.includes('lock')) {
+			x += pl
+			y += pt 
+		 }
+		this.setShadow(style)
+		ctx.setFillStyle(color)
+		let rulesObj = {};
+		if(rules) {
+			if (rules.word.length > 0) {
+				for (let i = 0; i < rules.word.length; i++) {
+					let startIndex = 0,
+						index;
+					while ((index = text.indexOf(rules.word[i], startIndex)) > -1) {
+						rulesObj[index] = { 
+							reset: true
+						};
+						for (let j = 0; j < rules.word[i].length; j++) {
+							rulesObj[index + j] = { 
+								reset: true
+							};
+						}
+						startIndex = index + 1;
+					}
+				}
+			}
+		}
+		// 水平布局
+		switch (textAlign) {
+			case 'left':
+				break
+			case 'center':
+				x += 0.5 * w
+				break
+			case 'right':
+				x += w
+				break
+			default:
+				break
+		}
+		const textWidth = ctx.measureText(text, fontSize).width
+		const actualHeight = Math.ceil(textWidth / w) * lineHeight
+		let paddingTop = Math.ceil((h - actualHeight) / 2)
+		if (paddingTop < 0) paddingTop = 0
+		// 垂直布局
+		switch (va) {
+			case 'top':
+				break
+			case 'middle':
+				y += fontSize / 2
+				break
+			case 'bottom':
+				y += fontSize 
+				break
+			default:
+				break
+		}
+		
+		// 绘线
+		const _drawLine = (x, y, textWidth) => {
+			const { system } = uni.getSystemInfoSync()
+			if(/win|mac/.test(system)){
+				y += (fontSize / 3)
+			}
+			// 垂直布局
+			switch (va) {
+				case 'top':
+					break
+				case 'middle':
+					y -= fontSize / 2 
+					break
+				case 'bottom':
+					y -= fontSize
+					break
+				default:
+					break
+			}
+			let to = x
+			switch (textAlign) {
+				case 'left':
+					x = x
+					to+= textWidth
+					break
+				case 'center':
+					x = x - textWidth / 2
+					to = x + textWidth
+					break
+				case 'right':
+					to = x
+					x = x - textWidth
+					break
+				default:
+					break
+			}
+			
+			if(td) {
+				ctx.setLineWidth(fontSize / 13);
+				ctx.beginPath();
+				
+				if (/\bunderline\b/.test(td)) {
+					y -= inlinePaddingTop * 0.8
+					ctx.moveTo(x, y);
+					ctx.lineTo(to, y);
+				}
+				
+				if (/\boverline\b/.test(td)) {
+					y += inlinePaddingTop
+					ctx.moveTo(x, y - lineHeight);
+					ctx.lineTo(to, y - lineHeight);
+				}
+				if (/\bline-through\b/.test(td)) {
+					ctx.moveTo(x , y - lineHeight / 2 );
+					ctx.lineTo(to, y - lineHeight /2 );
+				}
+				ctx.closePath();
+				ctx.setStrokeStyle(color);
+				ctx.stroke();
+			}
+		}
+		const _reset = (text, x, y) => {
+			const rs = Object.keys(rulesObj)
+			for (let i = 0; i < rs.length; i++) {
+				const item = rulesObj[rs[i]]
+				// ctx.globalCompositeOperation = "destination-out";
+				ctx.save();
+				ctx.setFillStyle(rules.color);
+				if(item.char) {
+					ctx.fillText(item.char, item.x , item.y)
+				}
+				ctx.restore();
+			}
+		}
+		const _setText = (isReset, char) => {
+			if(isReset) {
+				const t1 = Math.round(ctx.measureText('\u0020', fontSize).width)
+				const t2 = Math.round(ctx.measureText('\u3000', fontSize).width)
+				const width = Math.round(ctx.measureText(char, fontSize).width)
+				let _char = ''
+				let _num = 1
+				if(width == t2){
+					_char ='\u3000'
+					_num = 1
+				} else {
+					_char = '\u0020'
+					_num = Math.ceil(width / t1)
+				}
+				return {char: new Array(_num).fill(_char).join(''), width}
+			} else {
+				return {char}
+			}
+		}
+		const _setRulesObj = (text, index, x, y) => {
+			rulesObj[index].x = x
+			rulesObj[index].y = y
+			rulesObj[index].char = text
+		}
+		const _setRules = (x, rs, text, textWidth, {startIndex = 0, endIndex}) => {
+			let clonetext = text
+			if(/·/.test(text)) {
+				clonetext = clonetext.replace(/·/g, '.')
+				textWidth = ctx.measureText(clonetext, fontSize).width
+			}
+			let _text = text.split('')
+			console.log(_text)
+			let _x = x
+			let first = true
+			for (let i = 0; i < rs.length; i++) {
+				const index = rs[i]
+				const key = index - startIndex
+				const t = _text[key]
+
+				if(t) {
+					let {char, width} = _setText(rulesObj[index], t)
+					_text[key] = char
+					if(first) {
+						first = false
+						let dot = 0
+						let dot2 = 0
+						let num = 0
+						if(textAlign == 'center') {
+							_x = x - 0.5 * (textWidth - width - (dot2 - dot) * num)
+						}
+						if(textAlign == 'right') {
+							 _x = x - textWidth + width + (dot2 - dot) * num
+						}
+					}
+					_setRulesObj(t, index, _x  + ctx.measureText(clonetext.substring(0, key), fontSize).width, y + inlinePaddingTop)
+				} else {
+					continue
+				}
+				
+			}
+			return _text
+		}
+		const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)
+		// 不超过一行
+		if (textWidth + ol <= w && !text.includes('\n')) {
+			x = x + ol
+			const rs = Object.keys(rulesObj)
+			let _text = ''
+			if(rs) {
+				_text = _setRules(x, rs, text, textWidth, {})
+				_reset()
+			}
+			ctx.fillText(_text.join(''), x, y + inlinePaddingTop)
+			y += lineHeight
+			_drawLine(x, y, textWidth)
+			ctx.restore()
+			this.setBorder(box, style)
+			return
+		}
+		// 多行文本
+		const chars = text.split('')
+		const _y = y
+		let _x = x
+		// 逐行绘制
+		let line = ''
+		let lineIndex = 0
+		let startIndex = 0
+		for(let index = 0 ; index <= chars.length; index++){
+			let ch = chars[index] || ''
+			const isLine = ch === '\n'
+			const isRight = ch == ''// index == chars.length
+			ch = isLine ? '' : ch;
+			let textline = line + ch
+			let textWidth = ctx.measureText(textline, fontSize).width
+			// 绘制行数大于最大行数,则直接跳出循环
+			if (lineIndex >= maxLines) {
+				break;
+			}
+			if(lineIndex == 0) {
+				textWidth = textWidth + ol
+				_x = x + ol
+			} else {
+				textWidth = textWidth
+				_x = x
+			}
+			
+			if (textWidth > w || isLine || isRight) {
+				let endIndex = index
+				lineIndex++
+				line = isRight && textWidth <= w ? textline : line
+				if(lineIndex === maxLines && textWidth > w) {
+					while( ctx.measureText(`${line}...`, fontSize).width > w) {
+						if (line.length <= 1) {
+							// 如果只有一个字符时,直接跳出循环
+							break;
+						}
+						line = line.substring(0, line.length - 1);
+					}
+					line += '...'
+				}
+				const rs = Object.keys(rulesObj)
+				let _text = ''
+				if(rs) {
+					_text = _setRules(x, rs, line, textWidth, {startIndex, endIndex})
+					_reset()
+				}
+				ctx.fillText(_text.join(''), _x, y + inlinePaddingTop)
+				y += lineHeight
+				_drawLine(_x, y, textWidth)
+				line = ch
+				startIndex = endIndex + (isLine ? 1 : 0)
+				// if ((y + lineHeight) > (_y + h)) break
+			} else {
+				line = textline
+			}
+		}
+		// const rs = Object.keys(rulesObj)
+		// if(rs) {
+		// 	_reset()
+		// }
+		ctx.restore()
+		this.setBorder(box, style)
+	}
+	async drawNode(element) {
+		console.log(element,'element')
+		const {
+			layoutBox,
+			computedStyle,
+			name,
+			rules,
+			use
+		} = element
+		const {
+			src,
+			text
+		} = element.attributes
+		var that = this
+		if (name === 'view') {
+			this.drawView(layoutBox, computedStyle)
+		} else if (name === 'image' && src) {
+			// console.log(element.attributes, layoutBox, computedStyle)
+			await this.drawImage(element.attributes, layoutBox, computedStyle, false)
+		} else if (name === 'text') {
+			this.drawText(text, layoutBox, computedStyle, rules)
+		} else if (name === 'qrcode') {
+			QR.api.draw(text, that, layoutBox, computedStyle)
+		}
+		if (!element.children) return
+		const childs = Object.values ? Object.values(element.children) : Object.keys(element.children).map((key) => element.children[key]);
+			for (const child of childs) {
+				await this.drawNode(child)
+			}
+		
+	}
+}

+ 107 - 0
xiaochengxu/components/lime-painter/gradient.js

@@ -0,0 +1,107 @@
+/* eslint-disable */
+
+export const GD = {
+	isGradient(bg) {
+		if (bg && (bg.startsWith('linear') || bg.startsWith('radial'))) {
+			return true;
+		}
+		return false;
+	},
+	doGradient(bg, width, height, ctx) {
+		if (bg.startsWith('linear')) {
+			linearEffect(width, height, bg, ctx);
+		} else if (bg.startsWith('radial')) {
+			radialEffect(width, height, bg, ctx);
+		}
+	},
+}
+
+function analizeGrad(string) {
+	const colorPercents = string.substring(0, string.length - 1).split("%,");
+	const colors = [];
+	const percents = [];
+	for (let colorPercent of colorPercents) {
+		colors.push(colorPercent.substring(0, colorPercent.lastIndexOf(" ")).trim());
+		percents.push(colorPercent.substring(colorPercent.lastIndexOf(" "), colorPercent.length) / 100);
+	}
+	return {
+		colors: colors,
+		percents: percents
+	};
+}
+
+function radialEffect(width, height, bg, ctx) {
+	const colorPer = analizeGrad(bg.match(/radial-gradient\((.+)\)/)[1]);
+	const grd = ctx.createCircularGradient(0, 0, width < height ? height / 2 : width / 2);
+	for (let i = 0; i < colorPer.colors.length; i++) {
+		grd.addColorStop(colorPer.percents[i], colorPer.colors[i]);
+	}
+	ctx.setFillStyle(grd);
+}
+
+function analizeLinear(bg, width, height) {
+	const direction = bg.match(/([-]?\d{1,3})deg/);
+	const dir = direction && direction[1] ? parseFloat(direction[1]) : 0;
+	let coordinate;
+	switch (dir) {
+		case 0:
+			coordinate = [0, -height / 2, 0, height / 2];
+			break;
+		case 90:
+			coordinate = [width / 2, 0, -width / 2, 0];
+			break;
+		case -90:
+			coordinate = [-width / 2, 0, width / 2, 0];
+			break;
+		case 180:
+			coordinate = [0, height / 2, 0, -height / 2];
+			break;
+		case -180:
+			coordinate = [0, -height / 2, 0, height / 2];
+			break;
+		default:
+			let x1 = 0;
+			let y1 = 0;
+			let x2 = 0;
+			let y2 = 0;
+			if (direction[1] > 0 && direction[1] < 90) {
+				x1 = (width / 2) - ((width / 2) * Math.tan((90 - direction[1]) * Math.PI * 2 / 360) - height / 2) * Math.sin(2 * (
+					90 - direction[1]) * Math.PI * 2 / 360) / 2;
+				y2 = Math.tan((90 - direction[1]) * Math.PI * 2 / 360) * x1;
+				x2 = -x1;
+				y1 = -y2;
+			} else if (direction[1] > -180 && direction[1] < -90) {
+				x1 = -(width / 2) + ((width / 2) * Math.tan((90 - direction[1]) * Math.PI * 2 / 360) - height / 2) * Math.sin(2 * (
+					90 - direction[1]) * Math.PI * 2 / 360) / 2;
+				y2 = Math.tan((90 - direction[1]) * Math.PI * 2 / 360) * x1;
+				x2 = -x1;
+				y1 = -y2;
+			} else if (direction[1] > 90 && direction[1] < 180) {
+				x1 = (width / 2) + (-(width / 2) * Math.tan((90 - direction[1]) * Math.PI * 2 / 360) - height / 2) * Math.sin(2 * (
+					90 - direction[1]) * Math.PI * 2 / 360) / 2;
+				y2 = Math.tan((90 - direction[1]) * Math.PI * 2 / 360) * x1;
+				x2 = -x1;
+				y1 = -y2;
+			} else {
+				x1 = -(width / 2) - (-(width / 2) * Math.tan((90 - direction[1]) * Math.PI * 2 / 360) - height / 2) * Math.sin(2 *
+					(90 - direction[1]) * Math.PI * 2 / 360) / 2;
+				y2 = Math.tan((90 - direction[1]) * Math.PI * 2 / 360) * x1;
+				x2 = -x1;
+				y1 = -y2;
+			}
+			coordinate = [x1, y1, x2, y2];
+			break;
+	}
+	return coordinate;
+}
+
+function linearEffect(width, height, bg, ctx) {
+	const param = analizeLinear(bg, width, height);
+	const grd = ctx.createLinearGradient(param[0], param[1], param[2], param[3]);
+	const content = bg.match(/linear-gradient\((.+)\)/)[1];
+	const colorPer = analizeGrad(content.substring(content.indexOf(',') + 1));
+	for (let i = 0; i < colorPer.colors.length; i++) {
+		grd.addColorStop(colorPer.percents[i], colorPer.colors[i]);
+	}
+	ctx.setFillStyle(grd);
+}

+ 300 - 0
xiaochengxu/components/lime-painter/index.vue

@@ -0,0 +1,300 @@
+<template>
+	<canvas 
+		v-if="use2dCanvas" 
+		:id="canvasId" 
+		type="2d" 
+		:style="style"
+		style='position: fixed;
+    top: 100rpx;
+    left: 750rpx;'
+		>
+	</canvas>
+	<canvas
+		v-else 
+		:canvas-id="canvasId" 
+		:style="style" 
+		:id="canvasId" 
+		:width="boardWidth * dpr" 
+		:height="boardHeight * dpr"
+		style='position: fixed;
+		top: 100rpx;
+		left: 750rpx;'
+		>
+	</canvas>
+</template>
+
+<script>
+import { toPx, base64ToPath, compareVersion} from './utils';
+import { Draw } from './draw';
+import { Layout } from './layout';
+import { adaptor, expand } from './canvas';
+export default {
+	// version: '1.5.9.7',
+	name: 'l-painter',
+	props: {
+		board: Object,
+		fileType: {
+			type: String,
+			default: 'png'
+		},
+		quality: {
+			type: Number,
+			default: 1
+		},
+		width: [Number, String],
+		height: [Number, String],
+		pixelRatio: Number,
+		customStyle: String,
+		isRenderImage: Boolean,
+		isBase64ToPath: Boolean,
+		isH5PathToBase64: Boolean,
+		sleep: {
+			type: Number,
+			default: 1000/30
+		},
+		// #ifdef MP-WEIXIN
+		type: {
+			type: String,
+			default: '2d',
+		},
+		// #endif
+		
+	},
+	data() {
+		return {
+			// #ifndef MP-WEIXIN || MP-QQ || MP-BAIDU
+			canvasId: `l-painter${this._uid}`,
+			// #endif
+			// #ifdef MP-WEIXIN || MP-QQ || MP-BAIDU
+			canvasId: `l-painter`,
+			// #endif
+			// #ifdef MP-WEIXIN
+			use2dCanvas: true,
+			// #endif
+			// #ifndef MP-WEIXIN
+			use2dCanvas: false,
+			// #endif
+			draw: null,
+			ctx: null,
+			layout: new Layout()
+		};
+	},
+	computed: {
+		newboard() {
+			console.log(1111,'board')
+			return this.board && JSON.parse(JSON.stringify(this.board))
+		},
+		style() {
+			console.log( `width:${this.boardWidth}px; height: ${this.boardHeight}px; ${this.customStyle}`)
+				return `width:${this.boardWidth}px; height: ${this.boardHeight}px; ${this.customStyle}`;
+		},
+		dpr() {
+			return this.pixelRatio || uni.getSystemInfoSync().pixelRatio;
+		},
+		boardWidth() {
+			const { width = 200 } = this.board || {};
+			return toPx(this.width || width);
+		},
+		boardHeight() {
+			const { height = 200 } = this.board || {};
+			var h=height.split('r')[0]
+			let height1=uni.getStorageSync('dtheight')?Number(h)+Number(uni.getStorageSync('dtheight'))+'rpx':height
+			
+			return toPx(this.height || height1);
+		}
+	},
+	watch: {
+		style() {
+			// #ifdef MP-WEIXIN
+			if(this.use2dCanvas) {
+				this.inited = false;
+			}
+			// #endif
+			// #ifdef MP-ALIPAY
+			this.inited = false;
+			// #endif
+		},
+	},
+	mounted() {
+		// #ifdef MP-WEIXIN
+		const {SDKVersion, version, platform} = wx.getSystemInfoSync()
+		// ios wx7.0.20 createImage bug
+		this.use2dCanvas = (this.type === '2d' && compareVersion(SDKVersion, '2.9.2') >= 0) && !(/ios/.test(platform) && /7.0.20/.test(version));
+		// #endif
+		this.$watch('board', async (val, old) => {
+			if (JSON.stringify(val) === '{}' || !val) return;
+			this.render();
+		}, {
+			deep: true, 
+			immediate: true,
+			})
+	},
+	methods: {
+		async render(args = {}, single = false) {
+			const isArgsNoEmpty = JSON.stringify(args) != '{}'
+			const ctx = await this.getContext()
+			const { use2dCanvas, boardWidth, boardHeight, board, canvas, isBase64ToPath, isH5PathToBase64, sleep } = this;
+			if (use2dCanvas && !canvas) {
+				return Promise.reject(new Error('render: fail canvas has not been created'));
+			}
+			this.boundary = {
+			  top: 0,
+			  left: 0,
+			  width: boardWidth,
+			  height: boardHeight,
+			}
+			if(!single) {
+				ctx.clearRect(0, 0, boardWidth, boardHeight);
+			}
+			if(!this.draw || isArgsNoEmpty) {
+				this.draw = new Draw(ctx, canvas, use2dCanvas, isH5PathToBase64, sleep);
+			} 
+			this.layout.init(ctx, this.boundary, this.isH5PathToBase64)
+			if(isArgsNoEmpty || board && JSON.stringify(board) != '{}') {
+				this.node = await this.layout.calcNode(isArgsNoEmpty ? args : board)
+			}
+			// console.log(this.node,'node')
+			if(this.node) {
+				await this.draw.drawNode(this.node);
+			}
+			await new Promise(resolve => this.$nextTick(resolve)) 
+			if (!use2dCanvas && !single) {
+				await this.canvasDraw(ctx);
+			}
+			this.$emit('done')
+			if(this.isRenderImage && !single) {
+				this.canvasToTempFilePath()
+				.then(async res => {
+					if(/^data:image\/(\w+);base64/.test(res.tempFilePath) && isBase64ToPath) {
+						const img = await base64ToPath(res.tempFilePath)
+						this.$emit('success', img)
+					} else {
+						this.$emit('success', res.tempFilePath)
+					}
+				})
+				.catch(err => {
+					this.$emit('fail', err)
+					new Error(JSON.stringify(err))
+					console.error(JSON.stringify(err))
+				})
+			}
+			
+			return Promise.resolve({ctx, draw: this.draw});
+		},
+		async custom(cb) {
+			const {ctx, draw} = await this.render({}, true)
+			ctx.save()
+			await cb(ctx, draw)
+			ctx.restore()
+			return Promise.resolve(true);
+		},
+		async single(args = {}) {
+			await this.render(args, true)
+			return Promise.resolve(true);
+		},
+		canvasDraw(flag = false) {
+			const {ctx} = this
+			return new Promise(resolve => {
+				ctx.draw(flag, () => {
+					resolve(true);
+				});
+			});
+		},
+		async getContext() {
+			if(this.ctx && this.inited) {
+				return Promise.resolve(this.ctx)
+			};
+			const { type, use2dCanvas, dpr, boardWidth, boardHeight } = this;
+			await new Promise(resolve => this.$nextTick(resolve)) 
+			const _getContext = () => {
+				console.log(11111)
+				return new Promise(resolve => {
+				uni.createSelectorQuery()
+					.in(this)
+					.select('#' + this.canvasId)
+					.boundingClientRect()
+					.exec(res => {
+						if(res) {
+							const ctx = uni.createCanvasContext(this.canvasId, this);
+							if (!this.inited) {
+								this.inited = true;
+								this.use2dCanvas = false;
+								this.canvas = res
+							}
+							// #ifdef MP-ALIPAY
+							ctx.scale(dpr, dpr);
+							// #endif
+							this.ctx = expand(ctx)
+						}
+					})
+				})
+			}
+			// #ifndef MP-WEIXIN 
+			return _getContext()
+			// #endif
+			
+			if(!use2dCanvas) {
+				return _getContext()
+			}
+			return new Promise(resolve => {
+				uni.createSelectorQuery()
+					.in(this)
+					.select('#l-painter')
+					.node()
+					.exec(res => {
+						const canvas = res[0].node;
+						if(!canvas) {
+							this.use2dCanvas = false;
+							return this.getContext()
+						}
+						const ctx = canvas.getContext(type);
+						if (!this.inited) {
+							this.inited = true;
+							canvas.width = boardWidth * dpr;
+							canvas.height = boardHeight * dpr;
+							this.use2dCanvas = true;
+							this.canvas = canvas
+							ctx.scale(dpr, dpr);
+						}
+						this.ctx = adaptor(ctx)
+						resolve(this.ctx);
+					});
+				
+			});
+		},
+		canvasToTempFilePath(args = {}) {
+		  const {use2dCanvas, canvasId} = this
+		  return new Promise((resolve, reject) => {
+		    let { top: y = 0, left: x = 0, width, height } = this.boundary || this
+			let destWidth = width * this.dpr
+			let destHeight = height * this.dpr
+			// #ifdef MP-ALIPAY
+			width = width * this.dpr
+			height = height * this.dpr
+			// #endif
+		    const copyArgs = {
+		      x,
+		      y,
+		      width,
+		      height,
+		      destWidth,
+		      destHeight,
+		      canvasId,
+		      fileType: args.fileType || this.fileType,
+		      quality: /\d/.test(args.quality) ? args.quality : this.quality,
+		      success: resolve,
+		      fail: reject
+		    }
+		    if (use2dCanvas) {
+		      delete copyArgs.canvasId
+		      copyArgs.canvas = this.canvas
+		    }
+			console.log(this)
+		    uni.canvasToTempFilePath(copyArgs, this)
+		  })
+		}
+	}
+};
+</script>
+
+<style></style>

+ 376 - 0
xiaochengxu/components/lime-painter/layout.js

@@ -0,0 +1,376 @@
+import { toPx, isNumber, getImageInfo  } from './utils'
+let uuid = 0;
+export class Layout {
+	constructor(context, root, isH5PathToBase64) {
+		this.ctx = context
+		this.root = root
+		this.isH5PathToBase64 = isH5PathToBase64
+	}
+	init(context, root, isH5PathToBase64) {
+		this.ctx = context
+		this.root = root
+		this.isH5PathToBase64 = isH5PathToBase64
+	}
+	async getNodeTree(element, parent = {}, index = 0, siblings = [], source) {
+		let computedStyle = Object.assign({}, this.getComputedStyle(element, parent, index));
+		let attributes = await this.getAttributes(element)
+		let node = {
+			id: uuid++,
+			parent,
+			computedStyle,
+			rules: element.rules,
+			attributes: Object.assign({}, attributes),
+			name: element?.type || 'view',
+			use:element?.use || 'view',
+		}
+		if(JSON.stringify(parent) === '{}' && !element.type) {
+			const {left = 0, top = 0, width = 0, height = 0 } = computedStyle
+			node.layoutBox = {left, top, width, height }
+		} else {
+			node.layoutBox = Object.assign({left: 0, top: 0}, this.getLayoutBox(node, parent, index, siblings, source))
+		}
+		if (element?.views) {
+			let childrens = []
+			node.children = []
+			for (let i = 0; i < element.views.length; i++) {
+				console.log(childrens,'childrens')
+				let v = element.views[i]
+				childrens.push(await this.getNodeTree(v, node, i, childrens, element))
+			}
+			 node.children = childrens
+		}
+		return node
+	}
+	getComputedStyle(element, parent = {}, index = 0) {
+		const style = {}
+		const name = element.name || element.type
+		const node = JSON.stringify(parent) == '{}' && !name ? element :  element.css;
+		
+		if(!node) return style
+		const inheritProps = ['color', 'fontSize', 'lineHeight', 'verticalAlign', 'fontWeight', 'textAlign']
+		if(parent.computedStyle) {
+			inheritProps.forEach(prop => {
+				if(node[prop] || parent.computedStyle[prop]) {
+					node[prop] = node[prop] || parent.computedStyle[prop]
+				}
+			})
+		}
+		for (let value of Object.keys(node)) {
+			const item = node[value]
+			if(value == 'views') {continue}
+			if (/^(box)?shadow$/i.test(value)) {
+				let shadows = item.split(' ').map(v => /^\d/.test(v) ? toPx(v) : v)
+				style.boxShadow = shadows
+				continue
+			}
+			if (/^border(?!radius)/i.test(value)) {
+				const prefix = value.match(/^border([BTRLa-z]+)?/)[0]
+				const type = value.match(/[W|S|C][a-z]+/)
+				let v = item.split(' ').map(v => /^\d/.test(v) ? toPx(v) : v)
+				
+				if(v.length > 1) {
+					style[prefix] = {
+						[prefix + 'Width'] : v[0] || 1,
+						[prefix + 'Style'] : v[1] || 'solid',
+						[prefix + 'Color'] : v[2] || 'black'
+					}
+				} else {
+					style[prefix] = {
+						[prefix + 'Width'] :  1,
+						[prefix + 'Style'] : 'solid',
+						[prefix + 'Color'] : 'black'
+					}
+					style[prefix][prefix + type[0]] = v[0]
+				}
+				continue
+			}
+			if (/^background(Color)?$/i.test(value)) {
+				style['backgroundColor'] = item
+				continue
+			}
+			if(/padding|margin|radius/i.test(value)) {
+				let isRadius = value.includes('adius')
+				let prefix = isRadius ? 'borderRadius' : value.match(/[a-z]+/)[0]
+				let pre = [0,0,0,0].map((item, i) => isRadius ? ['borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius'][i] : [prefix + 'Top', prefix + 'Right', prefix + 'Bottom', prefix + 'Left'][i] )
+				if(value === 'padding' || value === 'margin' || value === 'radius' || value === 'borderRadius') {
+					let v = item?.split(' ').map((item) => /^\d/.test(item) && toPx(item, node['width']), []) ||[0];
+					let type = isRadius ? 'borderRadius' : value;
+					if(v.length == 1) {
+						style[type] = v[0]
+					} else {
+						let [t, r, b, l] = v
+						style[type] = {
+							[pre[0]]: t,
+							[pre[1]]: isNumber(r) ? r : t,
+							[pre[2]]: isNumber(b) ? b : t,
+							[pre[3]]: isNumber(l) ? l : r
+						}
+					}
+				} else {
+					if(typeof style[prefix] === 'object') {
+						style[prefix][value] = toPx(item, node['width'])
+					} else {
+						style[prefix] = {
+							[pre[0]]: style[prefix] || 0,
+							[pre[1]]: style[prefix] || 0,
+							[pre[2]]: style[prefix] || 0,
+							[pre[3]]: style[prefix] || 0
+						}
+						style[prefix][value] = toPx(item, node['width'])
+					}
+				}
+				continue
+			}
+			 if(/^(width|height)$/i.test(value)) {
+				if(/%$/.test(item)) {
+					style[value] = toPx(item, parent.layoutBox[value])
+				} else {
+					style[value] = /px|rpx$/.test(item) ? toPx(item) : item
+				}
+				continue
+			}
+			if(/^transform$/i.test(value)) {
+				if(!item){ item = 'translate(0%,0)' }
+				style[value]= {}
+				item.replace(/([a-zA-Z]+)\(([0-9,-\.%rpxdeg\s]+)\)/g, (g1, g2, g3) => {
+					const v = g3.split(',').map(k => k.replace(/(^\s*)|(\s*$)/g,''))
+					const transform = (v, r) => {
+						return v.includes('deg') ? v * 1 : r && !/%$/.test(r) ? toPx(v, r) : v
+					}
+					if(g2.includes('matrix')) {
+						style[value][g2] = v.map(v => v * 1)
+					} else if(g2.includes('rotate')) {
+						style[value][g2] = g3.match(/\d+/)[0] * 1
+					}else if(/[X, Y]/.test(g2)) {
+						style[value][g2] = /[X]/.test(g2) ? transform(v[0], node['width']) : transform(v[0], node['height'])
+					} else {
+						style[value][g2+'X'] = transform(v[0], node['width'] )
+						style[value][g2+'Y'] = transform(v[1] || v[0], node['height'])
+					}
+				})
+				continue
+			}
+			if(/%/.test(item)) {
+				const {width: pw, height: ph, left: pl, top: pt} = parent.layoutBox;
+				const {width: rw, height: rh} = this.root;
+				const isR = style.position == 'relative'
+				
+				if(value == 'width') {
+					style[value] = toPx(item, pw || rw)
+				}else if(value == 'height') {
+					style[value] = toPx(item, ph || rh)
+				}else if(value == 'left') {
+					style[value] = item // isR ? toPx(item, pw) + pl : toPx(item, rw)
+				}else if(value == 'top') {
+					style[value] = item // isR ? toPx(item, ph) + pt : toPx(item, rh)
+				} else {
+					style[value] = toPx(item, node['width'])
+				}
+			} else {
+				style[value] = /px|rpx$/.test(item) ? toPx(item) : /em$/.test(item) && name == 'text'? toPx(item, node['fontSize']) : item
+			}
+		}
+		if(/image/.test(element.name||element.type ) && !style.mode) {
+			style.mode = element.mode || 'scaleToFill' 
+			if((!node.width || node.width == 'auto') && (!node.height || node.width == 'auto') ) {
+				style.mode = ''
+			} 
+		}
+		return style
+	}
+	getLayoutBox(element, parent = {}, index = 0, siblings = [], source = {}) {
+		
+		let box = {}
+		let {name, computedStyle: cstyle, layoutBox, attributes} = element || {}
+		if(!name) return box
+		const {ctx} = this
+		const pbox = parent.layoutBox || this.root
+		const pstyle = parent.computedStyle
+		let { 
+			verticalAlign: v, 
+			left: x, 
+			top: y,
+			width: w,
+			height: h,
+			fontSize = 14,
+			lineHeight = '1.4em',
+			maxLines,
+			fontWeight,
+			fontFamily,
+			textStyle,
+			position,
+			display
+			}  = cstyle;
+		const p = cstyle.padding
+		const m = cstyle.margin
+		const { paddingTop: pt = 0, paddingRight: pr = 0, paddingBottom: pb = 0, paddingLeft: pl = 0, } = cstyle.padding || {p,p,p,p}
+		const { marginTop: mt = 0, marginRight: mr = 0, marginBottom: mb = 0, marginLeft: ml = 0, } = cstyle.margin || {m,m,m,m}
+		const {layoutBox: lbox, computedStyle: ls, name: lname} = siblings[index - 1] || {}
+		const {layoutBox: rbox, computedStyle: rs, name: rname} = siblings[index + 1] || {}
+		
+		const lmb = ls?.margin?.marginBottom || 0
+		const lmr = ls?.margin?.marginRight || 0
+		
+		if(/%$/.test(x)) {
+			x = toPx(x, pbox.width)
+		}
+		if(/%$/.test(y)) {
+			y = toPx(y, pbox.height)
+		}
+		if(position == 'relative') {
+			x += pbox.left
+			y += pbox.top
+		}
+		if(name === 'text') {
+			const text = attributes.text ||''
+			lineHeight = toPx(lineHeight, fontSize)
+			ctx.save()
+			ctx.setFonts({fontFamily, fontSize, fontWeight, textStyle})
+			const isLeft = index == 0 
+			const islineBlock = display === 'inlineBlock'
+			const isblock = display === 'block' || ls?.display === 'block' 
+			const isOnly = isLeft && !rbox || !parent?.id
+			const lboxR = isLeft || isblock ? 0 : lbox.offsetRight || 0
+			let texts = text.split('\n')
+			let lineIndex = 1
+			let line = ''
+			const textIndent = cstyle.textIndent || 0
+			if(!isOnly && !islineBlock) {
+				texts.map((t, i) => {
+					lineIndex += i
+					const chars = t.split('')
+					for (let j = 0; j < chars.length; j++) {
+						let ch = chars[j]
+						let textline = line + ch
+						let textWidth = ctx.measureText(textline, fontSize).width
+						if(lineIndex == 1) {
+							textWidth = textWidth + (isblock ? 0 : lboxR) +  textIndent
+						}
+						if(textWidth > pbox.width) {
+							lineIndex++
+							line = ch
+						} else {
+							line = textline
+						}
+					}
+				})
+			} else {
+				line = text
+				lineIndex = Math.max(texts.length, Math.ceil(ctx.measureText(text, fontSize).width / ((w || pbox.width) - ctx.measureText('!', fontSize).width / 2)))
+			}
+			if(!islineBlock) {
+				box.offsetLeft =  (isNumber(x) || isblock || isOnly ? textIndent : lboxR) +  pl + ml;
+			}
+			
+			// 剩下的字宽度
+			const remain = ctx.measureText(line, fontSize).width
+			let width =  lineIndex > 1 ? pbox.width : remain + (box?.offsetLeft || 0);
+			if(!islineBlock) {
+				box.offsetRight = (x || 0) + box.offsetLeft + (w ? w : (isblock ? pbox.width : remain)) + pr + mr;
+			}
+			const lboxOffset = lbox ? lbox.left + lbox.width : 0;
+			const _getLeft = () => {
+				if(islineBlock) {
+					return (lboxOffset +  width > pbox.width || isLeft ? pbox.left : lboxOffset + lmr ) + ml
+				}
+				return (x || pbox.left)
+			}
+			const _getWidth = () => {
+				if(islineBlock) {
+					return width + pl + pr 
+				}
+				return w || (!isOnly || isblock ? pbox.width : (width > pbox.width - box.left || lineIndex > 1 ?  pbox.width - box.left : width))
+			}
+			const _getHeight = () => {
+				if(h) {
+					return h
+				} else if(lineIndex > 1 ) {
+					return (maxLines || lineIndex) * lineHeight + pt + pb 
+				} else {
+					return lineHeight + pt + pb 
+				}
+			}
+			const _getTop = () => {
+				let _y = y
+				if(_y) {
+					// return _y + pt + mt
+				} else if(isLeft) {
+					_y = pbox.top
+				} else if((lineIndex == 1 && width < pbox.width && lname === 'text' && !isblock && !islineBlock) || lbox.width < pbox.width && !(islineBlock && (lboxOffset +  width > pbox.width))) {
+					_y = lbox.top
+				} else {
+					_y = lbox.top + lbox.height - (ls?.lineHeight || 0)
+				}
+				if (v === 'bottom') {
+					_y = pbox.top + (pbox.height - box.height || 0)
+				}
+				if (v === 'middle') {
+					_y = pbox.top + (pbox.height ? (pbox.height - box.height || 0) / 2 : 0)
+				}
+				return _y + mt + (isblock && ls?.lineHeight || 0 ) + (lboxOffset +  width > pbox.width ? lmb : 0)
+			}
+			box.left = _getLeft() 
+			box.width = _getWidth() 
+			box.height = _getHeight()
+			box.top = _getTop()
+			if(pstyle && !pstyle.height) {
+				pbox.height = box.top - pbox.top + box.height
+			}
+			ctx.restore()
+		} else if(['view', 'qrcode'].includes(name)) {
+			box.left = ( x || pbox.left) + ml - mr
+			box.width = (w || pbox?.width) - pl - pr
+			box.height = (h || 0 ) 
+			if(isNumber(y)) {
+				box.top = y + mt
+			} else {
+				box.top = (lbox && (lbox.top + lbox.height) || pbox.top) + mt + lmb
+			}
+			
+		} else if(name === 'image') {
+			const {
+				width: rWidth,
+				height: rHeight
+			} = attributes
+			const limageOffset = lbox && (lbox.left + lbox.width)
+			if(isNumber(x)) {
+				box.left = x + ml - mr
+			} else {
+				box.left = (lbox && (limageOffset < pbox.width ? limageOffset : pbox.left) || pbox.left) + ml - mr
+			}
+			if(isNumber(w)) {
+				box.width = w // - pl - pr 
+			} else {
+				box.width = Math.round(isNumber(h) ? rWidth * h / rHeight : pbox?.width) // - pl - pr
+			}
+			if(isNumber(h)) {
+				box.height = h
+			} else {
+				const cH = Math.round(box.width * rHeight / rWidth )
+				box.height = Math.min(cH, pbox?.height)
+			}
+			if(isNumber(y)) {
+				box.top = y + mt
+			} else {
+				box.top = (lbox && (limageOffset < pbox.width ? limageOffset : (lbox.top + lbox.height)) || pbox.top) + mt + lmb
+			}
+		}
+		return box
+	}
+	async getAttributes(element) {
+		let arr = { }
+		if(element?.url || element?.src) {
+			arr.src = element.url || element?.src;
+			const {width = 0, height = 0, path: src, url} = await getImageInfo(arr.src, this.isH5PathToBase64) || {}
+			arr = Object.assign({}, arr, {width, height, src, url})
+		}
+		if(element?.text) {
+			arr.text = element.text
+		}
+		return arr
+	}
+	async calcNode(element) {
+		const node = element || this.element
+		return await this.getNodeTree(node)
+	}
+}

+ 791 - 0
xiaochengxu/components/lime-painter/qrcode.js

@@ -0,0 +1,791 @@
+/* eslint-disable */
+!(function () {
+
+  // alignment pattern
+  let adelta = [
+    0, 11, 15, 19, 23, 27, 31,
+    16, 18, 20, 22, 24, 26, 28, 20, 22, 24, 24, 26, 28, 28, 22, 24, 24,
+    26, 26, 28, 28, 24, 24, 26, 26, 26, 28, 28, 24, 26, 26, 26, 28, 28
+  ];
+
+  // version block
+  let vpat = [
+    0xc94, 0x5bc, 0xa99, 0x4d3, 0xbf6, 0x762, 0x847, 0x60d,
+    0x928, 0xb78, 0x45d, 0xa17, 0x532, 0x9a6, 0x683, 0x8c9,
+    0x7ec, 0xec4, 0x1e1, 0xfab, 0x08e, 0xc1a, 0x33f, 0xd75,
+    0x250, 0x9d5, 0x6f0, 0x8ba, 0x79f, 0xb0b, 0x42e, 0xa64,
+    0x541, 0xc69
+  ];
+
+  // final format bits with mask: level << 3 | mask
+  let fmtword = [
+    0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976,    //L
+    0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0,    //M
+    0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed,    //Q
+    0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b    //H
+  ];
+
+  // 4 per version: number of blocks 1,2; data width; ecc width
+  let eccblocks = [
+    1, 0, 19, 7, 1, 0, 16, 10, 1, 0, 13, 13, 1, 0, 9, 17,
+    1, 0, 34, 10, 1, 0, 28, 16, 1, 0, 22, 22, 1, 0, 16, 28,
+    1, 0, 55, 15, 1, 0, 44, 26, 2, 0, 17, 18, 2, 0, 13, 22,
+    1, 0, 80, 20, 2, 0, 32, 18, 2, 0, 24, 26, 4, 0, 9, 16,
+    1, 0, 108, 26, 2, 0, 43, 24, 2, 2, 15, 18, 2, 2, 11, 22,
+    2, 0, 68, 18, 4, 0, 27, 16, 4, 0, 19, 24, 4, 0, 15, 28,
+    2, 0, 78, 20, 4, 0, 31, 18, 2, 4, 14, 18, 4, 1, 13, 26,
+    2, 0, 97, 24, 2, 2, 38, 22, 4, 2, 18, 22, 4, 2, 14, 26,
+    2, 0, 116, 30, 3, 2, 36, 22, 4, 4, 16, 20, 4, 4, 12, 24,
+    2, 2, 68, 18, 4, 1, 43, 26, 6, 2, 19, 24, 6, 2, 15, 28,
+    4, 0, 81, 20, 1, 4, 50, 30, 4, 4, 22, 28, 3, 8, 12, 24,
+    2, 2, 92, 24, 6, 2, 36, 22, 4, 6, 20, 26, 7, 4, 14, 28,
+    4, 0, 107, 26, 8, 1, 37, 22, 8, 4, 20, 24, 12, 4, 11, 22,
+    3, 1, 115, 30, 4, 5, 40, 24, 11, 5, 16, 20, 11, 5, 12, 24,
+    5, 1, 87, 22, 5, 5, 41, 24, 5, 7, 24, 30, 11, 7, 12, 24,
+    5, 1, 98, 24, 7, 3, 45, 28, 15, 2, 19, 24, 3, 13, 15, 30,
+    1, 5, 107, 28, 10, 1, 46, 28, 1, 15, 22, 28, 2, 17, 14, 28,
+    5, 1, 120, 30, 9, 4, 43, 26, 17, 1, 22, 28, 2, 19, 14, 28,
+    3, 4, 113, 28, 3, 11, 44, 26, 17, 4, 21, 26, 9, 16, 13, 26,
+    3, 5, 107, 28, 3, 13, 41, 26, 15, 5, 24, 30, 15, 10, 15, 28,
+    4, 4, 116, 28, 17, 0, 42, 26, 17, 6, 22, 28, 19, 6, 16, 30,
+    2, 7, 111, 28, 17, 0, 46, 28, 7, 16, 24, 30, 34, 0, 13, 24,
+    4, 5, 121, 30, 4, 14, 47, 28, 11, 14, 24, 30, 16, 14, 15, 30,
+    6, 4, 117, 30, 6, 14, 45, 28, 11, 16, 24, 30, 30, 2, 16, 30,
+    8, 4, 106, 26, 8, 13, 47, 28, 7, 22, 24, 30, 22, 13, 15, 30,
+    10, 2, 114, 28, 19, 4, 46, 28, 28, 6, 22, 28, 33, 4, 16, 30,
+    8, 4, 122, 30, 22, 3, 45, 28, 8, 26, 23, 30, 12, 28, 15, 30,
+    3, 10, 117, 30, 3, 23, 45, 28, 4, 31, 24, 30, 11, 31, 15, 30,
+    7, 7, 116, 30, 21, 7, 45, 28, 1, 37, 23, 30, 19, 26, 15, 30,
+    5, 10, 115, 30, 19, 10, 47, 28, 15, 25, 24, 30, 23, 25, 15, 30,
+    13, 3, 115, 30, 2, 29, 46, 28, 42, 1, 24, 30, 23, 28, 15, 30,
+    17, 0, 115, 30, 10, 23, 46, 28, 10, 35, 24, 30, 19, 35, 15, 30,
+    17, 1, 115, 30, 14, 21, 46, 28, 29, 19, 24, 30, 11, 46, 15, 30,
+    13, 6, 115, 30, 14, 23, 46, 28, 44, 7, 24, 30, 59, 1, 16, 30,
+    12, 7, 121, 30, 12, 26, 47, 28, 39, 14, 24, 30, 22, 41, 15, 30,
+    6, 14, 121, 30, 6, 34, 47, 28, 46, 10, 24, 30, 2, 64, 15, 30,
+    17, 4, 122, 30, 29, 14, 46, 28, 49, 10, 24, 30, 24, 46, 15, 30,
+    4, 18, 122, 30, 13, 32, 46, 28, 48, 14, 24, 30, 42, 32, 15, 30,
+    20, 4, 117, 30, 40, 7, 47, 28, 43, 22, 24, 30, 10, 67, 15, 30,
+    19, 6, 118, 30, 18, 31, 47, 28, 34, 34, 24, 30, 20, 61, 15, 30
+  ];
+
+  // Galois field log table
+  let glog = [
+    0xff, 0x00, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6, 0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
+    0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81, 0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
+    0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21, 0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
+    0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9, 0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
+    0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd, 0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
+    0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd, 0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
+    0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e, 0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
+    0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b, 0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
+    0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d, 0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
+    0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c, 0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
+    0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd, 0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
+    0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e, 0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
+    0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76, 0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
+    0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa, 0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
+    0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51, 0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
+    0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8, 0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf
+  ];
+
+  // Galios field exponent table
+  let gexp = [
+    0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
+    0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9, 0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
+    0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35, 0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
+    0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0, 0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
+    0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc, 0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
+    0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f, 0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
+    0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88, 0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
+    0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93, 0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
+    0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9, 0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
+    0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa, 0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
+    0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e, 0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
+    0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4, 0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
+    0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e, 0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
+    0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef, 0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
+    0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5, 0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
+    0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83, 0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x00
+  ];
+
+  // Working buffers:
+  // data input and ecc append, image working buffer, fixed part of image, run lengths for badness
+  let strinbuf = [], eccbuf = [], qrframe = [], framask = [], rlens = [];
+  // Control values - width is based on version, last 4 are from table.
+  let version, width, neccblk1, neccblk2, datablkw, eccblkwid;
+  let ecclevel = 2;
+  // set bit to indicate cell in qrframe is immutable.  symmetric around diagonal
+  function setmask(x, y) {
+    let bt;
+    if (x > y) {
+      bt = x;
+      x = y;
+      y = bt;
+    }
+    // y*y = 1+3+5...
+    bt = y;
+    bt *= y;
+    bt += y;
+    bt >>= 1;
+    bt += x;
+    framask[bt] = 1;
+  }
+
+  // enter alignment pattern - black to qrframe, white to mask (later black frame merged to mask)
+  function putalign(x, y) {
+    let j;
+
+    qrframe[x + width * y] = 1;
+    for (j = -2; j < 2; j++) {
+      qrframe[(x + j) + width * (y - 2)] = 1;
+      qrframe[(x - 2) + width * (y + j + 1)] = 1;
+      qrframe[(x + 2) + width * (y + j)] = 1;
+      qrframe[(x + j + 1) + width * (y + 2)] = 1;
+    }
+    for (j = 0; j < 2; j++) {
+      setmask(x - 1, y + j);
+      setmask(x + 1, y - j);
+      setmask(x - j, y - 1);
+      setmask(x + j, y + 1);
+    }
+  }
+
+  //========================================================================
+  // Reed Solomon error correction
+  // exponentiation mod N
+  function modnn(x) {
+    while (x >= 255) {
+      x -= 255;
+      x = (x >> 8) + (x & 255);
+    }
+    return x;
+  }
+
+  let genpoly = [];
+
+  // Calculate and append ECC data to data block.  Block is in strinbuf, indexes to buffers given.
+  function appendrs(data, dlen, ecbuf, eclen) {
+    let i, j, fb;
+
+    for (i = 0; i < eclen; i++)
+      strinbuf[ecbuf + i] = 0;
+    for (i = 0; i < dlen; i++) {
+      fb = glog[strinbuf[data + i] ^ strinbuf[ecbuf]];
+      if (fb != 255)     /* fb term is non-zero */
+        for (j = 1; j < eclen; j++)
+          strinbuf[ecbuf + j - 1] = strinbuf[ecbuf + j] ^ gexp[modnn(fb + genpoly[eclen - j])];
+      else
+        for (j = ecbuf; j < ecbuf + eclen; j++)
+          strinbuf[j] = strinbuf[j + 1];
+      strinbuf[ecbuf + eclen - 1] = fb == 255 ? 0 : gexp[modnn(fb + genpoly[0])];
+    }
+  }
+
+  //========================================================================
+  // Frame data insert following the path rules
+
+  // check mask - since symmetrical use half.
+  function ismasked(x, y) {
+    let bt;
+    if (x > y) {
+      bt = x;
+      x = y;
+      y = bt;
+    }
+    bt = y;
+    bt += y * y;
+    bt >>= 1;
+    bt += x;
+    return framask[bt];
+  }
+
+  //========================================================================
+  //  Apply the selected mask out of the 8.
+  function applymask(m) {
+    let x, y, r3x, r3y;
+
+    switch (m) {
+      case 0:
+        for (y = 0; y < width; y++)
+          for (x = 0; x < width; x++)
+            if (!((x + y) & 1) && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+        break;
+      case 1:
+        for (y = 0; y < width; y++)
+          for (x = 0; x < width; x++)
+            if (!(y & 1) && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+        break;
+      case 2:
+        for (y = 0; y < width; y++)
+          for (r3x = 0, x = 0; x < width; x++ , r3x++) {
+            if (r3x == 3)
+              r3x = 0;
+            if (!r3x && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+          }
+        break;
+      case 3:
+        for (r3y = 0, y = 0; y < width; y++ , r3y++) {
+          if (r3y == 3)
+            r3y = 0;
+          for (r3x = r3y, x = 0; x < width; x++ , r3x++) {
+            if (r3x == 3)
+              r3x = 0;
+            if (!r3x && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+          }
+        }
+        break;
+      case 4:
+        for (y = 0; y < width; y++)
+          for (r3x = 0, r3y = ((y >> 1) & 1), x = 0; x < width; x++ , r3x++) {
+            if (r3x == 3) {
+              r3x = 0;
+              r3y = !r3y;
+            }
+            if (!r3y && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+          }
+        break;
+      case 5:
+        for (r3y = 0, y = 0; y < width; y++ , r3y++) {
+          if (r3y == 3)
+            r3y = 0;
+          for (r3x = 0, x = 0; x < width; x++ , r3x++) {
+            if (r3x == 3)
+              r3x = 0;
+            if (!((x & y & 1) + !(!r3x | !r3y)) && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+          }
+        }
+        break;
+      case 6:
+        for (r3y = 0, y = 0; y < width; y++ , r3y++) {
+          if (r3y == 3)
+            r3y = 0;
+          for (r3x = 0, x = 0; x < width; x++ , r3x++) {
+            if (r3x == 3)
+              r3x = 0;
+            if (!(((x & y & 1) + (r3x && (r3x == r3y))) & 1) && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+          }
+        }
+        break;
+      case 7:
+        for (r3y = 0, y = 0; y < width; y++ , r3y++) {
+          if (r3y == 3)
+            r3y = 0;
+          for (r3x = 0, x = 0; x < width; x++ , r3x++) {
+            if (r3x == 3)
+              r3x = 0;
+            if (!(((r3x && (r3x == r3y)) + ((x + y) & 1)) & 1) && !ismasked(x, y))
+              qrframe[x + y * width] ^= 1;
+          }
+        }
+        break;
+    }
+    return;
+  }
+
+  // Badness coefficients.
+  let N1 = 3, N2 = 3, N3 = 40, N4 = 10;
+
+  // Using the table of the length of each run, calculate the amount of bad image 
+  // - long runs or those that look like finders; called twice, once each for X and Y
+  function badruns(length) {
+    let i;
+    let runsbad = 0;
+    for (i = 0; i <= length; i++)
+      if (rlens[i] >= 5)
+        runsbad += N1 + rlens[i] - 5;
+    // BwBBBwB as in finder
+    for (i = 3; i < length - 1; i += 2)
+      if (rlens[i - 2] == rlens[i + 2]
+        && rlens[i + 2] == rlens[i - 1]
+        && rlens[i - 1] == rlens[i + 1]
+        && rlens[i - 1] * 3 == rlens[i]
+        // white around the black pattern? Not part of spec
+        && (rlens[i - 3] == 0 // beginning
+          || i + 3 > length  // end
+          || rlens[i - 3] * 3 >= rlens[i] * 4 || rlens[i + 3] * 3 >= rlens[i] * 4)
+      )
+        runsbad += N3;
+    return runsbad;
+  }
+
+  // Calculate how bad the masked image is - blocks, imbalance, runs, or finders.
+  function badcheck() {
+    let x, y, h, b, b1;
+    let thisbad = 0;
+    let bw = 0;
+
+    // blocks of same color.
+    for (y = 0; y < width - 1; y++)
+      for (x = 0; x < width - 1; x++)
+        if ((qrframe[x + width * y] && qrframe[(x + 1) + width * y]
+          && qrframe[x + width * (y + 1)] && qrframe[(x + 1) + width * (y + 1)]) // all black
+          || !(qrframe[x + width * y] || qrframe[(x + 1) + width * y]
+            || qrframe[x + width * (y + 1)] || qrframe[(x + 1) + width * (y + 1)])) // all white
+          thisbad += N2;
+
+    // X runs
+    for (y = 0; y < width; y++) {
+      rlens[0] = 0;
+      for (h = b = x = 0; x < width; x++) {
+        if ((b1 = qrframe[x + width * y]) == b)
+          rlens[h]++;
+        else
+          rlens[++h] = 1;
+        b = b1;
+        bw += b ? 1 : -1;
+      }
+      thisbad += badruns(h);
+    }
+
+    // black/white imbalance
+    if (bw < 0)
+      bw = -bw;
+
+    let big = bw;
+    let count = 0;
+    big += big << 2;
+    big <<= 1;
+    while (big > width * width)
+      big -= width * width, count++;
+    thisbad += count * N4;
+
+    // Y runs
+    for (x = 0; x < width; x++) {
+      rlens[0] = 0;
+      for (h = b = y = 0; y < width; y++) {
+        if ((b1 = qrframe[x + width * y]) == b)
+          rlens[h]++;
+        else
+          rlens[++h] = 1;
+        b = b1;
+      }
+      thisbad += badruns(h);
+    }
+    return thisbad;
+  }
+
+  function genframe(instring) {
+    let x, y, k, t, v, i, j, m;
+
+    // find the smallest version that fits the string
+    t = instring.length;
+    version = 0;
+    do {
+      version++;
+      k = (ecclevel - 1) * 4 + (version - 1) * 16;
+      neccblk1 = eccblocks[k++];
+      neccblk2 = eccblocks[k++];
+      datablkw = eccblocks[k++];
+      eccblkwid = eccblocks[k];
+      k = datablkw * (neccblk1 + neccblk2) + neccblk2 - 3 + (version <= 9);
+      if (t <= k)
+        break;
+    } while (version < 40);
+
+    // FIXME - insure that it fits insted of being truncated
+    width = 17 + 4 * version;
+
+    // allocate, clear and setup data structures
+    v = datablkw + (datablkw + eccblkwid) * (neccblk1 + neccblk2) + neccblk2;
+    for (t = 0; t < v; t++)
+      eccbuf[t] = 0;
+    strinbuf = instring.slice(0);
+
+    for (t = 0; t < width * width; t++)
+      qrframe[t] = 0;
+
+    for (t = 0; t < (width * (width + 1) + 1) / 2; t++)
+      framask[t] = 0;
+
+    // insert finders - black to frame, white to mask
+    for (t = 0; t < 3; t++) {
+      k = 0;
+      y = 0;
+      if (t == 1)
+        k = (width - 7);
+      if (t == 2)
+        y = (width - 7);
+      qrframe[(y + 3) + width * (k + 3)] = 1;
+      for (x = 0; x < 6; x++) {
+        qrframe[(y + x) + width * k] = 1;
+        qrframe[y + width * (k + x + 1)] = 1;
+        qrframe[(y + 6) + width * (k + x)] = 1;
+        qrframe[(y + x + 1) + width * (k + 6)] = 1;
+      }
+      for (x = 1; x < 5; x++) {
+        setmask(y + x, k + 1);
+        setmask(y + 1, k + x + 1);
+        setmask(y + 5, k + x);
+        setmask(y + x + 1, k + 5);
+      }
+      for (x = 2; x < 4; x++) {
+        qrframe[(y + x) + width * (k + 2)] = 1;
+        qrframe[(y + 2) + width * (k + x + 1)] = 1;
+        qrframe[(y + 4) + width * (k + x)] = 1;
+        qrframe[(y + x + 1) + width * (k + 4)] = 1;
+      }
+    }
+
+    // alignment blocks
+    if (version > 1) {
+      t = adelta[version];
+      y = width - 7;
+      for (; ;) {
+        x = width - 7;
+        while (x > t - 3) {
+          putalign(x, y);
+          if (x < t)
+            break;
+          x -= t;
+        }
+        if (y <= t + 9)
+          break;
+        y -= t;
+        putalign(6, y);
+        putalign(y, 6);
+      }
+    }
+
+    // single black
+    qrframe[8 + width * (width - 8)] = 1;
+
+    // timing gap - mask only
+    for (y = 0; y < 7; y++) {
+      setmask(7, y);
+      setmask(width - 8, y);
+      setmask(7, y + width - 7);
+    }
+    for (x = 0; x < 8; x++) {
+      setmask(x, 7);
+      setmask(x + width - 8, 7);
+      setmask(x, width - 8);
+    }
+
+    // reserve mask-format area
+    for (x = 0; x < 9; x++)
+      setmask(x, 8);
+    for (x = 0; x < 8; x++) {
+      setmask(x + width - 8, 8);
+      setmask(8, x);
+    }
+    for (y = 0; y < 7; y++)
+      setmask(8, y + width - 7);
+
+    // timing row/col
+    for (x = 0; x < width - 14; x++)
+      if (x & 1) {
+        setmask(8 + x, 6);
+        setmask(6, 8 + x);
+      }
+      else {
+        qrframe[(8 + x) + width * 6] = 1;
+        qrframe[6 + width * (8 + x)] = 1;
+      }
+
+    // version block
+    if (version > 6) {
+      t = vpat[version - 7];
+      k = 17;
+      for (x = 0; x < 6; x++)
+        for (y = 0; y < 3; y++ , k--)
+          if (1 & (k > 11 ? version >> (k - 12) : t >> k)) {
+            qrframe[(5 - x) + width * (2 - y + width - 11)] = 1;
+            qrframe[(2 - y + width - 11) + width * (5 - x)] = 1;
+          }
+          else {
+            setmask(5 - x, 2 - y + width - 11);
+            setmask(2 - y + width - 11, 5 - x);
+          }
+    }
+
+    // sync mask bits - only set above for white spaces, so add in black bits
+    for (y = 0; y < width; y++)
+      for (x = 0; x <= y; x++)
+        if (qrframe[x + width * y])
+          setmask(x, y);
+
+    // convert string to bitstream
+    // 8 bit data to QR-coded 8 bit data (numeric or alphanum, or kanji not supported)
+    v = strinbuf.length;
+
+    // string to array
+    for (i = 0; i < v; i++)
+      eccbuf[i] = strinbuf.charCodeAt(i);
+    strinbuf = eccbuf.slice(0);
+
+    // calculate max string length
+    x = datablkw * (neccblk1 + neccblk2) + neccblk2;
+    if (v >= x - 2) {
+      v = x - 2;
+      if (version > 9)
+        v--;
+    }
+
+    // shift and repack to insert length prefix
+    i = v;
+    if (version > 9) {
+      strinbuf[i + 2] = 0;
+      strinbuf[i + 3] = 0;
+      while (i--) {
+        t = strinbuf[i];
+        strinbuf[i + 3] |= 255 & (t << 4);
+        strinbuf[i + 2] = t >> 4;
+      }
+      strinbuf[2] |= 255 & (v << 4);
+      strinbuf[1] = v >> 4;
+      strinbuf[0] = 0x40 | (v >> 12);
+    }
+    else {
+      strinbuf[i + 1] = 0;
+      strinbuf[i + 2] = 0;
+      while (i--) {
+        t = strinbuf[i];
+        strinbuf[i + 2] |= 255 & (t << 4);
+        strinbuf[i + 1] = t >> 4;
+      }
+      strinbuf[1] |= 255 & (v << 4);
+      strinbuf[0] = 0x40 | (v >> 4);
+    }
+    // fill to end with pad pattern
+    i = v + 3 - (version < 10);
+    while (i < x) {
+      strinbuf[i++] = 0xec;
+      // buffer has room    if (i == x)      break;
+      strinbuf[i++] = 0x11;
+    }
+
+    // calculate and append ECC
+
+    // calculate generator polynomial
+    genpoly[0] = 1;
+    for (i = 0; i < eccblkwid; i++) {
+      genpoly[i + 1] = 1;
+      for (j = i; j > 0; j--)
+        genpoly[j] = genpoly[j]
+          ? genpoly[j - 1] ^ gexp[modnn(glog[genpoly[j]] + i)] : genpoly[j - 1];
+      genpoly[0] = gexp[modnn(glog[genpoly[0]] + i)];
+    }
+    for (i = 0; i <= eccblkwid; i++)
+      genpoly[i] = glog[genpoly[i]]; // use logs for genpoly[] to save calc step
+
+    // append ecc to data buffer
+    k = x;
+    y = 0;
+    for (i = 0; i < neccblk1; i++) {
+      appendrs(y, datablkw, k, eccblkwid);
+      y += datablkw;
+      k += eccblkwid;
+    }
+    for (i = 0; i < neccblk2; i++) {
+      appendrs(y, datablkw + 1, k, eccblkwid);
+      y += datablkw + 1;
+      k += eccblkwid;
+    }
+    // interleave blocks
+    y = 0;
+    for (i = 0; i < datablkw; i++) {
+      for (j = 0; j < neccblk1; j++)
+        eccbuf[y++] = strinbuf[i + j * datablkw];
+      for (j = 0; j < neccblk2; j++)
+        eccbuf[y++] = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))];
+    }
+    for (j = 0; j < neccblk2; j++)
+      eccbuf[y++] = strinbuf[(neccblk1 * datablkw) + i + (j * (datablkw + 1))];
+    for (i = 0; i < eccblkwid; i++)
+      for (j = 0; j < neccblk1 + neccblk2; j++)
+        eccbuf[y++] = strinbuf[x + i + j * eccblkwid];
+    strinbuf = eccbuf;
+
+    // pack bits into frame avoiding masked area.
+    x = y = width - 1;
+    k = v = 1;         // up, minus
+    /* inteleaved data and ecc codes */
+    m = (datablkw + eccblkwid) * (neccblk1 + neccblk2) + neccblk2;
+    for (i = 0; i < m; i++) {
+      t = strinbuf[i];
+      for (j = 0; j < 8; j++ , t <<= 1) {
+        if (0x80 & t)
+          qrframe[x + width * y] = 1;
+        do {        // find next fill position
+          if (v)
+            x--;
+          else {
+            x++;
+            if (k) {
+              if (y != 0)
+                y--;
+              else {
+                x -= 2;
+                k = !k;
+                if (x == 6) {
+                  x--;
+                  y = 9;
+                }
+              }
+            }
+            else {
+              if (y != width - 1)
+                y++;
+              else {
+                x -= 2;
+                k = !k;
+                if (x == 6) {
+                  x--;
+                  y -= 8;
+                }
+              }
+            }
+          }
+          v = !v;
+        } while (ismasked(x, y));
+      }
+    }
+
+    // save pre-mask copy of frame
+    strinbuf = qrframe.slice(0);
+    t = 0;           // best
+    y = 30000;         // demerit
+    // for instead of while since in original arduino code
+    // if an early mask was "good enough" it wouldn't try for a better one
+    // since they get more complex and take longer.
+    for (k = 0; k < 8; k++) {
+      applymask(k);      // returns black-white imbalance
+      x = badcheck();
+      if (x < y) { // current mask better than previous best?
+        y = x;
+        t = k;
+      }
+      if (t == 7)
+        break;       // don't increment i to a void redoing mask
+      qrframe = strinbuf.slice(0); // reset for next pass
+    }
+    if (t != k)         // redo best mask - none good enough, last wasn't t
+      applymask(t);
+
+    // add in final mask/ecclevel bytes
+    y = fmtword[t + ((ecclevel - 1) << 3)];
+    // low byte
+    for (k = 0; k < 8; k++ , y >>= 1)
+      if (y & 1) {
+        qrframe[(width - 1 - k) + width * 8] = 1;
+        if (k < 6)
+          qrframe[8 + width * k] = 1;
+        else
+          qrframe[8 + width * (k + 1)] = 1;
+      }
+    // high byte
+    for (k = 0; k < 7; k++ , y >>= 1)
+      if (y & 1) {
+        qrframe[8 + width * (width - 7 + k)] = 1;
+        if (k)
+          qrframe[(6 - k) + width * 8] = 1;
+        else
+          qrframe[7 + width * 8] = 1;
+      }
+    return qrframe;
+  }
+  let _canvas = null;
+  let api = {
+    get ecclevel() {
+      return ecclevel;
+    },
+    set ecclevel(val) {
+      ecclevel = val;
+    },
+    get size() {
+      return _size;
+    },
+    set size(val) {
+      _size = val
+    },
+    get canvas() {
+      return _canvas;
+    },
+    set canvas(el) {
+      _canvas = el;
+    },
+    getFrame(string) {
+      return genframe(string);
+    },
+    //这里的utf16to8(str)是对Text中的字符串进行转码,让其支持中文
+    utf16to8(str) {
+      let out, i, len, c;
+      out = "";
+      len = str.length;
+      for (i = 0; i < len; i++) {
+        c = str.charCodeAt(i);
+        if ((c >= 0x0001) && (c <= 0x007F)) {
+          out += str.charAt(i);
+        } else if (c > 0x07FF) {
+          out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
+          out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
+          out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+        } else {
+          out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
+          out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
+        }
+      }
+      return out;
+    },
+	draw(str, $this, box, style, ecc) {
+		const {ctx} = $this
+		let {
+			left: startX, 
+			top: startY, 
+			width: cavW, 
+			height: cavH
+		} = box
+		const {
+			borderRadius = 0,
+			backgroundColor: bg,
+			color = '#000000',
+			border
+		} = style
+		const { borderWidth: bw } = border || {}
+      let that = this;
+      ecclevel = ecc || ecclevel;
+      if (!ctx) {
+        console.warn('No canvas provided to draw QR code in!')
+        return;
+      }
+	  ctx.save()
+	  if(bw) {
+		 cavW = cavW - bw
+		 cavH = cavH - bw
+	  }
+      let size = Math.min(cavW, cavH);
+	  //增加中文显示
+      str = that.utf16to8(str);
+
+      let frame = that.getFrame(str);
+      let px = size / width;
+	  $this.setOpacity(style)
+	  $this.setTransform(box, style)
+	  startX = -cavW/2,
+	  startY = -cavH/2
+      if (bg) {
+		  $this.setBackground(bg, cavW, cavH)
+		  $this.roundRect(startX, startY, cavW, cavH, borderRadius, true, false)
+      }
+	  ctx.setFillStyle(color)
+      for (let i = 0; i < width; i++) {
+        for (let j = 0; j < width; j++) {
+          if (frame[j * width + i]) {
+            ctx.fillRect(startX + px * i, startY + px * j, px, px);
+          }
+        }
+      }
+	  ctx.restore()
+	  $this.setBorder(box, style)
+    }
+  }
+  module.exports = { api }
+})();

+ 437 - 0
xiaochengxu/components/lime-painter/utils.js

@@ -0,0 +1,437 @@
+const screen = uni.getSystemInfoSync().windowWidth / 750;
+// 缓存图片
+let cache = {}
+export function isNumber(value) {
+	return /^-?\d+(\.\d+)?$/.test(value);
+}
+export function toPx(value, baseSize) {
+	// 如果是数字
+	if (typeof value === 'number') {
+		return value
+	}
+	// 如果是字符串数字
+	if (isNumber(value)) {
+		return value * 1
+	}
+	// 如果有单位
+	if (typeof value === 'string') {
+		const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
+		const results = reg.exec(value);
+		if (!value || !results) {
+			return 0;
+		}
+		const unit = results[3];
+		value = parseFloat(value);
+		let res = 0;
+		if (unit === 'rpx') {
+			res = Math.floor(value * (screen || 0.5) * 1);
+		} else if (unit === 'px') {
+			res = Math.floor(value * 1);
+		} else if (unit === '%') {
+			res = Math.floor(value * toPx(baseSize) / 100);
+		} else if (unit === 'em') {
+			res = Math.ceil(value * toPx(baseSize || 14));
+		}
+		return res;
+	}
+}
+
+// 计算版本
+export function compareVersion(v1, v2) {
+  v1 = v1.split('.')
+  v2 = v2.split('.')
+  const len = Math.max(v1.length, v2.length)
+  while (v1.length < len) {
+    v1.push('0')
+  }
+  while (v2.length < len) {
+    v2.push('0')
+  }
+  for (let i = 0; i < len; i++) {
+    const num1 = parseInt(v1[i], 10)
+    const num2 = parseInt(v2[i], 10)
+
+    if (num1 > num2) {
+      return 1
+    } else if (num1 < num2) {
+      return -1
+    }
+  }
+  return 0
+}
+
+/** 从 0x20 开始到 0x80 的字符宽度数据 */
+export const CHAR_WIDTH_SCALE_MAP = [0.296, 0.313, 0.436, 0.638, 0.586, 0.89, 0.87, 0.256, 0.334, 0.334, 0.455, 0.742,
+	0.241, 0.433, 0.241, 0.427, 0.586, 0.586, 0.586, 0.586, 0.586, 0.586, 0.586, 0.586, 0.586, 0.586, 0.241, 0.241, 0.742,
+	0.742, 0.742, 0.483, 1.031, 0.704, 0.627, 0.669, 0.762, 0.55, 0.531, 0.744, 0.773, 0.294, 0.396, 0.635, 0.513, 0.977,
+	0.813, 0.815, 0.612, 0.815, 0.653, 0.577, 0.573, 0.747, 0.676, 1.018, 0.645, 0.604, 0.62, 0.334, 0.416, 0.334, 0.742,
+	0.448, 0.295, 0.553, 0.639, 0.501, 0.64, 0.567, 0.347, 0.64, 0.616, 0.266, 0.267, 0.544, 0.266, 0.937, 0.616, 0.636,
+	0.639, 0.64, 0.382, 0.463, 0.373, 0.616, 0.525, 0.79, 0.507, 0.529, 0.492, 0.334, 0.269, 0.334, 0.742, 0.296
+];
+// #ifdef MP
+const prefix = () => {
+	// #ifdef MP-TOUTIAO
+	return tt
+	// #endif
+	// #ifdef MP-WEIXIN
+	return wx
+	// #endif
+	// #ifdef MP-BAIDU
+	return swan
+	// #endif
+	// #ifdef MP-ALIPAY
+	return my
+	// #endif
+	// #ifdef MP-QQ
+	return qq
+	// #endif
+	// #ifdef MP-360
+	return qh
+	// #endif
+}
+
+const base64ToArrayBuffer = (data) => {
+	/**
+	 * base64ToArrayBuffer
+	 * Base64Binary.decode(base64_string);  
+	 * Base64Binary.decodeArrayBuffer(base64_string); 
+	 */
+	const Base64Binary = {
+	  _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
+	  
+	  /* will return a  Uint8Array type */
+	  decodeArrayBuffer(input) {
+	    const bytes = (input.length/4) * 3;
+	    const ab = new ArrayBuffer(bytes);
+	    this.decode(input, ab);
+	    return ab;
+	  },
+	 
+	  removePaddingChars(input) {
+	    const lkey = this._keyStr.indexOf(input.charAt(input.length - 1));
+	    if(lkey == 64){
+	      return input.substring(0,input.length - 1);
+	    }
+	    return input;
+	  },
+	 
+	  decode(input, arrayBuffer) {
+	    //get last chars to see if are valid
+	    input = this.removePaddingChars(input);
+	    input = this.removePaddingChars(input);
+	 
+	    const bytes = parseInt((input.length / 4) * 3, 10);
+	    
+	    let uarray;
+	    let chr1, chr2, chr3;
+	    let enc1, enc2, enc3, enc4;
+	    let i = 0;
+	    let j = 0;
+	    
+	    if (arrayBuffer)
+	      uarray = new Uint8Array(arrayBuffer);
+	    else
+	      uarray = new Uint8Array(bytes);
+	    
+	    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+	    
+	    for (i=0; i<bytes; i+=3) {  
+	      //get the 3 octects in 4 ascii chars
+	      enc1 = this._keyStr.indexOf(input.charAt(j++));
+	      enc2 = this._keyStr.indexOf(input.charAt(j++));
+	      enc3 = this._keyStr.indexOf(input.charAt(j++));
+	      enc4 = this._keyStr.indexOf(input.charAt(j++));
+	  
+	      chr1 = (enc1 << 2) | (enc2 >> 4);
+	      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+	      chr3 = ((enc3 & 3) << 6) | enc4;
+	  
+	      uarray[i] = chr1;      
+	      if (enc3 != 64) uarray[i+1] = chr2;
+	      if (enc4 != 64) uarray[i+2] = chr3;
+	    }
+	    return uarray;  
+	  }
+	 }
+	return (uni.base64ToArrayBuffer && uni.base64ToArrayBuffer(data)) || Base64Binary.decodeArrayBuffer(data)
+}
+// #endif
+
+/**
+ * base64转路径
+ * @param {Object} base64
+ */
+export function base64ToPath(base64) {
+	const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
+	
+	return new Promise((resolve, reject) => {
+		// #ifdef MP
+		const fs = uni.getFileSystemManager()
+		
+		//自定义文件名
+		if (!format) {
+			console.error('ERROR_BASE64SRC_PARSE')
+			reject(new Error('ERROR_BASE64SRC_PARSE'))
+		}
+		const time = new Date().getTime();
+		let pre = prefix()
+		const filePath = `${pre.env.USER_DATA_PATH}/${time}.${format}`
+		let buffer = base64ToArrayBuffer(bodyData)
+		fs.writeFile({
+			filePath,
+			data: buffer,
+			encoding: 'binary',
+			success() {
+				resolve(filePath)
+			},
+			fail(err) {
+				console.error('获取base64图片失败', JSON.stringify(err))
+				reject(err)
+			}
+		})
+		// #endif
+		
+		// #ifdef H5
+		// mime类型
+		let mimeString = base64.split(',')[0].split(':')[1].split(';')[0]; 
+		//base64 解码
+		let byteString = atob(base64.split(',')[1]); 
+		//创建缓冲数组
+		let arrayBuffer = new ArrayBuffer(byteString.length);
+		//创建视图
+		let intArray = new Uint8Array(arrayBuffer); 
+		for (let i = 0; i < byteString.length; i++) {
+			intArray[i] = byteString.charCodeAt(i);
+		}
+		resolve(URL.createObjectURL(new Blob([intArray], { type: mimeString })))
+		// #endif
+		
+		// #ifdef APP-PLUS
+		const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
+		bitmap.loadBase64Data(base64, () => {
+			if (!format) {
+				console.error('ERROR_BASE64SRC_PARSE')
+				reject(new Error('ERROR_BASE64SRC_PARSE'))
+			}
+			const time = new Date().getTime();
+			const filePath = `_doc/uniapp_temp/${time}.${format}`
+			
+			bitmap.save(filePath, {}, 
+				() => {
+					bitmap.clear()
+					resolve(filePath)
+				}, 
+				(error) => {
+					bitmap.clear()
+					console.error(`${JSON.stringify(error)}`)
+					reject(error)
+				})
+		}, (error) => {
+			bitmap.clear()
+			console.error(`${JSON.stringify(error)}`)
+			reject(error)
+		})
+		// #endif
+	})
+}
+
+/**
+ * 路径转base64
+ * @param {Object} string
+ */
+
+export function pathToBase64(path) {
+	return new Promise((resolve, reject) => {
+		// #ifdef H5
+		const _canvas = ()=> {
+			let image = new Image();
+			image.onload = function() {
+				let canvas = document.createElement('canvas');
+				// 获取图片原始宽高
+				canvas.width = this.naturalWidth;
+				canvas.height = this.naturalHeight;
+				// 将图片插入画布并开始绘制
+				canvas.getContext('2d').drawImage(image, 0, 0);
+				let result = canvas.toDataURL('image/png')
+				resolve(result);
+				canvas.height = canvas.width = 0
+			}
+			image.src = path
+			image.setAttribute("crossOrigin",'Anonymous');
+			image.src = path;
+			image.onerror = (error) => {
+				console.error(`urlToBase64 error: ${path}`, JSON.stringify(error))
+			    reject(new Error('urlToBase64 error'));
+			};
+		}
+		const _fileReader = (blob) => {
+			const fileReader = new FileReader();
+			fileReader.onload = (e) => {
+			    resolve(e.target.result);
+			};
+			fileReader.readAsDataURL(blob);
+			fileReader.onerror = (error) => {
+				console.error('blobToBase64 error:', JSON.stringify(error))
+			    reject(new Error('blobToBase64 error'));
+			};
+		}
+		const isFileReader = typeof FileReader === 'function'
+		if(/^(http|\/\/)/.test(path) && isFileReader ) {
+			window.URL = window.URL || window.webkitURL;
+			const xhr = new XMLHttpRequest();
+			xhr.open("get", path, true);
+			xhr.timeout = 2000;
+			xhr.responseType = "blob";
+			xhr.onload = function() {
+				if(this.status == 200) {
+					_fileReader(this.response)
+				} else {
+					_canvas()
+				}
+			}
+			xhr.onreadystatechange = function() {
+				if(this.status === 0) {
+					console.error('图片跨域了,得后端处理咯')
+				}
+			}
+			xhr.send();
+		} else if(/^blob/.test(path) && isFileReader){
+			_fileReader(path)
+		} else {
+			_canvas()
+		}
+		// #endif
+		
+		// #ifdef MP
+		if(uni.canIUse('getFileSystemManager')) {
+			uni.getFileSystemManager().readFile({
+			    filePath: path,
+			    encoding: 'base64',
+			    success: (res) => {
+			        resolve('data:image/png;base64,' + res.data)
+			    },
+			    fail: (error) => {
+					console.error('urlToBase64 error:', JSON.stringify(error))
+			        reject(error)
+			    }
+			})
+		}
+		// #endif
+		
+		// #ifdef APP-PLUS
+		plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => {
+		    entry.file((file) => {
+		        const fileReader = new plus.io.FileReader()
+		        fileReader.onload = (data) => {
+		            resolve(data.target.result)
+		        }
+		        fileReader.onerror = (error) => {
+					console.error('pathToBase64 error:', JSON.stringify(error))
+		            reject(error)
+		        }
+		        fileReader.readAsDataURL(file)
+		    }, (error) => {
+				console.error('pathToBase64 error:', JSON.stringify(error))
+		        reject(error)
+		    })
+		}, (error) => {
+			console.error('pathToBase64 error:', JSON.stringify(error))
+		    reject(error)
+		})
+		// #endif
+	})
+}
+
+// #ifdef APP-PLUS
+const getLocalFilePath = (path)=> {
+    if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
+        return path
+    }
+    if (path.indexOf('file://') === 0) {
+        return path
+    }
+    if (path.indexOf('/storage/emulated/0/') === 0) {
+        return path
+    }
+    if (path.indexOf('/') === 0) {
+        const localFilePath = plus.io.convertAbsoluteFileSystem(path)
+        if (localFilePath !== path) {
+            return localFilePath
+        } else {
+            path = path.substr(1)
+        }
+    }
+    return '_www/' + path
+}
+// #endif
+
+export function getImageInfo(img, isH5PathToBase64) {
+	return new Promise(async (resolve, reject) => {
+		const base64Reg = /^data:image\/(\w+);base64/
+		const localReg = /^\.|^\/(?=[^\/])/;
+		const networkReg = /^(http|\/\/)/
+		// #ifdef H5
+		if(networkReg.test(img) && isH5PathToBase64) {
+			img = await pathToBase64(img)
+		}
+		// #endif
+		// #ifndef MP-ALIPAY 
+		if(base64Reg.test(img)) {
+			if(!cache[img]) {
+				const imgName = img
+				img = await base64ToPath(img)
+				cache[imgName] = img
+			} else {
+				img = cache[img]
+			}
+		}
+		// #endif
+		if(cache[img] && cache[img].errMsg) {
+			resolve(cache[img])
+		} else {
+			uni.getImageInfo({
+				src: img,
+				success: (image) => {
+					// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
+					image.path = localReg.test(img) ?  `/${image.path}` : image.path;
+					// #endif
+					// image.path = /^(http|\/\/|\/|wxfile|data:image\/(\w+);base64|file|bdfile|ttfile|blob)/.test(image.path) ? image.path : `/${image.path}`;
+					image.url = img
+					cache[img] = image
+					resolve(cache[img])
+				},
+				fail(err) {
+					resolve({path: img})
+					console.error(`getImageInfo:fail ${img} failed ${JSON.stringify(err)}`);
+				}
+			})
+		}
+	})
+}
+	
+	
+	
+export class DataUtil {
+  /**
+   * 设置差异数据
+   * @param component
+   * @param data
+   */
+  setDiffData(component, data) {
+	const diffData = {};
+
+	// 遍历获取到有差异的数据
+	Object.keys(data).forEach(key => {
+	  if (component[key] !== data[key]) {
+		diffData[key] = data[key];
+	  }
+	});
+
+	// 设置数据
+	if (Object.keys(diffData).length) {
+	  // component.setData(diffData);
+	}
+  }
+}
+// const dataUtil = new DataUtil;
+// export dataUtil;

+ 8 - 0
xiaochengxu/pages.json

@@ -211,6 +211,14 @@
             
         }
         ,{
+
+            "path" : "pages/mySet/poster",
+            "style" :                                                                                    
+            {
+                "navigationBarTitleText": "生成海报"
+			}
+		},
+		{
             "path" : "pages/circle/friendSCirlce",
             "style" :                                                                                    
             {

+ 13 - 2
xiaochengxu/pages/cardHolder/scanCodeAddCard.vue

@@ -338,10 +338,21 @@
 			})
 			that = this
 			console.log(options)
-			this.id = options.id.indexOf(",") ? options.id.split(",")[0] : options.id
+			if(options.id){
+				this.id = options.id.indexOf(",") ? options.id.split(",")[0] : options.id
+			}else if(options.q){
+				var id = decodeURIComponent(options.q).split("=")[1]
+				this.id =id.indexOf(",") ? id.split(",")[0] : id
+			}
 			if (uni.getStorageSync("userInfo").phone) {
 				this.userInfo = uni.getStorageSync("userInfo")
-				// this.commonId = options.id.indexOf(",") ? options.id.split(",")[1] : ''
+				if(options.q){
+					var id = decodeURIComponent(options.q).split("=")[1]
+					this.commonId =id.indexOf(",") ? id.split(",")[1] : ''
+				}else{
+					this.commonId = options.id.indexOf(",") ? options.id.split(",")[1] : ''
+				}
+				
 				
 				this.getList()
 				

+ 0 - 1
xiaochengxu/pages/mySet/help.vue

@@ -23,7 +23,6 @@
 .bg{
 	height:162rpx;
 	background: linear-gradient(180deg, #112253 0%,  #FCFCFC 100%);
-	
 }
 // .content{
 // 	padding:20rpx;

+ 5 - 0
xiaochengxu/pages/mySet/mySet.vue

@@ -175,6 +175,11 @@
 						type: "contactCustomer",
 						src: '../../static/imgs/mySet/kf.png'
 					},
+					{
+						name: "生成海报",
+						type: "poster",
+						src: '../../static/imgs/mySet/hb.png'
+					},
 					{
 						name: "设置",
 						type: 'set',

+ 1127 - 0
xiaochengxu/pages/mySet/poster.vue

@@ -0,0 +1,1127 @@
+<template>
+	<view class="page">
+		<view class="poster" v-if="!show_poster">
+			<view class="canvas-content" style="text-align:center;position:relative;" :style='{background:(posterObj1.backgroundType=="rgb"? posterObj1.background:"url("+posterObj1.background+") no-repeat;background-size:100% 100%;"),borderRadius:posterObj1.borderRadius,padding:(check_idx==2?"80rpx 68rpx":"40rpx 36rpx 80rpx")}'>
+				<image v-if='check_idx==1' style='position:absolute;width:103rpx;height:103rpx;bottom: 0;left: 0;' src="https://s.yun-live.com/images/20210201/d88d56843d43b917e2a28550b2a62723.png"></image>
+				<image v-if='check_idx==1' style='position:absolute;width:56.4rpx;height:56.4rpx;right:0rpx;top:50%;' src="https://s.yun-live.com/images/20210201/247736ffd279276b891ec14db8ed0fd0.png"></image>
+				<image :style='{width:posterObj1.width,height:posterObj1.height,borderRadius:posterObj1.imageBorderRadius}' :src="posterObj1.image"></image>
+				<view :style='{fontSize:posterObj1.titleCssFontSize,fontWeight:posterObj1.titleCssFontWeight,color:posterObj1.titleCssColor,lineWeight:posterObj1.titleCssLineHeight
+				}' class="title">{{posterObj1.title}}</view>
+				<view class='flex justify-space-between' style='align-items: flex-start;margin-top:18rpx;'>
+					<view style='text-align:left;margin-top:30rpx;'>
+						<view :style='{fontSize:posterObj1.title1CssFontSize,fontWeight:posterObj1.title1CssFontWeight,color:posterObj1.title1CssColor,lineWeight:posterObj1.title1CssLineHeight
+						}' class="title1">{{posterObj1.title1}}</view>
+						<view :style='{fontSize:posterObj1.title2CssFontSize,color:posterObj1.title2CssColor,lineWeight:posterObj1.title2CssLineHeight
+						}' class="title2">{{posterObj1.title2}}</view>
+					</view>		
+					<view>
+						<image :style='{width:posterObj1.qrcodeWidth,height:posterObj1.qrcodeHeight,}' :src="posterObj1.qrcode"></image>
+					</view>
+				</view>
+			</view>
+			<view class="footer-btn">
+				<view class="">
+					返回
+				</view>
+				<view class="save" @click="toSave">
+					保存
+				</view>
+			</view>
+		</view>
+		<view class="poster-btn">
+			<view class='diy_image'>
+				上传图片
+				<view>
+					<u-radio-group @change='imagechange($event)' v-model="imageValue">
+						<u-radio shape="square" name='自定义图片' label="自定义图片"></u-radio>
+						<u-radio shape="square" name='选择名片' label="选择名片"></u-radio>
+					</u-radio-group>
+				</view>
+			</view>
+			<view style='color:red;font-size:24rpx;padding: 20rpx 20rpx 0;'>注:如勾选“选择名片”,其他用户扫描海报二维码后会自动与您交换名片。</view>
+			<view class='diy_title'>
+				编辑内容
+				<view> 
+				<u--input v-model="value" @focus='titleclick' placeholder="请输入内容" border="bottom" clearable></u--input></view>
+			</view>
+			<view class='check_bg'>
+				<view style='font-size:28rpx'>定义背景</view>
+				<view :class="{'is-check':check_idx==index}" v-for="(item,index) in footer_arr" :key="index" @click="toChoose(index)">
+					<image :src="item" mode=""></image>
+					<view v-if="check_idx==index"></view>
+				</view>	
+			</view>
+			
+			
+		</view>
+		<!-- 生成的图片 -->
+		<uni-popup type="center" ref="posterImg" :maskClick="false">
+			<view class="poster-img">
+				<text @click="closePopup"></text>
+				<view style='overflow: scroll;width: 670rpx;height: 928rpx;'>
+					<image :src="path" mode="widthFix" @click="previewImg"></image>
+				</view>
+				<!-- #ifdef H5 -->
+				<view class="">
+					长按图片保存到手机
+				</view>
+				<!-- #endif -->
+				<!-- #ifndef H5 -->
+				<view class="">
+					点击图片保存到手机
+				</view>
+				<!-- #endif -->
+			</view>
+		</uni-popup>
+		<u-popup :show="titleStatus"  :round="10" mode="center" @close="close" @open="open">
+		    <view style='width:600rpx;padding:20rpx;'>
+		        <u--textarea v-model="value" maxlength='300' count placeholder="请输入内容" ></u--textarea>
+				<view style='margin:20rpx 0rpx 0;'>
+					<u-button @click='confirm' type="success" text="确定"></u-button>
+				</view>
+		    </view>
+		</u-popup>
+		<u-picker :immediateChange="true" keyName="cardBusiness" confirmText='确定' title="选择名片" @cancel="show1=false"
+			closeOnClickOverlay @confirm="cardConfirm" :show="show1" :columns="cardList"></u-picker>
+		<poster :data="canvasData" background-color="#FFF" :width='948' :height='534' @on-success="posterSuccess"
+			ref="poster" @on-error="posterError"></poster>
+		<lPainter :board="posterObj" ref="painter"></lPainter>
+		<canvas id="canvasId" 
+		:type="type" 
+		style="width:0;height:0;"></canvas>
+	</view>
+</template>
+
+<script>
+	var that;
+	import uploadImage from '@/components/ossutil/uploadFile.js';
+	import uniPopup from "@/uni_modules/uni-popup/components/uni-popup/uni-popup.vue";
+	import lPainter from '@/components/lime-painter/index.vue';
+	import { Layout } from '@/components/lime-painter/layout';
+	import Poster from '../../components/zhangyuhao-poster/Poster.vue';
+	import { toPx, isNumber, getImageInfo, base64ToPath, compareVersion  } from '@/components/lime-painter/utils'
+	import { adaptor, expand } from '@/components/lime-painter/canvas';
+	export default {
+		components: {
+			lPainter,
+			uniPopup,
+			Poster,
+		},
+		data() {
+			return {
+				imageValue:'',
+				canvasData:{},
+				value:'云现场 | 2020年 麓客城市 WO-LUNTEER创想礼·麓客共创之夜',
+				show_poster:false,//显示海报
+				path: '', //生成的图片地址
+				image:'',
+				show1:false,
+				cardList: [],
+				list:[],
+				theight:0,
+				canvas1:'',
+				poster:'',
+				type:'2d',
+				height:0,
+				use2dCanvas:false,
+				layout1: new Layout(),
+				titleStatus:false,
+				posterObj1:{
+					background:'#fff',
+					backgroundType:'rgb',
+					borderRadius: '16rpx',
+					image:'https://s.yun-live.com/images/20210201/eb694718fa6c7b90d60a2c250847a192.jpg',
+					width: '606rpx',
+					height: '341rpx',
+					imageBorderRadius:'16rpx',
+					title:'云现场 | 2020年 麓客城市 WO-LUNTEER创想礼·麓客共创之夜',
+					titleCssFontSize: '32rpx',
+					titleCssColor: '#1A2033',
+					titleCssFontWeight: 'bold',
+					titleCssLineHeight: '45rpx',
+					title1:'厦门吴彦祖',
+					title1CssFontSize: '28rpx',
+					title1CssColor: '#1A2033',
+					title1CssFontWeight: 'bold',
+					title1CssLineHeight: '28rpx',
+					title2:'扫描识别二维码',
+					title2CssFontSize: '24rpx',
+					title2CssColor: '#4070FF',
+					title2CssLineHeight: '24rpx',
+					qrcode:'../../static/imgs/qrcode.jpg',
+					qrcodeWidth: '240rpx',
+					qrcodeHeight: '240rpx',
+				},
+				posterObj: {
+					width: '670rpx',
+					height: '890rpx',
+					background: '#fff',
+					borderRadius: '16rpx',
+					views: [
+						// {
+						// 	type: 'image',
+						// 	src: 'https://s.yun-live.com/images/20210201/9a4e7df322dc5c9f30ea9b126d3269a6.png',
+						// 	css: {
+						// 		width: '128rpx',
+						// 		height: '50rpx',
+						// 		left: '542rpx',
+						// 		top: '0rpx',
+						// 	}
+						// },
+						// {
+						// 	type: 'image',
+						// 	src: 'https://s.yun-live.com/images/20210201/eb694718fa6c7b90d60a2c250847a192.jpg',
+						// 	css: {
+						// 		left: '32rpx',
+						// 		top: '480rpx',
+						// 		borderRadius: '50%',
+						// 		width: '80rpx',
+						// 		height: '80rpx'
+						// 	}
+						// },
+						{
+							type: 'text',
+							text: '云现场 | 2020年 麓客城市 WO-LUNTEER创想礼·麓客共创之夜',
+							use:'title',
+							css: {
+								fontSize: '32rpx',
+								color: '#1A2033',
+								fontWeight: 'bold',
+								lineHeight: '45rpx',
+								left: '32rpx',
+								top: '400rpx',
+								width: '606rpx'
+							}
+						},
+						{
+							type: 'text',
+							text: '厦门吴彦祖',
+							use:'title1',
+							css: {
+								fontSize: '28rpx',
+								fontWeight: 'bold',
+								color: '#1A2033',
+								lineHeight: '28rpx',
+								left: '32rpx',
+								top: '515rpx'
+							}
+						},
+						{
+							type: 'text',
+							text: '扫描识别二维码',
+							use:'title2',
+							css: {
+								fontSize: '24rpx',
+								color: '#4070FF',
+								lineHeight: '24rpx',
+								left: '32rpx',
+								top: '559rpx'
+							}
+						},
+						{
+							type: 'image',
+							src: 'https://s.yun-live.com/images/20210201/eb694718fa6c7b90d60a2c250847a192.jpg',
+							use:'dt',
+							css: {
+								left: '32rpx',
+								top: '34rpx',
+								width: '606rpx',
+								height: '341rpx',
+								borderRadius: '16rpx'
+							}
+						},
+						
+						{
+							type: 'qrcode',
+							text: 'https://www.yun-live.com/',
+							css: {
+								left: '445rpx',
+								top: '500rpx',
+								width: '200rpx',
+								height: '200rpx',
+							}
+						},
+						// {
+						// 	type: 'text',
+						// 	text: '————  由云现场提供技术支持  ————',
+						// 	css: {
+						// 		left: '0',
+						// 		top: '863rpx',
+						// 		width: '100%',
+						// 		textAlign: 'center',
+						// 		fontSize: '24rpx',
+						// 		color: '#989FB3',
+						// 		lineHeight: '33rpx'
+						// 	}
+						// }
+					]
+				}, //画板数据
+				footer_arr: [
+					'https://s.yun-live.com/images/20210201/d502979c734077930cee837739ee9285.png',
+					'https://s.yun-live.com/images/20210201/0289000561415e1f9f6e542a3553906d.png',
+					'https://s.yun-live.com/images/20210201/5d2e237728e1dd8727835ca95084721e.png',
+					'https://s.yun-live.com/images/20210201/15075d31c26cc446333d569b0d2346e8.png',
+					'https://s.yun-live.com/images/20210201/6e716c556d1a80e90ecb0260e0990add.png'
+				], //底部选项
+				check_idx: 0, //底部选中的下标
+				dtindex:0,
+				titleindex:0,
+				title1index:0,
+				title2index:0,
+			}
+		},
+		onLoad() {
+			that = this
+		},
+		onShow(){
+			// #ifdef MP-WEIXIN
+			const {SDKVersion, version, platform} = uni.getSystemInfoSync()
+			// ios wx7.0.20 createImage bug
+			this.use2dCanvas = (this.type === '2d' && compareVersion(SDKVersion, '2.9.2') >= 0) && !(/ios/.test(platform) && /7.0.20/.test(version));
+			// #endif
+			this.posterObj1.title1=uni.getStorageSync("userInfo").nickname+'推荐给你'
+			this.$request.baseRequest('admin.unimall.cardManagementInfo', 'list', {
+				commonId: uni.getStorageSync("userInfo").id
+			}, failres => {
+				uni.hideLoading()
+				uni.showToast({
+					icon: "none",
+					title: failres.errmsg,
+					duration: 3000
+				});
+			
+			}).then(res => {
+				this.list = res.data.items
+				this.cardList = [res.data.items]
+			})
+		},
+		watch: {
+			// check_idx(newVal,oldVal){
+			// 	console.log(111,newVal);
+			// 	console.log(222,oldVal);
+			// }
+		},
+		methods: {
+			confirm(e){
+				this.titleStatus=false
+				this.posterObj1.title=this.value
+			},
+			titleclick(){
+				this.titleStatus=true
+			},
+			posterError(err) {
+				console.log(err)
+				uni.hideLoading()
+			},
+			posterSuccess(url) {
+				console.log("hahahah", url)
+				// 生成成功,会把临时路径在这里返回
+				this.poster = url;
+				this.posterObj1.image = url
+				this.posterObj1.qrcode=this.currectData.qrCodeMyself
+				console.log(url)
+				this.show1 = false
+				// this.show=false
+				uni.hideLoading()
+			},
+			cardConfirm(e) {
+				console.log(e)
+				this.currectData = e.value[0]
+				uni.showLoading({
+					title: '加载中',
+					mask: true
+				})
+				var data = [{
+						type: 'image',
+						path: this.currectData.currentBackground,
+						use: 'bg',
+						x: -24,
+						y: -27,
+						width: 1000,
+						height: 600
+					},
+					{
+						type: 'image',
+						path: this.currectData.headSculpture ? this.currectData.headSculpture :
+							'../../static/imgs/card/defaulthead.png',
+						shape: 'circle',
+						use: 'head',
+						imageType: this.currectData.headSculpture ? 'wl' : 'bd',
+						x: 60,
+						y: 60,
+						width: 150,
+						height: 150
+					},
+					{
+						type: 'text',
+						text: this.currectData.name,
+						use: 'name',
+						x: 300,
+						y: 80,
+						size: 40,
+						color: '#000'
+			
+					},
+					{
+						type: 'text',
+						text: this.currectData.post,
+						use: 'post',
+						x: 440,
+						y: 85,
+						size: 32,
+						color: '#666666'
+			
+					},
+					{
+						type: 'text',
+						text: this.currectData.companyName,
+						use: 'companyName',
+						x: 300,
+						y: 155,
+						size: 32,
+						color: '#000'
+			
+					},
+					{
+						type: 'image',
+						path: '../../static/imgs/card/address1.png',
+						use: 'address-icon',
+						x: 300,
+						y: 220,
+						width: 30,
+						height: 40
+					},
+					{
+						type: 'textarea',
+						text: this.currectData.province + this.currectData.city + this.currectData.area + this
+							.currectData.detailedAddress,
+						lineSpace: 1,
+						width: 640,
+						use: 'address',
+						x: 345,
+						y: 228,
+						size: 28,
+						color: '#000'
+					},
+					{
+						type: 'image',
+						path: '../../static/imgs/card/phone1.png',
+						use: 'phone-icon',
+						x: 300,
+						y: 280,
+						width: 32,
+						height: 32
+					},
+					{
+						type: 'text',
+						text: this.currectData.phone,
+						use: 'phone',
+						x: 345,
+						y: 284,
+						size: 28,
+						color: '#000'
+					},
+					{
+						type: 'image',
+						path: '../../static/imgs/card/remark1.png',
+						use: 'remark-icon',
+						x: 300,
+						y: 340,
+						width: 26,
+						height: 32
+					},
+					{
+						type: 'textarea',
+						text: this.currectData.remark ? this.currectData.remark : '单击添加备注',
+						use: 'remark',
+						lineSpace: 2,
+						width: 200,
+						x: 345,
+						y: 345,
+						size: 26,
+						color: '#000'
+					},
+					{
+						type: 'image',
+						path: '../../static/imgs/card/bg3.png',
+						use: 'bg1',
+						x: 0,
+						y: 418,
+						width: 687,
+						height: 120
+					},
+					{
+						type: 'image',
+						path: '../../static/imgs/card/bg4.png',
+						use: 'bg2',
+						x: 647,
+						y: 418,
+						width: 305,
+						height: 120
+					},
+					{
+						type: 'image',
+						path: '../../static/imgs/card/home.png',
+						use: 'home',
+						x: 65,
+						y: 455,
+						width: 55,
+						height: 55
+					},
+					{
+						type: 'text',
+						text: this.currectData.classifyName ? this.currectData.classifyName : '默',
+						use: 'classify',
+						x: 245,
+						y: 460,
+						size: 54,
+						color: '#fff'
+					},
+					{
+						type: 'image',
+						path: '../../static/imgs/card/share.png',
+						use: 'share',
+						x: 435,
+						y: 453,
+						width: 66,
+						height: 52
+					},
+				]
+				console.log(this.currectData.cuttentTemplate, 111111)
+				for (var i = 0; i < data.length; i++) {
+					switch (this.currectData.cuttentTemplate) {
+						case '2':
+							if (data[i].use === 'name' ||
+								data[i].use === 'companyName' ||
+								data[i].use === 'address-icon' ||
+								data[i].use === 'phone-icon' ||
+								data[i].use === 'remark-icon') {
+								data[i].x = 30
+							}
+							if (data[i].use === 'post') {
+								data[i].x = 100
+							}
+							if (data[i].use === 'address' ||
+								data[i].use === 'phone' ||
+								data[i].use === 'remark') {
+								data[i].x = 60
+							}
+							if (data[i].use == 'head') {
+								data[i].x = 370
+							}
+							break;
+						case '3':
+							if (data[i].use === 'name') {
+								data[i].x = 30
+								data[i].y = 150
+							}
+							if (data[i].use === 'companyName') {
+								data[i].x = 30
+								data[i].y = 180
+							}
+							if (data[i].use === 'post') {
+								data[i].x = 100
+								data[i].y = 153
+							}
+							if (data[i].use === 'address-icon' ||
+								data[i].use === 'phone-icon' ||
+								data[i].use === 'remark-icon') {
+								data[i].x = 170
+							}
+							if (data[i].use === 'address-icon') {
+								data[i].y = 150
+							}
+							if (data[i].use === 'address') {
+								data[i].y = 155;
+								data[i].width = 300
+							}
+							if (data[i].use === 'phone-icon') {
+								data[i].y = 180
+							}
+							if (data[i].use === 'phone') {
+								data[i].y = 183
+							}
+							if (data[i].use === 'remark-icon') {
+								data[i].y = 210
+							}
+							if (data[i].use === 'remark') {
+								data[i].y = 212
+							}
+							if (data[i].use === 'address' ||
+								data[i].use === 'phone' ||
+								data[i].use === 'remark') {
+								data[i].x = 190
+							}
+							break;
+						case '4':
+							if (data[i].use === 'name' ||
+								data[i].use === 'companyName') {
+								data[i].x = 30
+							}
+							if (data[i].use === 'companyName') {
+								data[i].y = 90
+							}
+							if (data[i].use === 'post') {
+								data[i].x = 100
+							}
+							if (data[i].use == 'head') {
+								data[i].x = 25
+								data[i].y = 120
+							}
+							if (data[i].use === 'address-icon' ||
+								data[i].use === 'phone-icon' ||
+								data[i].use === 'remark-icon') {
+								data[i].x = 170
+							}
+							if (data[i].use === 'address') {
+								data[i].width = 300
+							}
+							if (data[i].use === 'address' ||
+								data[i].use === 'phone' ||
+								data[i].use === 'remark') {
+								data[i].x = 190
+							}
+							break;
+						default:
+					}
+				}
+				this.canvasData = {
+					clicknum: this.clicknum++,
+					list: data
+				}
+			},
+			open() {
+			        // console.log('open');
+			},
+			close() {
+			    this.show = false
+			        // console.log('close');
+			},
+			
+			imagechange(e){
+				console.log(e,1111)
+				if(e=='自定义图片'){
+					uni.showActionSheet({
+					    itemList: ["拍照", "从手机相册选择"],
+					    success(res) {
+					        let sourceType = "camera";
+					        if (res.tapIndex == 0) {
+					            sourceType = "camera";
+					        } else if (res.tapIndex == 1) {
+					            sourceType = "album";
+					        }
+					        uni.chooseImage({
+					            count: 1, //最多可以选择的图片张数,默认9
+					            sizeType: ["original", "compressed"],   //original 原图,compressed 压缩图,默认二者都有
+					            sourceType: [sourceType], //album 从相册选图,camera 使用相机,默认二者都有。如需直接开相机或直接选相册,请只使用一个选项
+					            success: function(res) {
+					                let tempFilePaths = res.tempFilePaths;
+									that.posterObj1.image = tempFilePaths[0]
+									this.posterObj1.qrcode = '../../static/imgs/qrcode.jpg'
+					               console.log(tempFilePaths)
+					            }
+					          });
+					        }
+					      });
+
+				}else{
+					if(this.list.length>0){
+						this.show1=true
+					}else{
+						uni.showToast({
+							icon: "none",
+							title: '您还未创建名片',
+							duration: 3000
+						})
+					}
+				}
+			},
+			
+			previewImg(){
+				// #ifdef H5
+				return;
+				// #endif
+				uni.previewImage({
+					current:this.path,
+					urls: [this.path]
+				});
+			},
+			closePopup(){
+				this.$refs.posterImg.close();
+				this.show_poster=false;
+			},
+			async toSave() {
+			var that = this
+			//写个同步方法 把所谓 高度宽度 位置 都算好
+			let query = wx.createSelectorQuery();
+			await query.select('.canvas-content').boundingClientRect(rect=>{
+			  this.height = rect.height;
+			}).exec()
+			await query.select('.title').boundingClientRect(rect=>{
+			  this.theight = rect.height;
+			}).exec()
+		
+			setTimeout(()=>{
+				var posterObj = this.$request.makeCanvasData(this.check_idx,this.height,this.posterObj1,this.theight)
+				uni.showLoading({
+					title:'海报生成中',
+				})
+				this.posterObj = posterObj
+				setTimeout(()=>{
+				const painter = this.$refs.painter;
+				console.log(painter)
+					painter.canvasToTempFilePath().then(res => {
+						console.log(res.tempFilePath,'path')
+						this.path = res.tempFilePath;
+						this.$refs.posterImg.open();
+						this.show_poster=true;
+						uni.hideLoading()
+					});
+				},4000)
+			},1000)
+				
+			
+				console.log(this.posterObj)
+				
+				console.log(this.check_idx)
+				if (this.check_idx == 4) return;
+				// switch (this.check_idx) {
+				// 	case 0:
+
+				// 		this.posterObj.background = '#fff';
+				// 		this.posterObj.views=[
+					
+				// 				{
+				// 					type: 'text',
+				// 					text: this.posterObj1.title,
+				// 					use:'title',
+				// 					css: {
+				// 						fontSize: '32rpx',
+				// 						color: '#1A2033',
+				// 						fontWeight: 'bold',
+				// 						lineHeight: '45rpx',
+				// 						left: '32rpx',
+				// 						top: '400rpx',
+				// 						width: '606rpx'
+				// 					}
+				// 				},
+				// 				{
+				// 					type: 'text',
+				// 					text:  this.posterObj1.title1,
+				// 					use:'title1',
+				// 					css: {
+				// 						fontSize: '28rpx',
+				// 						fontWeight: 'bold',
+				// 						color: '#1A2033',
+				// 						lineHeight: '28rpx',
+				// 						left: '32rpx',
+				// 						top: '515rpx'
+				// 					}
+				// 				},
+				// 				{
+				// 					type: 'text',
+				// 					text: '长按或扫描识别二维码',
+				// 					use:'title2',
+				// 					css: {
+				// 						fontSize: '24rpx',
+				// 						color: '#4070FF',
+				// 						lineHeight: '24rpx',
+				// 						left: '32rpx',
+				// 						top: '559rpx'
+				// 					}
+				// 				},
+				// 				{
+				// 					type: 'image',
+				// 					src: 'https://s.yun-live.com/images/20210201/eb694718fa6c7b90d60a2c250847a192.jpg',
+				// 					use:'dt',
+				// 					css: {
+				// 						left: '32rpx',
+				// 						top: '34rpx',
+				// 						width: '606rpx',
+				// 						height: '341rpx',
+				// 						borderRadius: '16rpx'
+				// 					}
+				// 				},
+								
+				// 				{
+				// 					type: 'qrcode',
+				// 					text: 'https://www.yun-live.com/',
+				// 					css: {
+				// 						left: '445rpx',
+				// 						top: '500rpx',
+				// 						width: '200rpx',
+				// 						height: '200rpx',
+				// 					}
+				// 				},
+				// 				// {
+				// 				// 	type: 'text',
+				// 				// 	text: '————  由云现场提供技术支持  ————',
+				// 				// 	css: {
+				// 				// 		left: '0',
+				// 				// 		top: '863rpx',
+				// 				// 		width: '100%',
+				// 				// 		textAlign: 'center',
+				// 				// 		fontSize: '24rpx',
+				// 				// 		color: '#989FB3',
+				// 				// 		lineHeight: '33rpx'
+				// 				// 	}
+				// 				// }
+				// 			]
+				// 		break;
+				// 	case 1:
+				// 	this.posterObj.height=(this.height*2)+'rpx'
+				// 	this.posterObj.views=[{
+				// 				type: 'image',
+				// 				use:'bg',
+				// 				src: 'https://s.yun-live.com/images/20210201/39ae4d9d8ad0b1acac7c224e845c641f.png',
+				// 				css: {
+				// 					left: '0',
+				// 					top: '0',
+				// 					width: '100%',
+				// 					height: '100%'
+				// 				}
+				// 			},
+				// 			{
+				// 				type: 'image',
+				// 				src: 'https://s.yun-live.com/images/20210201/eb694718fa6c7b90d60a2c250847a192.jpg',
+				// 				css: {
+				// 					left: '32rpx',
+				// 					top: '34rpx',
+				// 					width: '606rpx',
+				// 					height: '341rpx',
+				// 					borderRadius: '16rpx'
+				// 				}
+				// 			},
+				// 			{
+				// 				type: 'text',
+				// 				text: this.posterObj1.title,
+				// 				use:'title',
+				// 				css: {
+				// 					fontSize: '32rpx',
+				// 					color: '#fff',
+				// 					fontWeight: 'bold',
+				// 					lineHeight: '45rpx',
+				// 					left: '32rpx',
+				// 					top: '400rpx',
+				// 					width: '606rpx'
+				// 				}
+				// 			},
+				// 			{
+				// 				type: 'text',
+				// 				text: this.posterObj1.title1,
+				// 				use:'title1',
+				// 				css: {
+				// 					fontSize: '28rpx',
+				// 					fontWeight: 'bold',
+				// 					color: '#D8AB87',
+				// 					lineHeight: '28rpx',
+				// 					left: '32rpx',
+				// 					top: '515rpx'
+				// 				}
+				// 			},
+				// 			{
+				// 				type: 'text',
+				// 				text: '长按或扫描识别二维码',
+				// 				use:'title2',
+				// 				css: {
+				// 					fontSize: '24rpx',
+				// 					color: '#FFFFFF',
+				// 					lineHeight: '24rpx',
+				// 					left: '32rpx',
+				// 					top: '559rpx'
+				// 				}
+				// 			},
+							
+				// 			{
+				// 				type: 'image',
+				// 				src: 'https://s.yun-live.com/images/20210201/d88d56843d43b917e2a28550b2a62723.png',
+				// 				css: {
+				// 					left: '551rpx',
+				// 					top: '111rpx',
+				// 					width: '103rpx',
+				// 					height: '103rpx',
+				// 				}
+				// 			},
+				// 			{
+				// 				type: 'image',
+				// 				src: 'https://s.yun-live.com/images/20210201/247736ffd279276b891ec14db8ed0fd0.png',
+				// 				css: {
+				// 					left: '43rpx',
+				// 					top: '432rpx',
+				// 					width: '56.4rpx',
+				// 					height: '56.4rpx',
+				// 				}
+				// 			},
+							
+				// 			{
+				// 				type: 'image',
+				// 				src: 'https://s.yun-live.com/images/20210201/63a9b504fb745b43b9fe5c0adef8fddb.png',
+				// 				css: {
+				// 					left: '50%',
+				// 					top: '750rpx',
+				// 					transform: 'translate(-50%,0)',
+				// 					width: '192rpx',
+				// 					height: '78rpx',
+				// 				}
+				// 			},
+				// 			{
+				// 				type: 'view',
+				// 				use:'qrcode-view',
+				// 				css: {
+				// 					left: '445rpx',
+				// 					top: '500rpx',
+				// 					width: '200rpx',
+				// 					height: '200rpx',
+				// 					background: '#fff',
+				// 				}
+				// 			},
+				// 			{
+				// 				type: 'image',
+				// 				src:this.posterObj1.qrcode,
+				// 				use:'qrcode',
+				// 				css: {
+				// 					left: '455rpx',
+				// 					top: '510rpx',
+				// 					// transform: 'translate(-50%,0)',
+				// 					width: '180rpx',
+				// 					height: '180rpx',
+				// 				}
+				// 			},
+
+				// 		]
+						
+				// 		break;
+				// 	case 2:
+				// 		this.posterObj1.backgroundType='image'
+				// 		this.posterObj1.background='https://s.yun-live.com/images/20210201/78f227bd701da20676c9da9166ce3144.png';
+				// 		this.posterObj1.width='540rpx',
+				// 		this.posterObj1.height='303rpx',
+				// 		this.posterObj1.titleCssColor='#1D1D25'
+				// 		this.posterObj1.title1CssColor='#1D1D25'
+				// 		this.posterObj1.title2CssColor='#6CB37A'
+				// 		break;
+				// 	case 3:
+				// 		this.posterObj1.backgroundType='image'
+				// 		this.posterObj1.background='https://s.yun-live.com/images/20210201/524ab6a41fe8c7eb57b35df9a547d388.png';
+				// 		this.posterObj1.width='606rpx',
+				// 		this.posterObj1.height='341rpx',
+				// 		this.posterObj1.titleCssColor='#1D1D25'
+				// 		this.posterObj1.title1CssColor='#1D1D25'
+				// 		this.posterObj1.title2CssColor='#6CB37A'
+				// 		break;
+				// 	default:
+				// 		break;
+				// }
+				// var node = await this.layout1.calcNode(this.posterObj)
+				// console.log(node,'node')
+				// if(node&&node.children.length===this.posterObj.views.length){
+					// for(var i=0;i<node.children.length;i++){
+					// 	if(node.children[i].use==='title'){
+					// 		dth=node.children[i].layoutBox.height-48
+					// 	}
+					// 	if(node.children[i].use==='title'||node.children[i].use==='title1'||node.children[i].use==='title2'){
+					// 		h+=node.children[i].layoutBox.height
+					// 	}
+					// }
+					// console.log(h,h+780,'h打印')
+					// for(var i=0;i<this.posterObj.views.length;i++){
+					// 	if(this.posterObj.views[i].use==='title1'||this.posterObj.views[i].use==='title2'
+					// 	||this.posterObj.views[i].use==='qrcode-view'||this.posterObj.views[i].use==='qrcode'){
+					// 		var top=this.posterObj.views[i].css.top.split('r')[0]
+					// 		this.posterObj.views[i].css.top=(Number(top)+Number(dth*2))+'rpx'
+					// 	}
+					// }
+					// this.posterObj.width='670rpx'
+					
+					// console.log(this.posterObj,'posterObj')
+					// uni.showLoading({
+					// 	title:'海报生成中',
+					// })
+					// const painter = this.$refs.painter;
+					// setTimeout(()=>{
+					// 	console.log(this.height*2,'height')
+					// 	painter.canvasToTempFilePath().then(res => {
+					// 		console.log(res.tempFilePath,'path')
+					// 		this.path = res.tempFilePath;
+					// 		this.$refs.posterImg.open();
+					// 		this.show_poster=true;
+					// 		uni.hideLoading()
+					// 	});
+					// },1000)
+				// }
+				
+				
+			},
+			toChoose(index) {
+				if (index == 4) return;
+				this.check_idx = index;
+				switch (index) {
+					case 0:
+						this.posterObj1.background='#fff';
+						this.posterObj1.backgroundType='rgb'
+						this.posterObj1.width='606rpx',
+						this.posterObj1.height='341rpx',
+						this.posterObj1.titleCssColor='#1A2033'
+						this.posterObj1.title1CssColor='#1A2033'
+						this.posterObj1.title2CssColor='#4070FF'
+						break;
+					case 1:
+						this.posterObj1.backgroundType='image'
+						this.posterObj1.background='https://s.yun-live.com/images/20210201/39ae4d9d8ad0b1acac7c224e845c641f.png';
+						this.posterObj1.width='606rpx',
+						this.posterObj1.height='341rpx',
+						this.posterObj1.titleCssColor='#fff'
+						this.posterObj1.title1CssColor='#D8AB87'
+						this.posterObj1.title2CssColor='#fff'
+						break;
+					case 2:
+						this.posterObj1.backgroundType='image'
+						this.posterObj1.background='https://s.yun-live.com/images/20210201/78f227bd701da20676c9da9166ce3144.png';
+						this.posterObj1.width='540rpx',
+						this.posterObj1.height='303rpx',
+						this.posterObj1.titleCssColor='#1D1D25'
+						this.posterObj1.title1CssColor='#1D1D25'
+						this.posterObj1.title2CssColor='#6CB37A'
+						break;
+					case 3:
+						this.posterObj1.backgroundType='image'
+						this.posterObj1.background='https://s.yun-live.com/images/20210201/524ab6a41fe8c7eb57b35df9a547d388.png';
+						this.posterObj1.width='606rpx',
+						this.posterObj1.height='341rpx',
+						this.posterObj1.titleCssColor='#1D1D25'
+						this.posterObj1.title1CssColor='#1D1D25'
+						this.posterObj1.title2CssColor='#6CB37A'
+						break;
+					default:
+						break;
+				}
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page{
+		background-color: #F2F4F8;
+	}
+	.page{
+		/deep/ .uni-transition{
+			background-color:rgba(0, 0, 0, 0.6)
+		}
+		.poster-img{
+			width: 670rpx;
+			height: 928rpx;
+			position: relative;
+			text{
+				background: url('https://s.yun-live.com/images/20210201/5c4ef9d86bc5eec90f2f915683d9db08.png') no-repeat;
+				background-size: 100% 100%;
+				display: inline-block;
+				width: 50rpx;
+				height: 50rpx;
+				position: absolute;
+				top: -60rpx;
+				right: 0;
+			}
+			image{
+				width: 100%;
+				height: 100%;
+			}
+			view{
+				font-size: 32rpx;
+				font-family: PingFang-SC-Bold, PingFang-SC;
+				font-weight: bold;
+				color: #FFFFFF;
+				line-height: 32rpx;
+				text-align: center;
+				margin-top: 28rpx;
+			}
+		}
+		.poster{
+			padding: 24rpx 40rpx 410rpx;
+			
+			.footer-btn{
+				margin-top: 24rpx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				view{
+					width: 319rpx;
+					height: 66rpx;
+					border-radius: 40rpx;
+					border: 1px solid #4070FF;
+					font-size: 26rpx;
+					font-family: PingFangSC-Regular, PingFang SC;
+					font-weight: 400;
+					color: #4070FF;
+					line-height: 66rpx;
+					text-align: center;
+				}
+				.save{
+					background: #4070FF;
+					color: #FFFFFF;
+				}
+			}
+		}
+		.poster-btn{
+			position: fixed;
+			bottom: 0;
+			width: 100%;
+			height: 364rpx;
+			background: #FFFFFF;
+			border-radius: 32rpx 32rpx 0px 0px;
+			>.check_bg{
+				display: flex;
+				align-items: center;
+				justify-content: space-around;
+				padding-top:25rpx;
+				>view{
+					width: 80rpx;
+					height: 80rpx;
+					position: relative;
+					border-radius: 10rpx;
+					border: 4rpx solid #fff;
+					&.is-check{
+						border: 4rpx solid #4070FF;
+					}
+					image{
+						width: 100%;
+						height: 100%;
+					}
+					view{
+						width: 30rpx;
+						height: 30rpx;
+						position: absolute;
+						right: -15rpx;
+						bottom: -15rpx;
+						background: url('https://s.yun-live.com/images/20210201/311c01265c1aa508418f6bae10d67602.png') no-repeat;
+						background-size: 100% 100%;
+					}
+				}
+			}
+			/deep/.u-radio{
+				margin-left: 10px;
+			}
+			.diy_title,.diy_image{
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding: 20rpx 20rpx 0;
+				font-size:28rpx;
+			}
+			.diy_title{
+				padding: 10rpx 20rpx 0;
+			}
+		}
+	}
+	.poster{
+		position:relative;
+	}
+	.poster .title{
+		text-align:left;
+		margin-top:20rpx;
+		word-break: break-word;
+	}
+	.poster .title1{
+		margin-bottom:10rpx;
+	}
+</style>

BIN
xiaochengxu/static/imgs/mySet/hb.png


BIN
xiaochengxu/static/imgs/qrcode.jpg


+ 411 - 1
xiaochengxu/util/request.js

@@ -181,11 +181,421 @@ const syncInfo = (userInfo) => {
 	})
 	return promise
 }
+
+// 处理海报数据
+const makeCanvasData = (check_idx,height,posterObj1,theight) => {
+		console.log(height,theight)
+		var dth = (theight - 22) * 2
+		var h=((height+40)*2)
+		console.log(posterObj1)
+		var posterObj= {}
+		switch (check_idx) {
+			case 0:
+		posterObj={
+			width: '670rpx',
+			height: h+'rpx',
+			background: '#fff',
+			borderRadius: '16rpx',
+			views:[
+			
+						{
+							type: 'text',
+							text: posterObj1.title,
+							use:'title',
+							css: {
+								fontSize: '32rpx',
+								color: '#1A2033',
+								fontWeight: 'bold',
+								lineHeight: '45rpx',
+								left: '32rpx',
+								top: '410rpx',
+								width: '606rpx'
+							}
+						},
+						{
+							type: 'text',
+							text:  posterObj1.title1,
+							use:'title1',
+							css: {
+								fontSize: '28rpx',
+								fontWeight: 'bold',
+								color: '#1A2033',
+								lineHeight: '28rpx',
+								left: '32rpx',
+								top: 535+dth+'rpx'
+							}
+						},
+						{
+							type: 'text',
+							text: '长按或扫描识别二维码',
+							use:'title2',
+							css: {
+								fontSize: '24rpx',
+								color: '#4070FF',
+								lineHeight: '24rpx',
+								left: '32rpx',
+								top: 579+dth+'rpx'
+							}
+						},
+						{
+							type: 'image',
+							src:  posterObj1.image,
+							use:'dt',
+							css: {
+								left: '32rpx',
+								top: '44rpx',
+								width: '606rpx',
+								height: '341rpx',
+								borderRadius: '16rpx'
+							}
+						},
+						{
+							type: 'image',
+							src: posterObj1.qrcode,
+							use:'qrcode',
+							css: {
+								left: '455rpx',
+								top: 520+dth+'rpx',
+								// transform: 'translate(-50%,0)',
+								width: '240rpx',
+								height: '240rpx',
+							}
+						},
+						// {
+						// 	type: 'text',
+						// 	text: '————  由云现场提供技术支持  ————',
+						// 	css: {
+						// 		left: '0',
+						// 		top: '863rpx',
+						// 		width: '100%',
+						// 		textAlign: 'center',
+						// 		fontSize: '24rpx',
+						// 		color: '#989FB3',
+						// 		lineHeight: '33rpx'
+						// 	}
+						// }
+					],
+					}
+				break;
+			case 1:
+				posterObj={
+					width: '670rpx',
+					height: h+'rpx',
+					background: '#fff',
+					borderRadius: '16rpx',
+					views: [{
+						type: 'image',
+						use:'bg',
+						src: 'https://s.yun-live.com/images/20210201/39ae4d9d8ad0b1acac7c224e845c641f.png',
+						css: {
+							left: '0',
+							top: '0',
+							width: '100%',
+							height: '100%'
+						}
+					},
+					{
+						type: 'image',
+						src: posterObj1.image,
+						css: {
+							left: '32rpx',
+							top: '44rpx',
+							width: '606rpx',
+							height: '341rpx',
+							borderRadius: '16rpx'
+						}
+					},
+					{
+						type: 'text',
+						text: posterObj1.title,
+						use:'title',
+						css: {
+							fontSize: '32rpx',
+							color: '#fff',
+							fontWeight: 'bold',
+							lineHeight: '45rpx',
+							left: '32rpx',
+							top: '420rpx',
+							width: '590rpx'
+						}
+					},
+					{
+						type: 'text',
+						text:posterObj1.title1,
+						use:'title1',
+						css: {
+							fontSize: '28rpx',
+							fontWeight: 'bold',
+							color: '#D8AB87',
+							lineHeight: '28rpx',
+							left: '32rpx',
+							top: 535+dth+'rpx'
+						}
+					},
+					{
+						type: 'text',
+						text: '长按或扫描识别二维码',
+						use:'title2',
+						css: {
+							fontSize: '24rpx',
+							color: '#FFFFFF',
+							lineHeight: '24rpx',
+							left: '32rpx',
+							top: 579+dth+'rpx'
+						}
+					},
+					
+					{
+						type: 'image',
+						src: 'https://s.yun-live.com/images/20210201/d88d56843d43b917e2a28550b2a62723.png',
+						css: {
+							left: '0rpx',
+							top: '90%',
+							width: '103rpx',
+							height: '103rpx',
+						}
+					},
+					{
+						type: 'image',
+						src: 'https://s.yun-live.com/images/20210201/247736ffd279276b891ec14db8ed0fd0.png',
+						css: {
+							left: '600rpx',
+							top: '50%',
+							width: '56.4rpx',
+							height: '56.4rpx',
+						}
+					},
+					{
+						type: 'view',
+						use:'qrcode-view',
+						css: {
+							left: '445rpx',
+							top: 520+dth+'rpx',
+							width: '240rpx',
+							height: '240rpx',
+							background: '#fff',
+						}
+					},
+					{
+						type: 'image',
+						src: posterObj1.qrcode,
+						use:'qrcode',
+						css: {
+							left: '455rpx',
+							top: 530+dth+'rpx',
+							// transform: 'translate(-50%,0)',
+							width: '220rpx',
+							height: '220rpx',
+						}
+					},
+				]
+			}
+				break;
+			case 2:
+						posterObj={
+							width: '670rpx',
+							height: h+'rpx',
+							background: '#fff',
+							borderRadius: '16rpx',
+							views: [{
+								type: 'image',
+								use:'bg',
+								src: 'https://s.yun-live.com/images/20210201/78f227bd701da20676c9da9166ce3144.png',
+								css: {
+									left: '0',
+									top: '0',
+									width: '100%',
+									height: '100%'
+								}
+							},
+							{
+								type: 'image',
+								src: posterObj1.image,
+								css: {
+									left: '62rpx',
+									top: '84rpx',
+									width: '540rpx',
+									height: '304rpx',
+									borderRadius: '16rpx'
+								}
+							},
+							{
+								type: 'text',
+								text: posterObj1.title,
+								use:'title',
+								css: {
+									fontSize: '32rpx',
+									color: '#1D1D25',
+									fontWeight: 'bold',
+									lineHeight: '45rpx',
+									left: '62rpx',
+									top: '420rpx',
+									width: '530rpx'
+								}
+							},
+							{
+								type: 'text',
+								text:posterObj1.title1,
+								use:'title1',
+								css: {
+									fontSize: '28rpx',
+									fontWeight: 'bold',
+									color: '#1D1D25',
+									lineHeight: '28rpx',
+									left: '62rpx',
+									top: 515+dth+'rpx'
+								}
+							},
+							{
+								type: 'text',
+								text: '长按或扫描识别二维码',
+								use:'title2',
+								css: {
+									fontSize: '24rpx',
+									color: '#6CB37A',
+									lineHeight: '24rpx',
+									left: '62rpx',
+									top: 559+dth+'rpx'
+								}
+							},
+
+							{
+								type: 'view',
+								use:'qrcode-view',
+								css: {
+									left: '405rpx',
+									top: 500+dth+'rpx',
+									width: '240rpx',
+									height: '240rpx',
+									background: '#fff',
+								}
+							},
+							{
+								type: 'image',
+								src: posterObj1.qrcode,
+								use:'qrcode',
+								css: {
+									left: '415rpx',
+									top: 510+dth+'rpx',
+									// transform: 'translate(-50%,0)',
+									width: '220rpx',
+									height: '220rpx',
+								}
+							},]
+						}
+				break;
+			case 3:
+				posterObj={
+					width: '670rpx',
+					height: h+'rpx',
+					background: '#fff',
+					borderRadius: '16rpx',
+					views: [{
+						type: 'image',
+						use:'bg',
+						src: 'https://s.yun-live.com/images/20210201/524ab6a41fe8c7eb57b35df9a547d388.png',
+						css: {
+							left: '0',
+							top: '0',
+							width: '100%',
+							height: '100%'
+						}
+					},
+					{
+						type: 'image',
+						src: posterObj1.image,
+						css: {
+							left: '32rpx',
+							top: '44rpx',
+							width: '606rpx',
+							height: '341rpx',
+							borderRadius: '16rpx'
+						}
+					},
+					{
+						type: 'text',
+						text: posterObj1.title,
+						use:'title',
+						css: {
+							fontSize: '32rpx',
+							color: '#1D1D25',
+							fontWeight: 'bold',
+							lineHeight: '45rpx',
+							left: '32rpx',
+							top: '410rpx',
+							width: '530rpx'
+						}
+					},
+					{
+						type: 'text',
+						text:posterObj1.title1,
+						use:'title1',
+						css: {
+							fontSize: '28rpx',
+							fontWeight: 'bold',
+							color: '#1D1D25',
+							lineHeight: '28rpx',
+							left: '32rpx',
+							top: 525+dth+'rpx'
+						}
+					},
+					{
+						type: 'text',
+						text: '长按或扫描识别二维码',
+						use:'title2',
+						css: {
+							fontSize: '24rpx',
+							color: '#6CB37A',
+							lineHeight: '24rpx',
+							left: '32rpx',
+							top: 569+dth+'rpx'
+						}
+					},
+				
+					{
+						type: 'view',
+						use:'qrcode-view',
+						css: {
+							left: '440rpx',
+							top: 500+dth+'rpx',
+							width: '240rpx',
+							height: '240rpx',
+							background: '#fff',
+						}
+					},
+					{
+						type: 'image',
+						src: posterObj1.qrcode,
+						use:'qrcode',
+						css: {
+							left: '450rpx',
+							top: 510+dth+'rpx',
+							// transform: 'translate(-50%,0)',
+							width: '220rpx',
+							height: '220rpx',
+						}
+					},]
+				}
+				// this.posterObj1.backgroundType='image'
+				// this.posterObj1.background='https://s.yun-live.com/images/20210201/524ab6a41fe8c7eb57b35df9a547d388.png';
+				// this.posterObj1.width='606rpx',
+				// this.posterObj1.height='341rpx',
+				// this.posterObj1.titleCssColor='#1D1D25'
+				// this.posterObj1.title1CssColor='#1D1D25'
+				// this.posterObj1.title2CssColor='#6CB37A'
+				// break;
+			default:
+				break;
+		}
+
+	return posterObj
+}
 export default {
 	baseUrl,
 	baseRequest,
 	TokenRequest,
 	wxlogin,
 	getPhone,
-	syncInfo
+	syncInfo,
+	makeCanvasData
 }