海报:完善商品海报分享
							parent
							
								
									8963ebc6af
								
							
						
					
					
						commit
						dabefbe21c
					
				
							
								
								
									
										2
									
								
								.env
								
								
								
								
							
							
						
						
									
										2
									
								
								.env
								
								
								
								
							|  | @ -5,7 +5,7 @@ SHOPRO_VERSION = v1.8.3 | ||||||
| SHOPRO_BASE_URL = http://api-dashboard.yudao.iocoder.cn | SHOPRO_BASE_URL = http://api-dashboard.yudao.iocoder.cn | ||||||
| 
 | 
 | ||||||
| # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) | # 后端接口 - 测试环境(通过 process.env.NODE_ENV = development) | ||||||
| SHOPRO_DEV_BASE_URL = https://192.168.1.105:48080 | SHOPRO_DEV_BASE_URL = http://192.168.2.21:48080 | ||||||
| ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc | ### SHOPRO_DEV_BASE_URL = http://yunai.natapp1.cc | ||||||
| 
 | 
 | ||||||
| # 后端接口前缀(一般不建议调整) | # 后端接口前缀(一般不建议调整) | ||||||
|  |  | ||||||
|  | @ -196,7 +196,6 @@ | ||||||
| 			title: state.goodsInfo.name, | 			title: state.goodsInfo.name, | ||||||
| 			image: sheep.$url.cdn(state.goodsInfo.picUrl), | 			image: sheep.$url.cdn(state.goodsInfo.picUrl), | ||||||
| 			desc: state.goodsInfo.introduction, | 			desc: state.goodsInfo.introduction, | ||||||
|       path: 'pages/goods/index', |  | ||||||
| 			params: { | 			params: { | ||||||
| 				page: '2', | 				page: '2', | ||||||
| 				query: state.goodsInfo.id, | 				query: state.goodsInfo.id, | ||||||
|  |  | ||||||
|  | @ -53,6 +53,7 @@ | ||||||
| 		// 小程序识别二维码 | 		// 小程序识别二维码 | ||||||
| 		if (options.scene) { | 		if (options.scene) { | ||||||
| 			const sceneParams = decodeURIComponent(options.scene).split('='); | 			const sceneParams = decodeURIComponent(options.scene).split('='); | ||||||
|  |       console.log("sceneParams=>",sceneParams); | ||||||
| 			options[sceneParams[0]] = sceneParams[1]; | 			options[sceneParams[0]] = sceneParams[1]; | ||||||
| 		} | 		} | ||||||
| 		// #endif | 		// #endif | ||||||
|  |  | ||||||
|  | @ -2,15 +2,39 @@ | ||||||
| <template> | <template> | ||||||
|   <su-popup :show="show" round="10" @close="onClosePoster" type="center" class="popup-box"> |   <su-popup :show="show" round="10" @close="onClosePoster" type="center" class="popup-box"> | ||||||
|     <view class="ss-flex-col ss-col-center ss-row-center"> |     <view class="ss-flex-col ss-col-center ss-row-center"> | ||||||
|  |       <view | ||||||
|  |         v-if="poster.views.length === 0" | ||||||
|  |         class="poster-title ss-flex ss-row-center" | ||||||
|  |         :style="{ | ||||||
|  |           height: poster.height + 'px', | ||||||
|  |           width: poster.width + 'px', | ||||||
|  |         }" | ||||||
|  |       > | ||||||
|  |         海报加载中... | ||||||
|  |       </view> | ||||||
|  |       <!-- v-if 保障等待所有的海报信息都加载完后才开始渲染 --> | ||||||
|           <template v-if="poster.views.length > 0"> |           <template v-if="poster.views.length > 0"> | ||||||
|         <l-painter :board="poster" /> |             <l-painter :board="poster" ref="painterRef"/> | ||||||
|           </template> |           </template> | ||||||
|     </view> |     </view> | ||||||
|  |     <view | ||||||
|  |       class="poster-btn-box ss-m-t-20 ss-flex ss-row-between ss-col-center" | ||||||
|  |       v-if="poster.views.length > 0" | ||||||
|  |     > | ||||||
|  |       <button class="cancel-btn ss-reset-button" @tap="onClosePoster">取消</button> | ||||||
|  |       <button class="save-btn ss-reset-button ui-BG-Main" @tap="onSavePoster"> | ||||||
|  |         {{ | ||||||
|  |           ['wechatOfficialAccount', 'H5'].includes(sheep.$platform.name) | ||||||
|  |             ? '长按图片保存' | ||||||
|  |             : '保存图片' | ||||||
|  |         }} | ||||||
|  |       </button> | ||||||
|  |     </view> | ||||||
|   </su-popup> |   </su-popup> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script setup> | <script setup> | ||||||
|   import { getCurrentInstance, reactive } from 'vue'; |   import { reactive, ref } from 'vue'; | ||||||
|   import sheep from '@/sheep'; |   import sheep from '@/sheep'; | ||||||
|   import { getPosterData } from '@/sheep/components/s-share-modal/canvas-poster/poster'; |   import { getPosterData } from '@/sheep/components/s-share-modal/canvas-poster/poster'; | ||||||
| 
 | 
 | ||||||
|  | @ -26,24 +50,22 @@ | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|   const poster = reactive({ |   const poster = reactive({ | ||||||
|     css: { |     css: { | ||||||
|       // 根节点若无尺寸,自动获取父级节点 |       // 根节点若无尺寸,自动获取父级节点 | ||||||
|       width: sheep.$platform.device.windowWidth * 0.9, |       width: sheep.$platform.device.windowWidth * 0.9, | ||||||
|       height: 600, |       height: 600 | ||||||
|     }, |     }, | ||||||
|     views: [], |     views: [], | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|   const emits = defineEmits(['success', 'close']); |   const emits = defineEmits(['success', 'close']); | ||||||
|   const vm = getCurrentInstance(); |  | ||||||
| 
 | 
 | ||||||
|   const onClosePoster = () => { |   const onClosePoster = () => { | ||||||
|     emits('close'); |     emits('close'); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|  |   const painterRef = ref() | ||||||
|   // 保存海报图片 |   // 保存海报图片 | ||||||
|   const onSavePoster = () => { |   const onSavePoster = () => { | ||||||
|     if (['WechatOfficialAccount', 'H5'].includes(sheep.$platform.name)) { |     if (['WechatOfficialAccount', 'H5'].includes(sheep.$platform.name)) { | ||||||
|  | @ -51,8 +73,16 @@ | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     painterRef.value.canvasToTempFilePathSync({ | ||||||
|  |       fileType: "jpg", | ||||||
|  |       // 如果返回的是base64是无法使用 saveImageToPhotosAlbum,需要设置 pathType为url | ||||||
|  |       pathType: 'url', | ||||||
|  |       quality: 1, | ||||||
|  |       success: (res) => { | ||||||
|  |         console.log(res.tempFilePath); | ||||||
|  |         // 非H5 保存到相册 | ||||||
|         uni.saveImageToPhotosAlbum({ |         uni.saveImageToPhotosAlbum({ | ||||||
|       filePath: poster.src, |           filePath: res.tempFilePath, | ||||||
|           success: (res) => { |           success: (res) => { | ||||||
|             onClosePoster(); |             onClosePoster(); | ||||||
|             sheep.$helper.toast('保存成功'); |             sheep.$helper.toast('保存成功'); | ||||||
|  | @ -62,15 +92,16 @@ | ||||||
|             console.log('图片保存失败:', err); |             console.log('图片保存失败:', err); | ||||||
|           }, |           }, | ||||||
|         }); |         }); | ||||||
|  |       }, | ||||||
|  |     }); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   // 使用 canvas 生成海报 |   // 获得海报数据 | ||||||
|   async function getPoster(params) { |   async function getPoster() { | ||||||
|     let drawer = await getPosterData({ |     poster.views = await getPosterData({ | ||||||
|       width: sheep.$platform.device.windowWidth * 0.9, |       width: poster.css.width, | ||||||
|       shareInfo: props.shareInfo, |       shareInfo: props.shareInfo, | ||||||
|     }); |     }); | ||||||
|     poster.views = drawer; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   defineExpose({ |   defineExpose({ | ||||||
|  | @ -120,10 +151,4 @@ | ||||||
|     border-radius: 20rpx; |     border-radius: 20rpx; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .hideCanvas { |  | ||||||
|     position: fixed; |  | ||||||
|     top: -99999rpx; |  | ||||||
|     left: -99999rpx; |  | ||||||
|     z-index: -99999; |  | ||||||
|   } |  | ||||||
| </style> | </style> | ||||||
|  |  | ||||||
|  | @ -12,7 +12,6 @@ const goods = async (poster) => { | ||||||
|       src: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.goods_bg)), |       src: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.goods_bg)), | ||||||
|       css: { |       css: { | ||||||
|         width, |         width, | ||||||
|         background: 'white', |  | ||||||
|         position: 'fixed', |         position: 'fixed', | ||||||
|         'object-fit': 'contain', |         'object-fit': 'contain', | ||||||
|         top: '0', |         top: '0', | ||||||
|  |  | ||||||
|  | @ -1,87 +0,0 @@ | ||||||
| /** |  | ||||||
|  * Shopro + qs-canvas 绘制海报 |  | ||||||
|  * @version 1.0.0 |  | ||||||
|  * @author lidongtony |  | ||||||
|  * @param {Object} options - 海报参数 |  | ||||||
|  * @param {Object} vm - 自定义组件实例 |  | ||||||
|  */ |  | ||||||
| import QSCanvas from 'qs-canvas'; |  | ||||||
| import { getPosterData } from './poster'; |  | ||||||
| 
 |  | ||||||
| export default async function useCanvas(options, vm) { |  | ||||||
|   const width = options.width; |  | ||||||
|   const qsc = new QSCanvas( |  | ||||||
|     { |  | ||||||
|       canvasId: options.canvasId, |  | ||||||
|       width: options.width, |  | ||||||
|       height: options.height, |  | ||||||
|       setCanvasWH: (canvas) => { |  | ||||||
|         options.height = canvas.height; |  | ||||||
|       }, |  | ||||||
|     }, |  | ||||||
|     vm, |  | ||||||
|   ); |  | ||||||
| 
 |  | ||||||
|   let drawer = await getPosterData(options); |  | ||||||
|   console.log(drawer); |  | ||||||
|   // 绘制背景图
 |  | ||||||
|   const background = await qsc.drawImg({ |  | ||||||
|     type: 'image', |  | ||||||
|     val: drawer.background, |  | ||||||
|     x: 0, |  | ||||||
|     y: 0, |  | ||||||
|     width, |  | ||||||
|     mode: 'widthFix', |  | ||||||
|     zIndex: 0, |  | ||||||
|   }); |  | ||||||
|   await qsc.updateCanvasWH({ |  | ||||||
|     width: background.width, |  | ||||||
|     height: background.bottom, |  | ||||||
|   }); |  | ||||||
| 
 |  | ||||||
|   let list = drawer.list; |  | ||||||
| 
 |  | ||||||
|   for (let i = 0; i < list.length; i++) { |  | ||||||
|     let item = list[i]; |  | ||||||
|     // 绘制文字
 |  | ||||||
|     if (item.type === 'text') { |  | ||||||
|       await qsc.drawText(item); |  | ||||||
|     } |  | ||||||
|     // 绘制图片
 |  | ||||||
|     if (item.type === 'image') { |  | ||||||
|       if (item.d) { |  | ||||||
|         qsc.setCircle({ |  | ||||||
|           x: item.x, |  | ||||||
|           y: item.y, |  | ||||||
|           d: item.d, |  | ||||||
|           clip: true, |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
| 
 |  | ||||||
|       if (item.r) { |  | ||||||
|         qsc.setRect({ |  | ||||||
|           x: item.x, |  | ||||||
|           y: item.y, |  | ||||||
|           height: item.height, |  | ||||||
|           width: item.width, |  | ||||||
|           r: item.r, |  | ||||||
|           clip: true, |  | ||||||
|         }); |  | ||||||
|       } |  | ||||||
|       await qsc.drawImg(item); |  | ||||||
|       qsc.restore(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // 绘制二维码
 |  | ||||||
|     if (item.type === 'qrcode') { |  | ||||||
|       await qsc.drawQrCode(item); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   await qsc.draw(); |  | ||||||
|   // 延迟执行, 防止不稳定
 |  | ||||||
|   setTimeout(async () => { |  | ||||||
|     options.src = await qsc.toImage(); |  | ||||||
|   }, 100); |  | ||||||
|   return options; |  | ||||||
| } |  | ||||||
|  | @ -19,7 +19,6 @@ const getShareInfo = ( | ||||||
|     title: '', // 自定义分享标题
 |     title: '', // 自定义分享标题
 | ||||||
|     desc: '', // 自定义描述
 |     desc: '', // 自定义描述
 | ||||||
|     image: '', // 自定义分享图片
 |     image: '', // 自定义分享图片
 | ||||||
|     path: '', // 页面基础路径,不要以 / 开头
 |  | ||||||
|     params: {}, // 自定义分享参数
 |     params: {}, // 自定义分享参数
 | ||||||
|   }, |   }, | ||||||
|   poster = { |   poster = { | ||||||
|  | @ -46,6 +45,8 @@ const getShareInfo = ( | ||||||
| 
 | 
 | ||||||
|   // 配置分享链接地址
 |   // 配置分享链接地址
 | ||||||
|   shareInfo.link = buildSpmLink(query, shareConfig.linkAddress); |   shareInfo.link = buildSpmLink(query, shareConfig.linkAddress); | ||||||
|  |   // 配置页面地址带参数
 | ||||||
|  |   shareInfo.path = buildSpmPath(query); | ||||||
| 
 | 
 | ||||||
|   // 配置转发参数
 |   // 配置转发参数
 | ||||||
|   if (shareConfig.methods.includes('forward')) { |   if (shareConfig.methods.includes('forward')) { | ||||||
|  | @ -59,8 +60,7 @@ const getShareInfo = ( | ||||||
|     shareInfo.desc = scene.desc || shareConfig.forwardInfo.subtitle; |     shareInfo.desc = scene.desc || shareConfig.forwardInfo.subtitle; | ||||||
|     shareInfo.path = buildSpmPath(scene.path, query); |     shareInfo.path = buildSpmPath(scene.path, query); | ||||||
|   } |   } | ||||||
|   shareInfo.path = scene.path; | 
 | ||||||
|   console.log('shareInfo', shareInfo); |  | ||||||
|   return shareInfo; |   return shareInfo; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | @ -70,7 +70,6 @@ const buildSpmQuery = (params) => { | ||||||
|   let shareId = '0'; // 设置分享者用户ID
 |   let shareId = '0'; // 设置分享者用户ID
 | ||||||
|   if (typeof params.shareId === 'undefined') { |   if (typeof params.shareId === 'undefined') { | ||||||
|     if (user.isLogin) { |     if (user.isLogin) { | ||||||
|       // todo @芋艿 id为空
 |  | ||||||
|       shareId = user.userInfo.id; |       shareId = user.userInfo.id; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | @ -91,9 +90,11 @@ const buildSpmQuery = (params) => { | ||||||
|   return `spm=${shareId}.${page}.${query}.${platform}.${from}`; |   return `spm=${shareId}.${page}.${query}.${platform}.${from}`; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 构造页面分享参数
 | // 构造页面分享参数: 所有的分享都先到首页进行 spm 参数解析
 | ||||||
| const buildSpmPath = (path, query) => { | const buildSpmPath = (query) => { | ||||||
|   return `/${path}?${query}`; |   // 默认是主页,页面 page,例如 pages/index/index,根路径前不要填加 /,
 | ||||||
|  |   // 不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面。scancode_time为系统保留参数,不允许配置
 | ||||||
|  |   return `pages/index/index`; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // 构造分享链接
 | // 构造分享链接
 | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| import { | import { | ||||||
| 	defineStore | 	defineStore | ||||||
| } from 'pinia'; | } from 'pinia'; | ||||||
| // import userApi from '@/sheep/api/user';
 |  | ||||||
| import $share from '@/sheep/platform/share'; | import $share from '@/sheep/platform/share'; | ||||||
| import { | import { | ||||||
| 	isEmpty, | 	isEmpty, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 puhui999
						puhui999