poster.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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.msg">
  10. <image src="../static/images/yin_l.png" class="yin-l"></image>
  11. {{ cardInfo.msg }}
  12. <image src="../static/images/yin_r.png" class="yin-r"></image>
  13. </view>
  14. <!-- codUrl -->
  15. <view class="code"><image :src="codUrl"></image></view>
  16. <view class="infos">
  17. <view class="name">{{ cardInfo.realName }}</view>
  18. <view class="company">{{ cardInfo.companyName }}</view>
  19. <view class="industry">{{ cardInfo.job }}</view>
  20. </view>
  21. <!-- <view class="qy-logo ddflex"><image src="../../static/images/logo.png"></image></view> -->
  22. </view>
  23. <view class="opt dflex">
  24. <!-- @click="downloadImg(codUrl)" -->
  25. <!-- <button class="li" hover-class="none" open-type="share">
  26. <image src="../static/images/hb_ico1.png"></image>
  27. <view>分享到微信</view>
  28. </button> -->
  29. <view class="li" @click="downloadImg(codUrl)">
  30. <image src="../static/images/hb_ico1.png"></image>
  31. <view>下载名片码</view>
  32. </view>
  33. <view class="li" @click="generatePoster()">
  34. <image src="../static/images/hb_ico2.png"></image>
  35. <view>保存图片</view>
  36. </view>
  37. </view>
  38. <canvas class="canvas" style="width:590px;height:736px;" canvas-id="share"></canvas>
  39. </view>
  40. </view>
  41. </template>
  42. <script>
  43. const app = getApp();
  44. const req = require('../../utils/request.js');
  45. const api = require('../../utils/api.js');
  46. const util = require('../../utils/util.js');
  47. export default {
  48. data() {
  49. return {
  50. id: '',
  51. cardInfo: '',
  52. codUrl: 'https://img.zhiqiyun.com/zqy3t9kp2zc5i/2023/03/20/be0cfb643cd38b97d5a7b14eecaaa8a1.png',
  53. isShowLoading: false
  54. };
  55. },
  56. onLoad(opt) {
  57. this.id = opt.id;
  58. this.getCardInfo();
  59. },
  60. onShareAppMessage(res) {
  61. if (res.from == 'button') {
  62. let userInfo = req.getStorage('userInfo');
  63. this.userBehavior(6)
  64. return {
  65. title: this.cardInfo.realName,
  66. path: '/card/index/index?cardId=' + this.cardInfo.id + '&userId=' + userInfo.id+ '&appId=' + req.getStorage('appId'),
  67. imageUrl: this.cardInfo.avatar + '?x-oss-process=style/w375', // 分享图
  68. };
  69. }
  70. },
  71. methods: {
  72. removeHtml(content){
  73. return util.removeHtml(content);
  74. },
  75. appToShare() {
  76. let userInfo = req.getStorage('userInfo');
  77. uni.share({
  78. provider: 'weixin',
  79. scene: 'WXSceneSession',
  80. type: 5,
  81. imageUrl: this.cardInfo.avatar + '?x-oss-process=style/w375',
  82. title: this.cardInfo.realName,
  83. summary: this.cardInfo.realName,
  84. miniProgram: {
  85. id: req.public.miniProgramGh_id,
  86. path: '/card/index/index?cardId=' + this.cardInfo.id + '&userId=' + userInfo.id,
  87. type: req.env[req.env.NODE_ENV].typeMiniProgram,
  88. webUrl: req.public.homeWebUrl
  89. },
  90. success: ret => {
  91. this.userBehavior(6)
  92. }
  93. });
  94. },
  95. getCardInfo() {
  96. let dataP = {}
  97. let url = ''
  98. if (this.id) {
  99. url = api.card_info;
  100. dataP.id = this.id;
  101. } else {
  102. url = api.card_userInfo;
  103. dataP = {};
  104. }
  105. req.getRequest(url, dataP, data => {
  106. this.cardInfo = data;
  107. this.getCodeUrl()
  108. });
  109. },
  110. // 用户行为
  111. userBehavior(type){
  112. var dataP = {};
  113. dataP.type = 23 //、产品 2、活动 3、未知 4、内容 5、课程 6、老师 7、素材 8、题目 9、资料领取 10、招聘职位 12、用户须知 13、素材 15、医院科室 16、海报 20、医生 21 新闻 23名片
  114. dataP.behavior = type; //1、关注 2、收藏 3、点赞 4、浏览 5、确认 6、分享
  115. dataP.bindId = this.cardInfo.id;
  116. req.postRequestLoding('/api/v3/behavior/save', dataP, data => {
  117. });
  118. },
  119. getCodeUrl() {
  120. let that = this; //获取小程序码
  121. const params = {
  122. page: 'card/index/index',
  123. scene: this.cardInfo.id
  124. };
  125. return new Promise((resolve, reject) => {
  126. req.getRequest('/api/program/codev', params, url => {
  127. // console.log(url);
  128. this.codUrl = url
  129. resolve();
  130. });
  131. });
  132. },
  133. generatePoster() {
  134. let that = this;
  135. if (!that.isShowLoading) {
  136. uni.showLoading({
  137. title: '生成中',
  138. mask: true
  139. });
  140. that.isShowLoading = true;
  141. }
  142. this.generate(imgUrl => {
  143. console.log(imgUrl)
  144. if (that.isShowLoading) {
  145. uni.hideLoading();
  146. that.isShowLoading = false;
  147. that.saveImage(imgUrl);
  148. }
  149. });
  150. },
  151. generate(success) {
  152. let that = this;
  153. let logoPromise = this.getImageInfo(this.cardInfo.imageMap?this.cardInfo.imageMap:this.cardInfo.avatar);
  154. let codePromise = this.getImageInfo(this.codUrl);
  155. Promise.all([logoPromise, codePromise]).then(([logo, code]) => {
  156. const ctx = uni.createCanvasContext('share', this); // 绘制背景,填充满整个canvas画布
  157. let width = 590;
  158. let height = 736;
  159. // ctx.setFillStyle('#fff');
  160. ctx.fillRect(0, 0, width, height);
  161. ctx.drawImage('../static/images/poster_bg.png',0, 0, width, height);
  162. // 简介
  163. ctx.fillStyle = '#fff';
  164. ctx.font = '30px PingFang SC';
  165. // let text = '“'+that.removeHtml(that.cardInfo.brief)+'”';
  166. let text = '“'+that.cardInfo.msg+'”';
  167. var textWidth = ctx.measureText(text).width;
  168. var xPosition = width / 2 - textWidth / 2;
  169. ctx.fillText(text,xPosition,70);
  170. ctx.save();
  171. // 姓名
  172. ctx.fillStyle = '#fff';
  173. ctx.font = '32px PingFang SC';
  174. ctx.fillText(that.cardInfo.realName, (width - 324) / 2 , 490);
  175. ctx.save();
  176. ctx.fillStyle = '#fff';
  177. ctx.font = '20px PingFang SC';
  178. ctx.fillText(that.cardInfo.companyName, (width - 324) / 2, 530);
  179. ctx.save();
  180. ctx.fillStyle = '#fff';
  181. ctx.font = '20px PingFang SC';
  182. ctx.fillText(that.cardInfo.job, (width - 324) / 2, 570);
  183. ctx.save();
  184. this.roundRect(ctx,(width - 324) / 2, 100,324, 324,16)
  185. // 小程序码
  186. ctx.drawImage(code.path, (width - 304) / 2, 110, 304, 304);
  187. ctx.save();
  188. // 完成作画
  189. ctx.draw(false, function() {
  190. uni.canvasToTempFilePath(
  191. {
  192. canvasId: 'share',
  193. success: function(res) {
  194. success.call(this, res.tempFilePath);
  195. },
  196. fail: function(res) {}
  197. },
  198. that
  199. );
  200. });
  201. });
  202. },
  203. getImageInfo(url) {
  204. if (url) {
  205. return new Promise((resolve, reject) => {
  206. if (!url) {
  207. resolve();
  208. return '';
  209. }
  210. uni.getImageInfo({
  211. src: url,
  212. success: resolve,
  213. fail: reject
  214. });
  215. });
  216. } else {
  217. return '';
  218. }
  219. },
  220. /**
  221. *
  222. * @param {CanvasContext} ctx canvas上下文
  223. * @param {number} x 圆角矩形选区的左上角 x坐标
  224. * @param {number} y 圆角矩形选区的左上角 y坐标
  225. * @param {number} w 圆角矩形选区的宽度
  226. * @param {number} h 圆角矩形选区的高度
  227. * @param {number} r 圆角的半径
  228. */
  229. roundRect: function(ctx, x, y, w, h, r) {
  230. // 开始绘制
  231. ctx.beginPath(); // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
  232. // 这里是使用 fill 还是 stroke都可以,二选一即可
  233. ctx.setFillStyle('#fff'); // ctx.setStrokeStyle('transparent')
  234. // 左上角
  235. ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5); // border-top
  236. ctx.moveTo(x + r, y);
  237. ctx.lineTo(x + w - r, y);
  238. ctx.lineTo(x + w, y + r); // 右上角
  239. ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2); // border-right
  240. ctx.lineTo(x + w, y + h - r);
  241. ctx.lineTo(x + w - r, y + h); // 右下角
  242. ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5); // border-bottom
  243. ctx.lineTo(x + r, y + h);
  244. ctx.lineTo(x, y + h - r); // 左下角
  245. ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI); // border-left
  246. ctx.lineTo(x, y + r);
  247. ctx.lineTo(x + r, y); // 这里是使用 fill 还是 stroke都可以,二选一即可,但是需要与上面对应
  248. ctx.fill(); // ctx.stroke()
  249. ctx.closePath(); // 剪切
  250. ctx.clip();
  251. },
  252. borderRect(ctx, x, y, w, h, r, fillColor, strokeColor) {
  253. // 画圆角 ctx、x起点、y起点、w宽度、y高度、r圆角半径、fillColor填充颜色、strokeColor边框颜色
  254. // 开始绘制
  255. ctx.beginPath()
  256. // 绘制左上角圆弧 Math.PI = 180度
  257. // 圆心x起点、圆心y起点、半径、以3点钟方向顺时针旋转后确认的起始弧度、以3点钟方向顺时针旋转后确认的终止弧度
  258. ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5)
  259. // 绘制border-top
  260. // 移动起点位置 x终点、y终点
  261. ctx.moveTo(x + r, y)
  262. // 画一条线 x终点、y终点
  263. ctx.lineTo(x + w - r, y)
  264. // ctx.lineTo(x + w, y + r)
  265. // 绘制右上角圆弧
  266. ctx.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2)
  267. // 绘制border-right
  268. ctx.lineTo(x + w, y + h - r)
  269. // ctx.lineTo(x + w - r, y + h)
  270. // 绘制右下角圆弧
  271. ctx.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5)
  272. // 绘制border-bottom
  273. ctx.lineTo(x + r, y + h)
  274. // ctx.lineTo(x, y + h - r)
  275. // 绘制左下角圆弧
  276. ctx.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI)
  277. // 绘制border-left
  278. ctx.lineTo(x, y + r)
  279. // ctx.lineTo(x + r, y)
  280. if (fillColor) {
  281. // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
  282. ctx.setFillStyle(fillColor)
  283. // 对绘画区域填充
  284. ctx.fill()
  285. }
  286. if (strokeColor) {
  287. // 因为边缘描边存在锯齿,最好指定使用 transparent 填充
  288. ctx.setStrokeStyle(strokeColor)
  289. // 画出当前路径的边框
  290. ctx.stroke()
  291. }
  292. // 关闭一个路径
  293. // ctx.closePath()
  294. // 剪切,剪切之后的绘画绘制剪切区域内进行,需要save与restore
  295. ctx.clip()
  296. },
  297. /**
  298. * 下载图片
  299. * @param {Object} url
  300. */
  301. downloadImg(url) {
  302. let that = this;
  303. uni.getImageInfo({
  304. src: url,
  305. success(res) {
  306. console.log('res==',res)
  307. that.saveImage(res.path);
  308. }
  309. })
  310. // that.saveImage(url);
  311. },
  312. isAuth(fun) {
  313. if (!uni.saveImageToPhotosAlbum) {
  314. uni.showModal({
  315. title: '提示',
  316. content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
  317. });
  318. return;
  319. }
  320. ; //可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.writePhotosAlbum" 这个 scope
  321. // #ifndef H5
  322. uni.getSetting({
  323. success(res) {
  324. // console.log(res);
  325. if (!res.authSetting['scope.writePhotosAlbum']) {
  326. // 接口调用询问
  327. uni.authorize({
  328. scope: 'scope.writePhotosAlbum',
  329. success() {
  330. // console.log("2-授权《保存图片》权限成功");
  331. fun(true);
  332. },
  333. fail() {
  334. fun(false);
  335. }
  336. });
  337. } else {
  338. // 拒绝授权
  339. fun(true);
  340. }
  341. },
  342. fail(res) {
  343. fun(false);
  344. }
  345. });
  346. // #endif
  347. },
  348. /**
  349. * 保存图片
  350. * @param {Object} tempFilePath
  351. */
  352. saveImage(tempFilePath) {
  353. let that = this;
  354. that.isAuth(success => {
  355. if (success) {
  356. uni.saveImageToPhotosAlbum({
  357. filePath: tempFilePath,
  358. success(res) {
  359. that.saveShareBehavior();
  360. uni.showToast({
  361. title: '图片保存成功',
  362. icon: 'none',
  363. success() { // that.generate(imgUrl => {
  364. // that.setData({
  365. // imgUrl: imgUrl
  366. // })
  367. // })
  368. },
  369. fail() {
  370. uni.showToast({
  371. title: '图片保存失败'
  372. });
  373. }
  374. });
  375. }
  376. });
  377. }
  378. });
  379. },
  380. saveShareBehavior(){
  381. let params = {
  382. behavior: 6,
  383. type: 23,
  384. bindId: this.cardInfo.id
  385. }
  386. req.saveBehavior(params,res=>{})
  387. },
  388. // saveImage(tempFilePath) {
  389. // util.saveImage(tempFilePath);
  390. // }
  391. }
  392. };
  393. </script>
  394. <style>
  395. @import './poster.css';
  396. </style>