poster.vue 13 KB

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