poster.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. <template>
  2. <view class="content">
  3. <view class="bg" >
  4. <image :src="cardInfo.avatar" mode="aspectFit"></images>
  5. </view>
  6. <view>
  7. <view class="info">
  8. <view class="logo"><image :src="cardInfo.avatar ? cardInfo.avatar : '../../static/images/qy_logo.jpg'" mode="aspectFit"></image></view>
  9. <!-- <view class="brief" v-if="cardInfo.message">
  10. <image src="../static/images/yin_l.png" class="yin-l"></image>
  11. {{ cardInfo.message }}
  12. <image src="../static/images/yin_r.png" class="yin-r"></image>
  13. </view> -->
  14. <view class="info-title">专业顾问为你提供服务</view>
  15. <!-- codUrl -->
  16. <view class="code"><image :src="codUrl"></image></view>
  17. <view class="infos">
  18. <view class="name">{{ cardInfo.realName }}<text style="font-size: 24rpx;margin-left: 10rpx;font-weight: 400;">高级组经理</text></view>
  19. <view class="company">{{ cardInfo.companyName }}</view>
  20. <view class="industry">{{ cardInfo.industryName }}</view>
  21. </view>
  22. </view>
  23. <view class="opt dflex">
  24. <!-- @click="downloadImg(codUrl)" -->
  25. <!-- #ifdef MP-WEIXIN -->
  26. <button class="li" hover-class="none" open-type="share">
  27. <image src="../static/images/hb_ico1.png"></image>
  28. <view>分享到微信</view>
  29. </button>
  30. <!-- #endif -->
  31. <!-- #ifdef APP-PLUS -->
  32. <view class="li" @click="appToShare" >
  33. <image src="../static/images/hb_ico1.png"></image>
  34. <view>分享到微信</view>
  35. </view>
  36. <!-- #endif -->
  37. <view class="li" @click="generatePoster()">
  38. <image src="../static/images/hb_ico2.png"></image>
  39. <view>保存图片</view>
  40. </view>
  41. </view>
  42. <canvas class="canvas" style="width:590px;height:736px;" canvas-id="share"></canvas>
  43. </view>
  44. </view>
  45. </template>
  46. <script>
  47. const app = getApp();
  48. const req = require('../../utils/request.js');
  49. const api = require('../../utils/api.js');
  50. const util = require('../../utils/util.js');
  51. export default {
  52. data() {
  53. return {
  54. id: '',
  55. cardInfo: '',
  56. codUrl: 'https://img.zhiqiyun.com/zqy3t9kp2zc5i/2023/03/20/be0cfb643cd38b97d5a7b14eecaaa8a1.png',
  57. isShowLoading: false
  58. };
  59. },
  60. onLoad(opt) {
  61. this.id = opt.id;
  62. this.getCardInfo();
  63. },
  64. onShareAppMessage(res) {
  65. if (res.from == 'button') {
  66. let userInfo = req.getStorage('userInfo');
  67. this.userBehavior(6)
  68. return {
  69. title: this.cardInfo.realName,
  70. path: '/card/index/index?cardId=' + this.cardInfo.id + '&userId=' + userInfo.id,
  71. imageUrl: this.cardInfo.avatar + '?x-oss-process=style/w375', // 分享图
  72. };
  73. }
  74. },
  75. methods: {
  76. appToShare() {
  77. let userInfo = req.getStorage('userInfo');
  78. uni.share({
  79. provider: 'weixin',
  80. scene: 'WXSceneSession',
  81. type: 5,
  82. imageUrl: this.cardInfo.avatar + '?x-oss-process=style/w375',
  83. title: this.cardInfo.realName,
  84. summary: this.cardInfo.realName,
  85. miniProgram: {
  86. id: req.public.miniProgramGh_id,
  87. path: '/card/index/index?cardId=' + this.cardInfo.id + '&userId=' + userInfo.id,
  88. type: req.env[req.env.NODE_ENV].typeMiniProgram,
  89. webUrl: req.public.homeWebUrl
  90. },
  91. success: ret => {
  92. this.userBehavior(6)
  93. }
  94. });
  95. },
  96. getCardInfo() {
  97. let dataP = {}
  98. let url = ''
  99. if (this.id) {
  100. url = '/api/v3/visiting/card/info';
  101. dataP.id = this.id;
  102. } else {
  103. url = '/api/v3/visiting/card/userInfo';
  104. dataP = {};
  105. }
  106. req.getRequest(url, dataP, data => {
  107. this.cardInfo = data;
  108. this.getCodeUrl()
  109. });
  110. },
  111. // 用户行为
  112. userBehavior(type){
  113. var dataP = {};
  114. dataP.type = 23 //、产品 2、活动 3、未知 4、内容 5、课程 6、老师 7、素材 8、题目 9、资料领取 10、招聘职位 12、用户须知 13、素材 15、医院科室 16、海报 20、医生 21 新闻 23名片
  115. dataP.behavior = type; //1、关注 2、收藏 3、点赞 4、浏览 5、确认 6、分享
  116. dataP.bindId = this.cardInfo.id;
  117. req.postRequestLoding('/api/v3/behavior/save', dataP, data => {
  118. });
  119. },
  120. getCodeUrl() {
  121. let that = this; //获取小程序码
  122. const params = {
  123. page: 'card/index/index',
  124. scene: this.cardInfo.id
  125. };
  126. return new Promise((resolve, reject) => {
  127. req.getRequest('/api/program/codev', params, url => {
  128. // console.log(url);
  129. this.code = url
  130. resolve();
  131. });
  132. });
  133. },
  134. generatePoster() {
  135. let that = this;
  136. if (!that.isShowLoading) {
  137. uni.showLoading({
  138. title: '生成中',
  139. mask: true
  140. });
  141. that.isShowLoading = true;
  142. }
  143. this.generate(imgUrl => {
  144. console.log(imgUrl)
  145. if (that.isShowLoading) {
  146. uni.hideLoading();
  147. that.isShowLoading = false;
  148. that.saveImage(imgUrl);
  149. }
  150. });
  151. },
  152. generate(success) {
  153. let that = this;
  154. let logoPromise = this.getImageInfo(this.cardInfo.avatar);
  155. let codePromise = this.getImageInfo(this.codUrl);
  156. Promise.all([logoPromise, codePromise]).then(([logo, code]) => {
  157. const ctx = uni.createCanvasContext('share', this); // 绘制背景,填充满整个canvas画布
  158. let width = 590;
  159. let height = 736;
  160. ctx.setFillStyle('#FFF');
  161. ctx.fillRect(0, 0, width, height);
  162. this.roundRect(ctx,0,0,width, height,16)
  163. // 渐变色
  164. var grd=ctx.createLinearGradient(0,height-250,0,736);
  165. grd.addColorStop(0,'#A4C8FF');
  166. grd.addColorStop(1,'#498FF8');
  167. ctx.setFillStyle(grd)
  168. ctx.fillRect(0,height-250,590,250);
  169. ctx.save();
  170. // 小程序码
  171. ctx.drawImage(code.path, (width - 304) / 2, 110, 304, 304);
  172. ctx.save();
  173. // 姓名
  174. ctx.fillStyle = '#fff';
  175. ctx.font = '38px PingFang SC';
  176. ctx.fillText(that.cardInfo.realName, 60 , height-250+100);
  177. ctx.save();
  178. ctx.fillStyle = '#fff';
  179. ctx.font = '28px PingFang SC';
  180. ctx.fillText(that.cardInfo.companyName, 60, height-250+100+50);
  181. ctx.save();
  182. // 边框文本
  183. ctx.setFontSize(20);
  184. ctx.setFillStyle('#ffffff');
  185. ctx.setTextAlign('left');
  186. var discountText = that.cardInfo.industryName
  187. var bdColor = '#fff';
  188. var bdBackground = 'transparent';
  189. var bdRadius = 5;
  190. var textPadding = 12;
  191. var boxHeight = 38;
  192. var boxWidth = ctx.measureText(discountText).width + textPadding * 2;
  193. ctx.fillText(discountText, 60+textPadding, height-250+90+80+25);
  194. that.borderRect(ctx, 60, height-250+90+80, boxWidth, boxHeight, bdRadius, bdBackground, bdColor)
  195. // 完成作画
  196. ctx.draw(false, function() {
  197. uni.canvasToTempFilePath(
  198. {
  199. canvasId: 'share',
  200. success: function(res) {
  201. success.call(this, res.tempFilePath);
  202. },
  203. fail: function(res) {}
  204. },
  205. that
  206. );
  207. });
  208. });
  209. },
  210. getImageInfo(url) {
  211. if (url) {
  212. return new Promise((resolve, reject) => {
  213. if (!url) {
  214. resolve();
  215. return '';
  216. }
  217. uni.getImageInfo({
  218. src: url,
  219. success: resolve,
  220. fail: reject
  221. });
  222. });
  223. } else {
  224. return '';
  225. }
  226. },
  227. /**
  228. *
  229. * @param {CanvasContext} ctx canvas上下文
  230. * @param {number} x 圆角矩形选区的左上角 x坐标
  231. * @param {number} y 圆角矩形选区的左上角 y坐标
  232. * @param {number} w 圆角矩形选区的宽度
  233. * @param {number} h 圆角矩形选区的高度
  234. * @param {number} r 圆角的半径
  235. */
  236. roundRect: function(ctx, x, y, w, h, r) {
  237. // 开始绘制
  238. ctx.beginPath(); // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
  239. // 这里是使用 fill 还是 stroke都可以,二选一即可
  240. ctx.setFillStyle('#fff'); // ctx.setStrokeStyle('transparent')
  241. // 左上角
  242. ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5); // border-top
  243. ctx.moveTo(x + r, y);
  244. ctx.lineTo(x + w - r, y);
  245. ctx.lineTo(x + w, y + r); // 右上角
  246. ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2); // border-right
  247. ctx.lineTo(x + w, y + h - r);
  248. ctx.lineTo(x + w - r, y + h); // 右下角
  249. ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5); // border-bottom
  250. ctx.lineTo(x + r, y + h);
  251. ctx.lineTo(x, y + h - r); // 左下角
  252. ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI); // border-left
  253. ctx.lineTo(x, y + r);
  254. ctx.lineTo(x + r, y); // 这里是使用 fill 还是 stroke都可以,二选一即可,但是需要与上面对应
  255. ctx.fill(); // ctx.stroke()
  256. ctx.closePath(); // 剪切
  257. ctx.clip();
  258. },
  259. borderRect(ctx, x, y, w, h, r, fillColor, strokeColor) {
  260. // 画圆角 ctx、x起点、y起点、w宽度、y高度、r圆角半径、fillColor填充颜色、strokeColor边框颜色
  261. // 开始绘制
  262. ctx.beginPath()
  263. // 绘制左上角圆弧 Math.PI = 180度
  264. // 圆心x起点、圆心y起点、半径、以3点钟方向顺时针旋转后确认的起始弧度、以3点钟方向顺时针旋转后确认的终止弧度
  265. ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
  266. // 绘制border-top
  267. // 移动起点位置 x终点、y终点
  268. ctx.moveTo(x + r, y)
  269. // 画一条线 x终点、y终点
  270. ctx.lineTo(x + w - r, y)
  271. // ctx.lineTo(x + w, y + r)
  272. // 绘制右上角圆弧
  273. ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
  274. // 绘制border-right
  275. ctx.lineTo(x + w, y + h - r)
  276. // ctx.lineTo(x + w - r, y + h)
  277. // 绘制右下角圆弧
  278. ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5)
  279. // 绘制border-bottom
  280. ctx.lineTo(x + r, y + h)
  281. // ctx.lineTo(x, y + h - r)
  282. // 绘制左下角圆弧
  283. ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI)
  284. // 绘制border-left
  285. ctx.lineTo(x, y + r)
  286. // ctx.lineTo(x + r, y)
  287. if (fillColor) {
  288. // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
  289. ctx.setFillStyle(fillColor)
  290. // 对绘画区域填充
  291. ctx.fill()
  292. }
  293. if (strokeColor) {
  294. // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
  295. ctx.setStrokeStyle(strokeColor)
  296. // 画出当前路径的边框
  297. ctx.stroke()
  298. }
  299. // 关闭一个路径
  300. // ctx.closePath()
  301. // 剪切,剪切之后的绘画绘制剪切区域内进行,需要save与restore
  302. ctx.clip()
  303. },
  304. /**
  305. * 下载图片
  306. * @param {Object} url
  307. */
  308. downloadImg(url) {
  309. let that = this;
  310. console.log('下载图片>>>>>>',url);
  311. uni.downloadFile({
  312. url: url, //仅为示例,并非真实的资源
  313. success: res => {
  314. that.saveImage(res.tempFilePath);
  315. }
  316. });
  317. },
  318. /**
  319. * 保存图片
  320. * @param {Object} tempFilePath
  321. */
  322. saveImage(tempFilePath) {
  323. util.saveImage(tempFilePath);
  324. }
  325. }
  326. };
  327. </script>
  328. <style>
  329. @import './poster.css';
  330. </style>