时空网前端
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

692 lines
17 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. <template>
  2. <view>
  3. <skeleton :loading="skeletonLoading" :row="12" :showAvatar="false" :showTitle="true">
  4. <block v-if="isRight(goods_detail)">
  5. <!-- 商品图片轮播 -->
  6. <swiper :current="current" :indicator-dots="goods_detail.banners.length > 1 ? true : false"
  7. :circular="true" class="swiper-box" indicator-active-color="#FE9903">
  8. <swiper-item v-for="(item, index) in goods_detail.banners" :key="item.id">
  9. <image mode="aspectFill" :src="item.cover" style="width: 100%; height: 100%;"
  10. @click="lookImg(index)"></image>
  11. </swiper-item>
  12. </swiper>
  13. <view class="bill-position" @tap='formSubmit()'>
  14. <button class="cu-btn1 margin-left-sm lf-font-28 lf-m-20">
  15. 分享海报
  16. </button>
  17. </view>
  18. <!-- 商品主要信息 -->
  19. <view class="head-info">
  20. <view class="lf-font-40">{{ goods_detail.name }}</view>
  21. <view class="lf-row-between lf-font-24 lf-m-t-30 lf-p-b-20">
  22. <view class="lf-flex price">
  23. <lf-price :price="goods_detail.specs[0].selling_price"></lf-price>
  24. <view class="lf-m-l-20">¥{{ goods_detail.specs[0].original_price }}</view>
  25. <view v-if="goods_detail.specs[0].cost">{{ goods_detail.specs[0].cost }}</view>
  26. </view>
  27. <view class="lf-font-24 lf-text-right">
  28. <view class="lf-color-gray">{{ goods_detail.specs[0].sold_stock_text }}</view>
  29. <view class="lf-color-primary">{{ goods_detail.specs[0].stock_text }}</view>
  30. </view>
  31. </view>
  32. <view class="label-box" v-if="goods_detail.tags && goods_detail.tags.length">
  33. <view class="label-item" v-for="(item, index) in goods_detail.tags" :key="index">{{ item }}
  34. </view>
  35. </view>
  36. </view>
  37. <!-- 地址信息 -->
  38. <view class="address-box">
  39. <view class="lf-font-32 lf-font-bold">适用门店</view>
  40. <view class="lf-m-t-20 lf-row-between">
  41. <view class="lf-flex">
  42. <image mode="aspectFill" class="lf-fle shop-img" :src="goods_detail.store.cover"
  43. v-if="goods_detail.store.cover"></image>
  44. <image mode="aspectFill" class="lf-fle shop-img" src="../../static/center/shop-logo.png"
  45. v-else></image>
  46. <view class="lf-font-32 lf-m-l-20 lf-line-1" style="max-width: 512rpx;">
  47. {{ goods_detail.store.name }}</view>
  48. </view>
  49. <view @click="makePhoneCall(goods_detail.store.tel)">
  50. <text class="lf-iconfont lf-icon-dianhua lf-font-40" style="color: #3A62FF;"></text>
  51. </view>
  52. </view>
  53. <view class="lf-flex lf-m-t-20" @click="openMap">
  54. <view style="width: 60rpx; height: 60rpx;" class="lf-row-center">
  55. <text class="lf-iconfont lf-icon-dizhi lf-font-40" style="color: #555555;"></text>
  56. </view>
  57. <view class="lf-m-l-20 lf-font-28" style="color: #555555;">{{ goods_detail.store.address }}
  58. </view>
  59. </view>
  60. </view>
  61. <!-- 商品详情 -->
  62. <view class="goods-detail">
  63. <view class="lf-font-32 lf-font-bold lf-m-b-20">商品详情</view>
  64. <rich-text :nodes="formatRichText(goods_detail.content)"
  65. v-if="goods_detail.content_type == 'rich_text'"></rich-text>
  66. <image class="goods-img" :src="item" v-for="(item, index) in goods_detail.content" :key="index"
  67. v-if="goods_detail.content_type == 'img'"></image>
  68. </view>
  69. <!-- 修饰专用 -->
  70. <view class="extra"></view>
  71. <!-- 吸底操作按钮 -->
  72. <view class="lf-row-between fixed-bottom">
  73. <view class="lf-flex lf-p-t-10 lf-p-b-10">
  74. <view class="lf-flex-column lf-row-center icon-item"
  75. @click="$url('/pages/index/index', {type: 'switch'})">
  76. <image class="icon-img" src="../../static/center/home.png"></image>
  77. <view class="lf-m-t-1">首页</view>
  78. </view>
  79. <view class="lf-flex-column lf-row-center icon-item"
  80. @click="$url('/pages/contactService/index')">
  81. <image class="icon-img" src="../../static/center/service.png"></image>
  82. <view class="lf-m-t-1">客服</view>
  83. </view>
  84. <view class="lf-flex-column lf-row-center icon-item" @click="switchCollect">
  85. <image class="icon-img" src="../../static/center/collect-active.png" v-if="is_collect">
  86. </image>
  87. <image class="icon-img" src="../../static/center/collect.png" v-else></image>
  88. <view class="lf-m-t-1">{{ is_collect ? '已收藏' : '收藏' }}</view>
  89. </view>
  90. <button class="lf-flex-column lf-row-center icon-item" open-type="share">
  91. <image class="icon-img" src="../../static/center/share.png"></image>
  92. <view class="lf-m-t-1">分享</view>
  93. </button>
  94. </view>
  95. <button class="btn" @click="toAddOrder">立即抢购</button>
  96. </view>
  97. <!-- 回到顶部 -->
  98. <!-- <u-back-top :scroll-top="pageScrollTop" :custom-style="{background: 'rgba(51, 51 51, 0.3)'}" :icon-style="{color: '#ffffff'}"></u-back-top> -->
  99. <u-back-top :scroll-top="pageScrollTop" :custom-style="{background: 'rgba(51, 51 51, 0.3)'}">
  100. </u-back-top>
  101. </block>
  102. </skeleton>
  103. <view class="canvas-box">
  104. <canvas style="width: 375px;height: 667px;position:fixed;top:9999px" canvas-id="mycanvas" />
  105. </view>
  106. <view class='imagePathBox' v-if="maskHidden == true && imagePath" @click="maskHidden = false ">
  107. <image :src="imagePath" class='shengcheng' mode="widthFix"></image>
  108. <button class='baocun' @click.stop="saveBill()">保存相册分享到朋友圈</button>
  109. </view>
  110. </view>
  111. </template>
  112. <script>
  113. export default {
  114. data() {
  115. return {
  116. current: 0, // 轮播下标
  117. goods_id: 0,
  118. goods_detail: {},
  119. is_collect: 0, // 1为当前收藏商品了,0为否
  120. skeletonLoading: true,
  121. base64Img: '',
  122. checkArea: 'Cannot find module',
  123. maskHidden: false,
  124. info: {
  125. avatar: '',
  126. nickname: '',
  127. id: '',
  128. tel: '',
  129. tags: []
  130. },
  131. showLogin: true,
  132. imagePath: '',
  133. userToken: '',
  134. wxCode: '',
  135. onceCode: '',
  136. pt: 1
  137. }
  138. },
  139. computed: {
  140. isRight() {
  141. return function(val) {
  142. return this.$shared.isRight(val);
  143. }
  144. }
  145. },
  146. onLoad(options) {
  147. this.goods_id = options.id;
  148. this.pt = options.pt || 1;
  149. this.getGoodsDetail();
  150. this.getWxCode()
  151. },
  152. methods: {
  153. getWxCode() {
  154. uni.request({
  155. url: 'http://dev-gxsky.com/api/salesman/qrcode', //仅为示例,并非真实接口地址。
  156. data: {
  157. scene: 'route=goods&id=2&pt=2',
  158. page: 'pages/route/index',
  159. width: '2800'
  160. },
  161. method: 'POST',
  162. success: (res) => {
  163. this.wxCode = res.data.data.base_url
  164. if (this.wxCode) {
  165. this.getwxCodeImg()
  166. }
  167. }
  168. });
  169. },
  170. //海报开始
  171. //保存头像
  172. getwxCodeImg() {
  173. var imgSrc = this.wxCode; //base64编码
  174. var save = wx.getFileSystemManager();
  175. var number = Math.random();
  176. save.writeFile({
  177. filePath: wx.env.USER_DATA_PATH + '/pic' + number + '.jpg',
  178. data: imgSrc,
  179. encoding: 'base64',
  180. success: res => {
  181. this.onceCode = wx.env.USER_DATA_PATH + '/pic' + number + '.jpg'
  182. },
  183. fail: err => {
  184. console.log(err)
  185. }
  186. })
  187. },
  188. createNewImg() {
  189. var that = this;
  190. var context = wx.createCanvasContext('mycanvas');
  191. var path = "../../static/images/bill.png";
  192. context.drawImage(path, 0, 0, 375, 667);
  193. //绘制二维码
  194. var number = Math.random();
  195. let wxcode = that.onceCode
  196. context.drawImage(wxcode, 24, 530, 120, 120);
  197. //绘制名字
  198. // context.setFontSize(24);
  199. // context.setFillStyle('#fff');
  200. // context.setTextAlign('center');
  201. // context.fillText(name, 34, 620);
  202. context.stroke();
  203. context.draw();
  204. //将生成好的图片保存到本地,需要延迟一会,绘制期间耗时
  205. setTimeout(function() {
  206. wx.canvasToTempFilePath({
  207. canvasId: 'mycanvas',
  208. success: function(res) {
  209. that.imagePath = res.tempFilePath;
  210. if (that.imagePath) {
  211. that.canvasHidden = true
  212. that.maskHidden = true
  213. }
  214. console.log('海报生成成功')
  215. console.log(res)
  216. console.log('图片链接', that.imagePath)
  217. },
  218. fail: function(res) {
  219. console.log(res);
  220. }
  221. });
  222. }, 200);
  223. },
  224. saveBill() {
  225. var that = this
  226. wx.saveImageToPhotosAlbum({
  227. filePath: that.imagePath,
  228. success(res) {
  229. wx.showModal({
  230. content: '图片已保存到相册,赶紧晒一下吧~',
  231. showCancel: false,
  232. confirmText: '好的',
  233. confirmColor: '#333',
  234. success: function(res) {
  235. if (res.confirm) {
  236. console.log('用户点击确定');
  237. that.maskHidden = false
  238. }
  239. },
  240. fail: function(res) {
  241. that.maskHidden = false
  242. }
  243. })
  244. }
  245. })
  246. },
  247. formSubmit() {
  248. var that = this;
  249. wx.showToast({
  250. title: '生成海报中...',
  251. icon: 'loading',
  252. duration: 1000
  253. });
  254. wx.hideToast()
  255. that.createNewImg()
  256. },
  257. //海报结束
  258. getGoodsDetail() {
  259. let that = this;
  260. this.$http(this.API.API_GOODS_DETAIL, {
  261. goods_id: this.goods_id
  262. }).then(res => {
  263. this.skeletonLoading = false;
  264. this.goods_detail = res.data;
  265. this.is_collect = Boolean(res.data.user.is_collect);
  266. }).catch(err => {
  267. this.skeletonLoading = false;
  268. setTimeout(() => {
  269. that.$toBack();
  270. }, 1000);
  271. })
  272. },
  273. // 切换商品收藏
  274. switchCollect() {
  275. let userInfo = uni.getStorageSync('userinfo') || {};
  276. if (!userInfo.id || !userInfo.nickname || !userInfo.avatar) {
  277. this.$url('/pages/login/index?type=userinfo');
  278. return;
  279. }
  280. this.$http(this.API.API_COLLECT_DEAL, {
  281. goods_id: this.goods_id
  282. }).then(res => {
  283. this.$msg(res.msg);
  284. this.is_collect = Boolean(res.data.user.is_collect);
  285. })
  286. },
  287. // 拨打电话
  288. makePhoneCall(phoneStr) {
  289. uni.makePhoneCall({
  290. phoneNumber: String(phoneStr)
  291. })
  292. },
  293. // 打开地图
  294. openMap() {
  295. // return;
  296. uni.openLocation({
  297. longitude: 108.36637,
  298. latitude: 22.817746,
  299. scale: 18,
  300. name: this.goods_detail.store.address
  301. })
  302. },
  303. // 跳转到确认下单页面
  304. toAddOrder() {
  305. let goods_id = this.goods_detail.id;
  306. let goods_specs_id = this.goods_detail.specs[0].id
  307. this.$url('/pages/order/confirm-order?goods_id=' + goods_id + '&goods_specs_id=' + goods_specs_id +'&pt='+ this.pt);
  308. },
  309. // 预览图片
  310. lookImg(index) {
  311. this.$u.throttle(() => {
  312. let goods_banner = this.goods_detail.banners || [];
  313. let banners = goods_banner.map(item => item.cover);
  314. uni.previewImage({
  315. urls: banners,
  316. current: index
  317. })
  318. }, 200);
  319. },
  320. // 富文本处理
  321. formatRichText(richText) {
  322. if (richText != null) {
  323. let newRichText = richText.replace(/<img[^>]*>/gi, function(match, capture) {
  324. match = match.replace(/style="[^"]+"/gi, '').replace(/style='[^']+'/gi, '');
  325. match = match.replace(/width="[^"]+"/gi, '').replace(/width='[^']+'/gi, '');
  326. match = match.replace(/height="[^"]+"/gi, '').replace(/height='[^']+'/gi, '');
  327. return match;
  328. });
  329. newRichText = newRichText.replace(/style="[^"]+"/gi, function(match, capture) {
  330. match = match.replace(/width:[^;]+;/gi, 'width:100%;').replace(/width:[^;]+;/gi,
  331. 'width:100%;');
  332. return match;
  333. });
  334. newRichText = newRichText.replace(/<br[^>]*\/>/gi, '');
  335. newRichText = newRichText.replace(/\<img/gi,
  336. '<img style="width:100%;height:auto;display:block;margin:10px 0;"');
  337. return newRichText;
  338. } else {
  339. return null;
  340. }
  341. }
  342. },
  343. onShareAppMessage() {
  344. let goods = this.goods_detail;
  345. let title = goods.name;
  346. let imageUrl = goods.share_cover || goods.cover;
  347. let path = '/pages/route/index?route=goods_detail&id=' + goods.id;
  348. return {
  349. title,
  350. path,
  351. imageUrl
  352. }
  353. }
  354. }
  355. </script>
  356. <style>
  357. page {
  358. background-color: #f5f5f5;
  359. overflow-x: hidden;
  360. }
  361. </style>
  362. <style lang="scss" scoped="scoped">
  363. .bill-position {
  364. position: absolute;
  365. top: 0;
  366. right: 0;
  367. }
  368. .cu-btn1 {
  369. position: relative;
  370. display: inline-flex;
  371. align-items: center;
  372. justify-content: center;
  373. box-sizing: border-box;
  374. padding: 0 30rpx;
  375. font-size: 28rpx;
  376. height: 64rpx;
  377. line-height: 1;
  378. text-align: center;
  379. text-decoration: none;
  380. overflow: visible;
  381. margin-left: initial;
  382. transform: translate(0upx, 0upx);
  383. margin-right: initial;
  384. background-color: rgba(0, 0, 0, 0.5);
  385. color: #FFFFFF;
  386. border-radius: 33rpx;
  387. }
  388. .swiper-box {
  389. width: 750rpx;
  390. height: 520rpx;
  391. background-color: #FFFFFF;
  392. }
  393. .head-info {
  394. width: 750rpx;
  395. height: auto;
  396. box-sizing: border-box;
  397. padding: 0 32rpx;
  398. padding-top: 20rpx;
  399. background-color: #FFFFFF;
  400. // .price>view:nth-of-type(1){
  401. // color: #FF0000;
  402. // margin-right: 22rpx;
  403. // font-weight: bold;
  404. // }
  405. .price>view:nth-of-type(1) {
  406. text-decoration: line-through;
  407. color: #777777;
  408. margin-right: 22rpx;
  409. }
  410. .price>view:nth-of-type(2) {
  411. width: max-content;
  412. padding: 0 18rpx;
  413. height: 46rpx;
  414. background-color: #FE9903;
  415. border-radius: 10rpx;
  416. display: flex;
  417. justify-content: center;
  418. align-items: center;
  419. color: #FFFFFF;
  420. }
  421. .label-box {
  422. min-height: 130rpx;
  423. width: 100%;
  424. border-top: 1rpx solid #E5E5E5;
  425. display: flex;
  426. flex-wrap: wrap;
  427. padding: 30rpx 0 10rpx 0;
  428. .label-item {
  429. width: 156rpx;
  430. height: 70rpx;
  431. border-radius: 10rpx;
  432. border: 2rpx solid #FE9903;
  433. display: flex;
  434. justify-content: center;
  435. align-items: center;
  436. font-size: 28rpx;
  437. color: #FE9903;
  438. margin-right: 20rpx;
  439. margin-bottom: 20rpx;
  440. }
  441. }
  442. }
  443. .address-box {
  444. width: 750rpx;
  445. height: auto;
  446. box-sizing: border-box;
  447. background-color: #FFFFFF;
  448. padding: 32rpx;
  449. margin-top: 20rpx;
  450. .shop-img {
  451. width: 60rpx;
  452. height: 60rpx;
  453. border-radius: 50%;
  454. }
  455. }
  456. .goods-detail {
  457. width: 750rpx;
  458. height: auto;
  459. background-color: #FFFFFF;
  460. padding: 32rpx;
  461. box-sizing: border-box;
  462. margin-top: 20rpx;
  463. .goods-img {
  464. width: 100%;
  465. }
  466. }
  467. .extra {
  468. width: 100%;
  469. height: 120rpx;
  470. padding-bottom: constant(safe-area-inset-bottom);
  471. padding-bottom: env(safe-area-inset-bottom);
  472. box-sizing: content-box;
  473. }
  474. .fixed-bottom {
  475. position: fixed;
  476. bottom: 0;
  477. left: 0;
  478. background-color: #FFFFFF;
  479. width: 100%;
  480. height: auto;
  481. padding: 0 32rpx;
  482. border-top: 1rpx solid #e5e5e5;
  483. padding-bottom: constant(safe-area-inset-bottom);
  484. padding-bottom: env(safe-area-inset-bottom);
  485. .icon-item {
  486. margin-right: 16rpx;
  487. background-color: transparent;
  488. margin: 0;
  489. line-height: initial;
  490. font-size: 28rpx;
  491. font-weight: inherit;
  492. margin-right: 10rpx;
  493. padding: 0;
  494. width: 88rpx;
  495. &:first-child {
  496. padding-left: 0;
  497. }
  498. .icon-img {
  499. height: 50rpx;
  500. width: 50rpx;
  501. }
  502. }
  503. .btn {
  504. margin: 0;
  505. padding: 0;
  506. width: 208rpx;
  507. height: 80rpx;
  508. background-color: #FE9903;
  509. color: #FFFFFF;
  510. line-height: 80rpx;
  511. font-size: 32rpx;
  512. border-radius: 42rpx;
  513. }
  514. }
  515. //海报
  516. .bgImg {
  517. display: block;
  518. width: 100%;
  519. height: 366rpx;
  520. }
  521. .mine {
  522. display: block;
  523. text-align: center;
  524. color: #333;
  525. margin-top: 44rpx;
  526. }
  527. .code {
  528. display: block;
  529. text-align: center;
  530. color: #333;
  531. font-size: 76rpx;
  532. font-weight: bold;
  533. margin-top: 30rpx;
  534. }
  535. .who {
  536. display: block;
  537. margin-top: 80rpx;
  538. font-size: 32rpx;
  539. color: #333;
  540. text-align: center;
  541. }
  542. .inputBox {
  543. text-align: center;
  544. margin-top: 44rpx;
  545. }
  546. .input {
  547. text-align: center;
  548. width: 440rpx;
  549. height: 88rpx;
  550. border-radius: 44rpx;
  551. background: #f5f5f5;
  552. font-size: 32rpx;
  553. display: inline-block;
  554. }
  555. .btn {
  556. width: 160rpx;
  557. height: 88rpx;
  558. border-radius: 44rpx;
  559. background: rgba(254, 153, 3, 1);
  560. color: #333;
  561. font-size: 32rpx;
  562. display: inline-block;
  563. line-height: 88rpx;
  564. margin-left: 40rpx;
  565. }
  566. button[class="btn"]::after {
  567. border: 0;
  568. }
  569. .tishi {
  570. display: block;
  571. text-align: center;
  572. color: #999;
  573. margin-top: 30rpx;
  574. }
  575. .shareText {
  576. display: block;
  577. text-align: center;
  578. color: #333;
  579. font-size: 28rpx;
  580. margin-top: 100rpx;
  581. }
  582. .imgBox {
  583. text-align: center;
  584. width: 100%;
  585. margin-top: 60rpx;
  586. padding-bottom: 120rpx;
  587. }
  588. .img {
  589. display: inline-block;
  590. width: 100%;
  591. height: 100%;
  592. }
  593. .m_l {
  594. margin-left: 180rpx;
  595. }
  596. .zfbtn {
  597. display: inline-block;
  598. width: 120rpx;
  599. height: 120rpx;
  600. border-radius: 50%;
  601. background: transparent;
  602. outline: none;
  603. border: 0;
  604. padding: 0;
  605. }
  606. button[class="zfbtn"]::after {
  607. border: 0;
  608. }
  609. button[class="zfbtn m_l"]::after {
  610. border: 0;
  611. }
  612. .imagePathBox {
  613. width: 100%;
  614. height: 100%;
  615. background: rgba(0, 0, 0, 0.7);
  616. position: fixed;
  617. top: 0;
  618. left: 0;
  619. right: 0;
  620. bottom: 0;
  621. z-index: 10;
  622. }
  623. .shengcheng {
  624. width: 80%;
  625. height: 80%;
  626. position: fixed;
  627. top: 50rpx;
  628. left: 50%;
  629. margin-left: -40%;
  630. z-index: 10;
  631. }
  632. .baocun {
  633. display: block;
  634. width: 80%;
  635. height: 80rpx;
  636. padding: 0;
  637. line-height: 80rpx;
  638. text-align: center;
  639. position: fixed;
  640. bottom: 50rpx;
  641. left: 10%;
  642. background: rgba(254, 153, 3, 1);
  643. color: #fff;
  644. font-size: 32rpx;
  645. border-radius: 44rpx;
  646. }
  647. button[class="baocun"]::after {
  648. border: 0;
  649. }
  650. </style>