时空网前端
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.

675 lines
17 KiB

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