index.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. <template>
  2. <view :style="viewColor">
  3. <view class='poster-poster' v-if="posterStatus">
  4. <view class='poster-pop'>
  5. <image src='../../../static/images/poster-close.png' class='close' @click="posterImageClose"></image>
  6. <view class="user-code">
  7. <image class="canvas" :style="{width:wd+'px', height:hg+'px'}" :src="posterImage" v-if="posterImage"></image>
  8. <canvas class="canvas" :style="{width:wd+'px',height:hg+'px'}" canvas-id="myCanvas" v-else></canvas>
  9. </view>
  10. <!-- #ifndef H5 -->
  11. <view v-if="posterImage" class='save-poster' @click="savePosterPath(posterImage)">保存到手机</view>
  12. <!-- #endif -->
  13. <!-- #ifdef H5 -->
  14. <view v-if="posterImage" class="keep">长按图片可以保存到手机</view>
  15. <!-- #endif -->
  16. </view>
  17. </view>
  18. <view class='mask' v-if="posterImage"></view>
  19. </view>
  20. </template>
  21. <script>
  22. // +----------------------------------------------------------------------
  23. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  24. // +----------------------------------------------------------------------
  25. // | Copyright (c) 2016~2024 https://www.crmeb.com All rights reserved.
  26. // +----------------------------------------------------------------------
  27. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  28. // +----------------------------------------------------------------------
  29. // | Author: CRMEB Team <admin@crmeb.com>
  30. // +----------------------------------------------------------------------
  31. import { imgToBase } from '@/api/user.js';
  32. import { mapGetters } from 'vuex';
  33. export default {
  34. computed: mapGetters(['viewColor']),
  35. data() {
  36. return {
  37. wd: 225,
  38. hg: 357,
  39. posterImage: "",
  40. posterBag: '../static/images/poster-bag.png',
  41. }
  42. },
  43. props: {
  44. picData: {
  45. type: Object
  46. },
  47. posterStatus: {
  48. type: Boolean,
  49. default: false
  50. }
  51. },
  52. watch: {
  53. posterStatus(data) {
  54. if (data) {
  55. this.posterShow()
  56. }
  57. },
  58. },
  59. onLoad() {},
  60. methods: {
  61. async posterShow() {
  62. let that = this;
  63. uni.showLoading({
  64. title: '海报生成中',
  65. mask: true
  66. });
  67. let arr, bag_pic, goods_img, mp_code
  68. goods_img = await that.imgToBase(that.picData.goodsPic)
  69. mp_code = await that.imgToBase(that.picData.codePic)
  70. // #ifdef H5
  71. arr = [that.posterBag, goods_img, mp_code]
  72. // #endif
  73. // #ifdef MP || APP-PLUS
  74. arr = [that.posterBag, await that.downloadFilestoreImage(that.picData.goodsPic), await that.downloadFilestoreImage(
  75. that.picData.codePic)]
  76. // #endif
  77. that.bargainPosterCanvas(arr, that.picData.title, that.picData.label, that.picData.msg, that.picData.price,
  78. that.wd,
  79. that.hg,
  80. (tempFilePath) => {
  81. that.posterImage = tempFilePath
  82. });
  83. },
  84. async imgToBase(url) {
  85. let res = await imgToBase({
  86. image: url
  87. })
  88. return res.data.image
  89. },
  90. savePosterPath(url) {
  91. let that = this;
  92. // #ifdef MP
  93. uni.getSetting({
  94. success(res) {
  95. if (!res.authSetting['scope.writePhotosAlbum']) {
  96. uni.authorize({
  97. scope: 'scope.writePhotosAlbum',
  98. success() {
  99. uni.saveImageToPhotosAlbum({
  100. filePath: url,
  101. success: function(res) {
  102. that.$util.Tips({
  103. title: '保存成功',
  104. icon: 'success'
  105. });
  106. },
  107. fail: function(res) {
  108. that.$util.Tips({
  109. title: '保存失败'
  110. });
  111. }
  112. });
  113. }
  114. });
  115. } else {
  116. uni.saveImageToPhotosAlbum({
  117. filePath: url,
  118. success: function(res) {
  119. that.$util.Tips({
  120. title: '保存成功',
  121. icon: 'success'
  122. });
  123. },
  124. fail: function(res) {
  125. that.$util.Tips({
  126. title: '保存失败'
  127. });
  128. }
  129. });
  130. }
  131. }
  132. });
  133. // #endif
  134. // #ifdef APP-PLUS
  135. uni.saveImageToPhotosAlbum({
  136. filePath: that.posterImage,
  137. success: function(res) {
  138. that.posterImageClose();
  139. that.$util.Tips({
  140. title: '保存成功',
  141. icon: 'success'
  142. });
  143. },
  144. fail: function(res) {
  145. that.$util.Tips({
  146. title: '保存失败'
  147. });
  148. },
  149. })
  150. // #endif
  151. },
  152. posterImageClose() {
  153. this.$emit('update:posterStatus', false)
  154. this.posterImage = ""
  155. },
  156. //图片转符合安全域名路径
  157. downloadFilestoreImage(url) {
  158. // #ifdef MP
  159. let ishttps = url.split('//')[0] == 'https:'
  160. if (!ishttps) {
  161. url = 'https://'+url.split('//')[1]
  162. }
  163. // #endif
  164. return new Promise((resolve, reject) => {
  165. let that = this;
  166. uni.downloadFile({
  167. url: url,
  168. success: function(res) {
  169. resolve(res.tempFilePath);
  170. },
  171. fail: function() {
  172. return that.$util.Tips({
  173. title: ''
  174. });
  175. }
  176. });
  177. })
  178. },
  179. bargainPosterCanvas(arr2, title, label, msg, price, wd, hg, successFn) {
  180. let that = this;
  181. const ctx = uni.createCanvasContext('myCanvas', this);
  182. ctx.clearRect(0, 0, 0, 0);
  183. /**
  184. * 只能获取合法域名下的图片信息,本地调试无法获取
  185. *
  186. */
  187. ctx.fillStyle = '#ccc';
  188. ctx.fillRect(0, 0, wd, hg);
  189. uni.getImageInfo({
  190. src: arr2[0],
  191. success: (res) => {
  192. console.log(arr2)
  193. const WIDTH = res.width;
  194. const HEIGHT = res.height;
  195. ctx.drawImage(arr2[0], 0, 0, wd, hg);
  196. // 保证在不同机型对应坐标准确
  197. let labelx = 0.6656 //标签x
  198. let labely = 0.167 //标签y
  199. let pricex = 0.1847 //价格x
  200. let pricey = 0.175 //价格y
  201. let codex = 0.385 //二维码
  202. let codey = 0.77
  203. let picturex = 0.1571 //商品图左上点
  204. let picturey = 0.2916
  205. let picturebx = 0.6857 //商品图右下点
  206. let pictureby = 0.3916
  207. let msgx = 0.1036 //msg
  208. let msgy = 0.2306
  209. ctx.drawImage(arr2[1], wd * picturex, hg * picturey, wd * picturebx, hg * pictureby);
  210. ctx.save();
  211. ctx.drawImage(arr2[2], wd * codex, hg * codey, 55, 55);
  212. ctx.save();
  213. //标题
  214. const CONTENT_ROW_LENGTH = 30;
  215. let [contentLeng, contentArray, contentRows] = that.textByteLength(title, CONTENT_ROW_LENGTH);
  216. if (contentRows > 2) {
  217. contentRows = 2;
  218. let textArray = contentArray.slice(0, 2);
  219. textArray[textArray.length - 1] += '…';
  220. contentArray = textArray;
  221. }
  222. ctx.setTextAlign('left');
  223. ctx.setFillStyle('#000');
  224. if (contentArray.length < 2) {
  225. ctx.setFontSize(14);
  226. } else {
  227. ctx.setFontSize(12);
  228. }
  229. let contentHh = 3;
  230. for (let m = 0; m < contentArray.length; m++) {
  231. if (m) {
  232. ctx.fillText(contentArray[m], 20, 20 + contentHh * m + 13, 1100);
  233. } else {
  234. ctx.fillText(contentArray[m], 20, 20, 1100);
  235. }
  236. }
  237. // 标签内容
  238. ctx.setTextAlign('left')
  239. ctx.setFontSize(10);
  240. ctx.setFillStyle('#FFF');
  241. ctx.fillText(label, wd * labelx, hg * labely);
  242. ctx.save();
  243. // 价格
  244. ctx.setFillStyle('red');
  245. // ctx.setFontSize(16);
  246. ctx.font = "bold 18px Arial";
  247. ctx.fillText(price, wd * pricex, hg * pricey);
  248. ctx.save();
  249. // msg
  250. ctx.setFontSize(8);
  251. ctx.setFillStyle('#3F3F3F');
  252. ctx.fillText(msg, wd * msgx, hg * msgy);
  253. ctx.save();
  254. ctx.draw(true, () => {
  255. uni.canvasToTempFilePath({
  256. canvasId: 'myCanvas',
  257. fileType: 'png',
  258. quality: 1,
  259. success: (res) => {
  260. console.log(res)
  261. successFn && successFn(res.tempFilePath);
  262. uni.hideLoading();
  263. }
  264. }, this)
  265. });
  266. },
  267. fail: function(err) {
  268. uni.hideLoading();
  269. that.Tips({
  270. title: '无法获取图片信息'
  271. });
  272. }
  273. })
  274. },
  275. textByteLength(text, num) {
  276. let strLength = 0;
  277. let rows = 1;
  278. let str = 0;
  279. let arr = [];
  280. for (let j = 0; j < text.length; j++) {
  281. if (text.charCodeAt(j) > 255) {
  282. strLength += 2;
  283. if (strLength > rows * num) {
  284. strLength++;
  285. arr.push(text.slice(str, j));
  286. str = j;
  287. rows++;
  288. }
  289. } else {
  290. strLength++;
  291. if (strLength > rows * num) {
  292. arr.push(text.slice(str, j));
  293. str = j;
  294. rows++;
  295. }
  296. }
  297. }
  298. arr.push(text.slice(str, text.length));
  299. return [strLength, arr, rows] // [处理文字的总字节长度,每行显示内容的数组,行数]
  300. },
  301. }
  302. }
  303. </script>
  304. <style>
  305. .poster-poster {
  306. width: 100%;
  307. height: 100%;
  308. width: 450rpx;
  309. height: 714rpx;
  310. }
  311. .poster-pop {
  312. position: fixed;
  313. left: 50%;
  314. transform: translateX(-50%);
  315. top: 50%;
  316. margin: 0 auto;
  317. margin-top: -357rpx;
  318. z-index: 999;
  319. }
  320. .poster-pop image {
  321. width: 100%;
  322. height: 100%;
  323. display: block;
  324. }
  325. .poster-pop .close {
  326. width: 46rpx;
  327. height: 75rpx;
  328. position: fixed;
  329. right: 0;
  330. top: -73rpx;
  331. display: block;
  332. z-index: 3999;
  333. }
  334. .poster-pop .save-poster {
  335. background-color: var(--view-theme);
  336. font-size: :22rpx;
  337. color: #fff;
  338. text-align: center;
  339. height: 76rpx;
  340. line-height: 76rpx;
  341. width: 100%;
  342. margin-top: 20rpx;
  343. }
  344. .poster-pop .keep {
  345. color: #fff;
  346. text-align: center;
  347. font-size: 25rpx;
  348. margin-top: 10rpx;
  349. }
  350. .mask {
  351. z-index: 300 !important;
  352. position: fixed;
  353. top: 0;
  354. left: 0;
  355. right: 0;
  356. bottom: 0;
  357. background-color: rgba(0, 0, 0, 0.6);
  358. z-index: 9;
  359. }
  360. </style>