goods-detail.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. <template>
  2. <view class="rg">
  3. <view class="rg_swiper">
  4. <swiper @change="swiperChange">
  5. <swiper-item v-for="(item, index) in banner" :key="index">
  6. <image :src="item" mode="aspectFit"></image>
  7. </swiper-item>
  8. </swiper>
  9. <view class="rg_swiper_page">
  10. <text>{{ allImg ? current + 1 : current }}/{{ allImg }}</text>
  11. </view>
  12. </view>
  13. <view class="rg_info">
  14. <view class="goods_name">{{ goods.name }}</view>
  15. <view class="goods_price flexS">
  16. <view class="big_price">
  17. <text>¥</text>
  18. <text>{{ goods.price }}</text>
  19. </view>
  20. <view class="save_box">VIP可省{{ Number(goods.price) - Number(goods.vip_price) || 0 }}</view>
  21. </view>
  22. <view class="safe_box">
  23. <view>
  24. <text class="iconfont iconxuanzhong"></text>
  25. <text>支付安全有保障</text>
  26. </view>
  27. <view style="margin-top:20rpx;">
  28. <text class="iconfont iconxuanzhong"></text>
  29. <text>90天-366天不满意全额退款</text>
  30. </view>
  31. <view style="margin-top:20rpx;">
  32. <text class="iconfont iconxuanzhong"></text>
  33. <text>一次性购买满2套,在本店终身享受VIP价格</text>
  34. </view>
  35. </view>
  36. </view>
  37. <view class="rich">
  38. <rich-text :nodes="goods.content | formatRichText"></rich-text>
  39. </view>
  40. <view class="rg_bug" @click="shareNow()">立即分享</view>
  41. <view class="share_show" v-if="shareShow">
  42. <view class="share_con flexCC">
  43. <text class="iconfont iconguanbi" @click="shareShow = false"></text>
  44. <view class="canvas">
  45. <view class="canvas">
  46. <canvas canvas-id="canvas" style="width: 100%;height:100%;" id="canvas"><img :src="poster"
  47. class="poster_img" mode="widthFix" @longpress="saveImg" /></canvas>
  48. </view>
  49. </view>
  50. <view class="save_img">长按图片发送给朋友</view>
  51. </view>
  52. </view>
  53. <view class="qrimg">
  54. <tki-qrcode ref="qrcode" :val="code.val" :size="code.size" :icon="code.icon" :iconSize="code.iconsize"
  55. :onval="code.onval" :loadMake="code.loadMake" :showLoading="code.showLoading"
  56. :loadingText="code.loadingText" @result="drawPoster" />
  57. </view>
  58. </view>
  59. </template>
  60. <script>
  61. import {
  62. goodsDetail
  63. } from '../../apis/shop.js';
  64. import formatRichText from '@/filters/index.js';
  65. import QR from '@/common/util/wxqrcode.js'; // 二维码生成器
  66. import {
  67. handleClipboard
  68. } from '../../common/util/utils.js';
  69. export default {
  70. data() {
  71. return {
  72. goods: '', //商品详情
  73. banner: [],
  74. allImg: 0, //banner图总张数
  75. current: 0, //当前banner图
  76. shareShow: false, //显示海报
  77. canvasWidth: 0, //canvas 宽
  78. canvasHeight: 0, //canvas 高
  79. store: '', //店铺的信息
  80. poster: '', //海报图片
  81. imgPath: '',
  82. model: '', //手机机型
  83. isVip: '', // 0不是vip 1 是vip
  84. code: {
  85. val: '',
  86. size: 200,
  87. icon: '',
  88. iconsize: 25,
  89. onval: true,
  90. loadMake: '',
  91. showLoading: true,
  92. loadingText: '二维码生成中...'
  93. },
  94. shareGoods: {}, //分享海报上的图片
  95. qr_code: '', //微信二维码
  96. goods_img: '' //分享的商品图片
  97. };
  98. },
  99. onLoad(ops) {
  100. if (ops.goods_id) {
  101. this.getDetail(ops.goods_id);
  102. this.store = uni.getStorageSync('store');
  103. }
  104. wx.getSystemInfo({
  105. success: res => {
  106. this.model = res.model;
  107. this.canvasWidth = res.windowWidth / 375;
  108. this.canvasHeight = res.windowHeight;
  109. }
  110. });
  111. },
  112. onShow() {
  113. this.qr_code = uni.getStorageSync('qrCode');
  114. },
  115. methods: {
  116. //保存海报到相册
  117. saveImg() {
  118. uni.getSetting({
  119. success: res => {
  120. // 如果没有则获取授权
  121. if (!res.authSetting['scope.writePhotosAlbum']) {
  122. uni.authorize({
  123. scope: 'scope.writePhotosAlbum',
  124. success: res => {
  125. uni.saveImageToPhotosAlbum({
  126. filePath: this.poster,
  127. success: res => {
  128. uni.showModal({
  129. content: '图片已保存到相册,赶快分享给好友吧~',
  130. showCancel: false
  131. })
  132. },
  133. fail: err => {
  134. uni.showToast({
  135. title: '保存失败',
  136. icon: 'none'
  137. })
  138. }
  139. })
  140. },
  141. fail: err => {
  142. uni.showModal({
  143. title: '提示',
  144. content: '需要获取相册访问权限,请到小程序设置页面打开授权',
  145. cancelText: '取消',
  146. cancelColor: '#999',
  147. confirmText: '确定',
  148. confirmColor: '#f94218',
  149. success: res => {
  150. if (res.confirm) {
  151. uni.openSetting({
  152. success: res => {
  153. console.log(res
  154. .authSetting
  155. )
  156. },
  157. fail: err => {
  158. uni.showModal({
  159. content: '调起设置失败,请手动设置相册权限',
  160. showCancel: false
  161. })
  162. }
  163. })
  164. } else if (res.cancel) {
  165. uni.showModal({
  166. content: '您取消了设置相册权限,不能保存图片到相册',
  167. showCancel: false
  168. })
  169. }
  170. }
  171. })
  172. }
  173. })
  174. } else {
  175. uni.showLoading({
  176. title: '保存中...'
  177. })
  178. // 有则直接保存不知道哦
  179. wx.saveImageToPhotosAlbum({
  180. filePath: this.poster,
  181. success: res => {
  182. uni.showModal({
  183. content: '图片已保存到相册,赶快分享给好友吧~',
  184. showCancel: false
  185. })
  186. uni.hideLoading()
  187. },
  188. fail: err => {
  189. uni.hideLoading()
  190. uni.showToast({
  191. title: '保存失败',
  192. icon: 'none'
  193. })
  194. }
  195. })
  196. }
  197. }
  198. })
  199. },
  200. /*复制单号*/
  201. copy(data) {
  202. uni.setClipboardData({
  203. data,
  204. success: res => {
  205. uni.showToast({
  206. title: '复制成功'
  207. });
  208. },
  209. fail() {
  210. uni.showToast({
  211. title: '复制失败',
  212. icon: 'none',
  213. });
  214. }
  215. })
  216. },
  217. /*立即分享*/
  218. shareNow() {
  219. if (!this.qr_code) {
  220. uni.showModal({
  221. content: '为了方便客户加你微信咨询购买,请先上传微信二维码',
  222. showCancel: false,
  223. success: res => {
  224. if (res.confirm) {
  225. uni.navigateTo({
  226. url: '../add-qrcode/add-qrcode'
  227. });
  228. }
  229. }
  230. });
  231. return false;
  232. }
  233. let goods = this.goods;
  234. uni.getImageInfo({
  235. src: goods.imgs,
  236. success: res => {
  237. this.goods_img = res.path
  238. }
  239. })
  240. this.shareGoods = {
  241. // img: goods.imgs,
  242. price: parseInt(goods.price),
  243. name: goods.main_attr
  244. };
  245. this.code.val =
  246. `${this.$config.share_url}#pages/index/index?store_id=${this.store.id}&goods_id=${this.goods.id}`;
  247. this.shareShow = true;
  248. this.$refs.qrcode._makeCode();
  249. },
  250. drawPoster(codeImg) {
  251. let _this = this;
  252. uni.showLoading({
  253. title: '图片生成中...'
  254. });
  255. let goods = _this.shareGoods;
  256. uni.getImageInfo({
  257. src: 'https://api.wd.cliu.cc/good_poster.png',
  258. success: res => {
  259. uni.showLoading({
  260. title: '图片加载中...'
  261. });
  262. let ctx = uni.createCanvasContext('canvas');
  263. let store = _this.store;
  264. store.name = store.name.length > 7 ? store.name.slice(0, 7) + '...的店铺' : store.name +
  265. '的店铺';
  266. let rpx = _this.canvasWidth;
  267. //背景图
  268. ctx.drawImage(res.path, 0, 0, 270 * rpx, 480 * rpx);
  269. //店铺名称
  270. ctx.font = 'normal bold 14px pingfang';
  271. ctx.textAlign = 'center';
  272. ctx.fillText(store.name, uni.upx2px(270), uni.upx2px(210));
  273. //商品图
  274. ctx.drawImage(this.goods_img, uni.upx2px(110), uni.upx2px(270), uni.upx2px(130), uni
  275. .upx2px(
  276. 103));
  277. // ctx.drawImage(res.tempFilePath, uni.upx2px(110), uni.upx2px(270), uni.upx2px(130), uni.upx2px(103));
  278. //商品款式
  279. ctx.font = 'normal bold 14px pingfang';
  280. ctx.fillText(goods.name, goods.name == '纯棉老人版' ? uni.upx2px(320) : uni.upx2px(290), uni
  281. .upx2px(300));
  282. //商品数量
  283. ctx.font = 'normal normal 12px pingfang';
  284. ctx.setFillStyle('#999999');
  285. //商品价格
  286. ctx.font = 'normal bold 12px pingfang';
  287. ctx.setFillStyle('#FB231F');
  288. ctx.fillText(`¥`, uni.upx2px(256), uni.upx2px(365));
  289. ctx.font = 'normal bold 16px pingfang';
  290. ctx.setFillStyle('#FB231F');
  291. ctx.fillText(goods.price, uni.upx2px(300), uni.upx2px(365));
  292. //二维码
  293. ctx.drawImage(codeImg, uni.upx2px(110), uni.upx2px(420), uni.upx2px(320), uni.upx2px(
  294. 320));
  295. //扫一扫下单,安全有保障
  296. ctx.font = 'noraml bold 14px pingfang';
  297. ctx.textAlign = 'center';
  298. ctx.setFillStyle('#060001');
  299. ctx.fillText('长按二维码-识别-进店铺', uni.upx2px(270), uni.upx2px(790));
  300. setTimeout(res => {
  301. ctx.draw(false, () => {
  302. uni.canvasToTempFilePath({
  303. width: 270 * rpx,
  304. height: 480 * rpx,
  305. canvasId: 'canvas',
  306. success: res => {
  307. uni.hideLoading();
  308. if (this.model == 'iPhone') {
  309. this.poster = this.dataURLtoFile(res
  310. .tempFilePath).name;
  311. } else {
  312. let url = res.tempFilePath.replace(
  313. /\. +/g, '');
  314. this.poster = url.replace(/[\r\n]/g,
  315. '');
  316. }
  317. },
  318. fail: () => {
  319. uni.hideLoading()
  320. }
  321. });
  322. });
  323. }, 500);
  324. }
  325. });
  326. },
  327. /*获取商品详情*/
  328. getDetail(goods_id) {
  329. let that = this;
  330. uni.showLoading({
  331. title: '加载中...'
  332. });
  333. goodsDetail({
  334. goods_id
  335. })
  336. .then(res => {
  337. if (res.code == 200) {
  338. this.goods = res.data;
  339. if (res.data.banner) {
  340. that.banner = JSON.parse(res.data.banner);
  341. that.allImg = that.banner.length;
  342. }
  343. } else {
  344. uni.showModal({
  345. content: res.data,
  346. showCancel: false
  347. });
  348. }
  349. uni.hideLoading();
  350. })
  351. .catch(err => {
  352. uni.hideLoading();
  353. });
  354. },
  355. swiperChange(e) {
  356. this.current = e.target.current;
  357. },
  358. dataURLtoFile(dataurl, filename = 'file') {
  359. let arr = dataurl.split(',');
  360. let mime = arr[0].match(/:(.*?);/)[1];
  361. let suffix = mime.split('/')[1];
  362. let bstr = atob(arr[1]);
  363. let n = bstr.length;
  364. let u8arr = new Uint8Array(n);
  365. while (n--) {
  366. u8arr[n] = bstr.charCodeAt(n);
  367. }
  368. return new File([u8arr], `${filename}.${suffix}`, {
  369. type: mime
  370. });
  371. }
  372. }
  373. };
  374. </script>
  375. <style lang="scss" scoped>
  376. .rg_info {
  377. width: 100%;
  378. padding: 30rpx;
  379. box-sizing: border-box;
  380. background-color: #ffffff;
  381. border-bottom: 2rpx solid #e9e9e9;
  382. .goods_name {
  383. font-size: 32rpx;
  384. font-weight: bold;
  385. }
  386. .safe {
  387. width: 690rpx;
  388. margin: 20rpx auto;
  389. font-size: 30rpx;
  390. background: linear-gradient(274deg, #edfdf3 0%, #e1f6e9 100%);
  391. border-radius: 24rpx;
  392. padding: 20rpx 30rpx;
  393. text {
  394. font-weight: bold;
  395. }
  396. .iconfont {
  397. color: #349e08;
  398. margin-right: 20rpx;
  399. font-weight: normal;
  400. vertical-align: 2rpx;
  401. }
  402. }
  403. .vip_con {
  404. width: 690rpx;
  405. margin: 0 auto;
  406. padding: 0 30rpx;
  407. height: 140rpx;
  408. background-size: 100% 100%;
  409. box-sizing: border-box;
  410. background: #fff4f3;
  411. border-radius: 16rpx;
  412. view {
  413. margin-left: 20rpx;
  414. text {
  415. font-size: 32rpx;
  416. color: $base-color;
  417. font-weight: bold;
  418. }
  419. text:last-child {
  420. display: inline-block;
  421. margin-top: 10rpx;
  422. }
  423. }
  424. .dart_icon {
  425. width: 56rpx;
  426. height: 56rpx;
  427. flex-shrink: 0;
  428. }
  429. }
  430. }
  431. .goods_price {
  432. margin-top: 20rpx;
  433. .old_price {
  434. color: #999999;
  435. text-decoration: line-through;
  436. }
  437. .price {
  438. color: $base-color;
  439. font-weight: 600;
  440. font-size: 36rpx;
  441. }
  442. .vip_img {
  443. width: 88rpx;
  444. height: 36rpx;
  445. vertical-align: -6rpx;
  446. margin-right: 10rpx;
  447. }
  448. .vip {
  449. font-size: 28rpx;
  450. font-weight: bold;
  451. }
  452. .vip_active {
  453. font-size: 36rpx;
  454. color: $base-color;
  455. font-weight: bold;
  456. }
  457. }
  458. .rich {
  459. width: 690rpx;
  460. margin: 0 auto;
  461. padding-bottom: 280rpx;
  462. }
  463. .richText {
  464. width: 690rpx;
  465. margin: 0 auto;
  466. padding: 20rpx;
  467. background-color: #fff;
  468. font-size: 28rpx;
  469. }
  470. uni-rich-text img {
  471. max-width: 100% !important;
  472. }
  473. .rg {
  474. padding-bottom: 50rpx;
  475. .rg_bug {
  476. position: fixed;
  477. bottom: 0;
  478. left: 0;
  479. right: 0;
  480. height: 100rpx;
  481. font-size: 32rpx;
  482. color: #ffffff;
  483. background: $base-line-bg;
  484. text-align: center;
  485. line-height: 100rpx;
  486. }
  487. .rg_imgs {
  488. width: 100%;
  489. margin-bottom: 100rpx;
  490. text-align: center;
  491. }
  492. .rg_size {
  493. width: 100%;
  494. padding: 30rpx;
  495. box-sizing: border-box;
  496. background-color: #ffffff;
  497. border-bottom: 2rpx solid #e9e9e9;
  498. .T {
  499. color: #999999;
  500. font-size: 28rpx;
  501. margin-bottom: 10rpx;
  502. }
  503. .B {
  504. display: flex;
  505. align-items: center;
  506. justify-content: space-between;
  507. text {
  508. color: #333333;
  509. font-size: 28rpx;
  510. }
  511. .B_counter {
  512. width: 180rpx;
  513. height: 64rpx;
  514. }
  515. }
  516. }
  517. .rg_info {
  518. width: 100%;
  519. padding: 30rpx;
  520. box-sizing: border-box;
  521. background-color: #ffffff;
  522. border-bottom: 2rpx solid #e9e9e9;
  523. .B {
  524. width: 100%;
  525. display: flex;
  526. align-items: center;
  527. justify-content: space-between;
  528. text {
  529. &:nth-of-type(1) {
  530. color: #999999;
  531. font-size: 28rpx;
  532. }
  533. &:nth-last-of-type(1) {
  534. color: #f76454;
  535. font-size: 36rpx;
  536. }
  537. }
  538. }
  539. .T {
  540. display: flex;
  541. justify-content: flex-start;
  542. align-items: center;
  543. margin-bottom: 20rpx;
  544. text {
  545. &:nth-of-type(1) {
  546. color: #333333;
  547. font-size: 36rpx;
  548. line-height: 48rpx;
  549. }
  550. &:nth-of-type(2) {
  551. color: #f76454;
  552. font-size: 28rpx;
  553. font-weight: 600;
  554. height: 50rpx;
  555. line-height: 50rpx;
  556. padding: 0 16rpx;
  557. width: max-content;
  558. border-radius: 46rpx;
  559. border: 2rpx solid $base-color;
  560. margin-right: 20rpx;
  561. }
  562. }
  563. }
  564. }
  565. .rg_swiper {
  566. width: 100%;
  567. height: 600rpx;
  568. position: relative;
  569. image,
  570. swiper {
  571. width: 100%;
  572. height: 600rpx;
  573. }
  574. .rg_swiper_page {
  575. height: 48rpx;
  576. border-radius: 48rpx;
  577. background-color: rgba(0, 0, 0, 0.6);
  578. font-size: 28rpx;
  579. line-height: 48rpx;
  580. text {
  581. color: #ffffff;
  582. &:nth-of-type(1) {
  583. font-size: 28rpx;
  584. }
  585. }
  586. padding: 0 36rpx;
  587. position: absolute;
  588. right: 30rpx;
  589. bottom: 30rpx;
  590. }
  591. }
  592. .share_show {
  593. width: 100%;
  594. height: 100vh;
  595. position: fixed;
  596. top: 0;
  597. left: 0;
  598. background-color: rgba(0, 0, 0, 0.7);
  599. z-index: 9999;
  600. .share_con {
  601. width: 80%;
  602. margin: 0 auto;
  603. height: 100%;
  604. z-index: 999999;
  605. .canvas {
  606. width: 540rpx;
  607. height: 960rpx;
  608. position: relative;
  609. #canvas {
  610. width: 540rpx;
  611. height: 960rpx;
  612. position: absolute;
  613. top: 0;
  614. left: 0;
  615. }
  616. .poster_img {
  617. -webkit-touch-callout: default;
  618. width: 100%;
  619. height: 100%;
  620. display: block;
  621. }
  622. img[src=''],
  623. img:not([src]) {
  624. opacity: 0;
  625. }
  626. }
  627. .save_img {
  628. color: #fff;
  629. width: 100%;
  630. text-align: center;
  631. font-size: 40rpx;
  632. margin-top: 20rpx;
  633. }
  634. .iconfont {
  635. position: absolute;
  636. top: 4vh;
  637. right: 25rpx;
  638. color: #dedede;
  639. font-size: 60rpx;
  640. z-index: 999;
  641. }
  642. }
  643. .sub_btn {
  644. margin-top: 76rpx;
  645. }
  646. }
  647. }
  648. .qrimg {
  649. opacity: 0;
  650. }
  651. </style>