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.

532 lines
13 KiB

4 years ago
  1. <template>
  2. <view class="app">
  3. <pageHeader ref="pageHeader"></pageHeader>
  4. <!-- 轮播图 -->
  5. <mix-swiper :list="data.images"></mix-swiper>
  6. <view class="introduce column">
  7. <text class="title">{{ data.title }}</text>
  8. <view class="price-wrap row">
  9. <mix-price-view :price="data.price" :size="40"></mix-price-view>
  10. <text v-if="data.market_price > data.price" class="m-price">{{ data.market_price }}</text>
  11. <text v-if="loaded && !data.freight_template" class="tag">免邮费</text>
  12. </view>
  13. <view class="bot row">
  14. <text class="fill">销量: {{ data.sales || 0 }}</text>
  15. <text class="fill">库存: {{ data.stock || 0 }}</text>
  16. <text class="fill">浏览量: {{ data.look_num || 0 }}</text>
  17. </view>
  18. </view>
  19. <!-- 分享 -->
  20. <view class="share-wrap row">
  21. <view class="icon row">
  22. <text class="mix-icon icon-iconfontxingxing"></text>
  23. <text>分享</text>
  24. </view>
  25. <text class="tit">发给好友看看吧~</text>
  26. <text class="mix-icon icon-bangzhu1"></text>
  27. <!-- #ifdef MP-WEIXIN -->
  28. <button type="primary" open-type="share">
  29. <view class="btn">
  30. 立即分享
  31. <text class="mix-icon icon-you"></text>
  32. </view>
  33. </button>
  34. <!-- #endif -->
  35. <!-- #ifndef MP-WEIXIN -->
  36. <view class="btn" @click="doAppShare">
  37. 立即分享
  38. <text class="mix-icon icon-you"></text>
  39. </view>
  40. <!-- #endif -->
  41. </view>
  42. <view class="c-list">
  43. <view v-if="data.skuData" class="row b-b" @click="showPopup('skuPopup')">
  44. <text class="tit">购买类型</text>
  45. <view class="con">
  46. <text class="attr">{{ currentSku.name || '' }}</text>
  47. </view>
  48. <text class="mix-icon icon-you"></text>
  49. </view>
  50. <view class="row b-b" @click="navTo('/pages/coupon/receiveCoupon', {login: true})">
  51. <text class="tit">优惠券</text>
  52. <text class="con red">领取优惠券</text>
  53. <text class="mix-icon icon-you"></text>
  54. </view>
  55. <!-- <view class="row b-b">
  56. <text class="tit">促销活动</text>
  57. <view class="con-list fill column">
  58. <text>新人首单送20元无门槛代金券</text>
  59. <text>订单满50减10</text>
  60. <text>订单满100减30</text>
  61. <text>单笔购买满两件免邮费</text>
  62. </view>
  63. </view> -->
  64. <view class="row b-b">
  65. <text class="tit">服务</text>
  66. <view class="con">
  67. <text class="service">7天无理由退换货 ·</text>
  68. <text class="service">假一赔十 ·</text>
  69. </view>
  70. </view>
  71. </view>
  72. <!-- 评价 -->
  73. <view id="rating" class="rating-wrap column" :class="{'no-data': !ratingData.data}">
  74. <view class="e-header" @click="navTo('/pages/rating/rating?id='+data._id)">
  75. <text class="tit">商品评价</text>
  76. <text>({{ ratingData.count || 0 }})</text>
  77. <text class="tip">好评率 {{ data.rating_ratio || 100 }}%</text>
  78. <text class="mix-icon icon-you"></text>
  79. </view>
  80. <rating-item v-if="ratingData.data" :item="ratingData.data"></rating-item>
  81. </view>
  82. <view class="detail-desc">
  83. <view class="d-header center">
  84. <text>图文详情</text>
  85. </view>
  86. <jyf-parser
  87. ref="article"
  88. :html="data.content"
  89. lazy-load
  90. show-with-animation
  91. ></jyf-parser>
  92. <!-- <rich-text :nodes="data.content"></rich-text> -->
  93. </view>
  94. <!-- 底部操作菜单 -->
  95. <bottom-operation :infoData="data" @onOprationClick="onOprationClick"></bottom-operation>
  96. <!-- 规格面板 -->
  97. <sku v-if="data._id" ref="skuPopup" :infoData="data" @setSku="setCurrentSku" @addToCart="addToCart" @buyNow="buyNow"></sku>
  98. <!-- loading -->
  99. <mix-loading v-if="isLoading" :mask="true"></mix-loading>
  100. </view>
  101. </template>
  102. <script>
  103. import jyfParser from '@/components/jyf-parser/jyf-parser.vue'
  104. import pageHeader from './components/detail-page-header'//页面头
  105. import mixSwiper from './components/mix-swiper'//轮播图
  106. import ratingItem from '@/pages/rating/components/rating-item'//评价
  107. import bottomOperation from './components/bottom-operation'//底部栏
  108. import sku from './components/sku'//sku面板
  109. let _anchorList = [];
  110. export default{
  111. components: {
  112. jyfParser,
  113. pageHeader,
  114. mixSwiper,
  115. ratingItem,
  116. bottomOperation,
  117. sku
  118. },
  119. data() {
  120. return {
  121. currentSku: {},
  122. data: {
  123. images: [],
  124. },
  125. ratingData: {}, //评价
  126. };
  127. },
  128. onLoad(options){
  129. this.id = options.id;
  130. this.loadRating(); //加载评价
  131. },
  132. onShow() {
  133. this.loadData();
  134. },
  135. onPageScroll(e) {
  136. this.$refs.pageHeader && this.$refs.pageHeader.pageScroll(e);
  137. },
  138. // #ifdef MP-WEIXIN
  139. onShareAppMessage(res) {
  140. return {
  141. title: this.data.title,
  142. path: '/pages/product/detail?id='+this.data._id,
  143. imageUrl: this.data.thumb
  144. }
  145. },
  146. // #endif
  147. methods:{
  148. async loadData(){
  149. const res = await this.$request('product', 'getDetail', {
  150. id: this.id
  151. })
  152. if(res.status === 0){
  153. this.$util.msg(res.msg || '产品不存在或已下架');
  154. setTimeout(()=>{
  155. uni.navigateBack();
  156. }, 1000)
  157. return;
  158. }
  159. const data = res.data;
  160. data.content = data.content.replace(/img src="/g, 'img style="display:block;width:100%;height:auto" src="');
  161. this.data = data;
  162. this.$nextTick(()=>{
  163. this.calcAnchor();//计算锚点参数
  164. })
  165. //添加浏览历史
  166. this.addProductHistory();
  167. },
  168. //加载评价
  169. async loadRating(){
  170. const res = await this.$request('rating', 'getDetailRating', {
  171. product_id: this.id,
  172. })
  173. this.ratingData = res;
  174. this.$nextTick(()=>{
  175. this.calcAnchor();//计算锚点参数
  176. })
  177. },
  178. //加入购物车
  179. addToCart(){
  180. this.$util.throttle(async ()=>{
  181. const data = this.getConfirmData();
  182. if(!data){
  183. return;
  184. }
  185. const res = await this.$request('cart', 'add', data, {
  186. showLoading: true,
  187. login: true
  188. })
  189. if(!res){
  190. return;
  191. }
  192. this.log(res)
  193. this.$util.msg(res.msg);
  194. if(res.status === 1){
  195. this.hidePopup('skuPopup');
  196. this.$store.dispatch('getCartCount');//更新购物车数量
  197. uni.$emit('refreshCart');//更新购物车
  198. }
  199. })
  200. },
  201. //立即购买
  202. buyNow(){
  203. const data = this.getConfirmData();
  204. if(!data){
  205. return;
  206. }
  207. this.hidePopup('skuPopup');
  208. this.navTo('/pages/order/createOrder?data='+JSON.stringify(data), {
  209. login: true
  210. })
  211. },
  212. //设置当前选择sku
  213. setCurrentSku(data){
  214. this.currentSku = data;
  215. },
  216. //获取当前sku 如果没有sku返回默认规格
  217. getConfirmData(){
  218. const sku = this.currentSku._id ? this.currentSku: this.data.sku[0];
  219. if(sku.stock <= 0 || this.data.stock <= 0){
  220. this.$util.msg('库存不足');
  221. return false;
  222. }
  223. const data = {
  224. product_id: this.data._id,
  225. number: this.$refs.skuPopup.buyNumber || 1,
  226. sku: this.currentSku._id ? this.currentSku: this.data.sku[0]
  227. }
  228. return data;
  229. },
  230. //计算锚点参数
  231. async calcAnchor(){
  232. const size = await new Promise(res => {
  233. uni.createSelectorQuery().select('#rating').boundingClientRect(data => {
  234. res(data);
  235. }).exec();
  236. });
  237. const headerHeight = this.systemInfo.statusBarHeight + this.systemInfo.navigationBarHeight;
  238. const a1 = (size ? size.top : 0) - headerHeight;
  239. const a2 = (size ? size.bottom : 0) + uni.upx2px(12) - headerHeight;
  240. this.$refs.pageHeader.anchorList[1].top = a1;
  241. this.$refs.pageHeader.anchorList[2].top = a2;
  242. _anchorList = [0, a1, a2];
  243. },
  244. //添加浏览历史
  245. addProductHistory(){
  246. const data = this.data;
  247. let list = uni.getStorageSync('productHistory');
  248. if(!list){
  249. list = [];
  250. }
  251. const index = list.findIndex(item=> item.id === data._id);
  252. if(index !== -1){
  253. list.splice(index, 1);
  254. }
  255. list.unshift({
  256. id: data._id,
  257. thumb: data.thumb
  258. })
  259. uni.setStorageSync('productHistory', list);
  260. },
  261. //删除当前浏览历史
  262. delHistory(){
  263. let list = uni.getStorageSync('productHistory');
  264. if(!list){
  265. return;
  266. }
  267. const index = list.findIndex(item=> item._id === data._id);
  268. if(index === -1){
  269. return;
  270. }
  271. list.splice(index, 1);
  272. uni.setStorageSync('productHistory', list);
  273. },
  274. showPopup(key, type){
  275. this.$refs[key].open(type);
  276. },
  277. onOprationClick(type){
  278. this.showPopup('skuPopup', type);
  279. },
  280. doAppShare(){
  281. this.$util.throttle(async ()=>{
  282. const data = {
  283. provider: "weixin",
  284. scene: 'WXSceneSession',
  285. type: 5,
  286. imageUrl: this.data.thumb,
  287. title: this.data.title,
  288. miniProgram: {
  289. id: 'gh_3dada2e0f833',
  290. path: '/pages/product/detail?id='+this.data._id,
  291. type: 0,
  292. webUrl: 'http://guoyunnet.com'
  293. },
  294. success: res=> {
  295. console.log("success:" + JSON.stringify(res));
  296. },
  297. fail: err=> {
  298. console.log("fail:" + JSON.stringify(err));
  299. }
  300. }
  301. uni.share(data);
  302. })
  303. },
  304. },
  305. }
  306. </script>
  307. <style>
  308. page{
  309. background-color: #f5f5f5;
  310. }
  311. </style>
  312. <style scoped lang='scss'>
  313. .app{
  314. padding-bottom: constant(safe-area-inset-bottom);
  315. padding-bottom: env(safe-area-inset-bottom);
  316. &:after{
  317. content: '';
  318. display: block;
  319. width: 100%;
  320. height: 100rpx;
  321. }
  322. }
  323. /* 标题简介 */
  324. .introduce{
  325. background: #fff;
  326. padding: 20rpx 30rpx;
  327. .title{
  328. min-height: 44rpx;
  329. font-size: 32rpx;
  330. color: #333;
  331. line-height: 44rpx;
  332. font-weight: 700;
  333. }
  334. .price-wrap{
  335. min-height: 40rpx;
  336. margin-top: 28rpx;
  337. font-size: 26rpx;
  338. }
  339. .m-price{
  340. margin-left: 10rpx;
  341. margin-right: 16rpx;
  342. color: #999;
  343. text-decoration: line-through;
  344. }
  345. .tag{
  346. transform: translateY(4rpx);
  347. padding: 0 10rpx;
  348. margin-left: 8rpx;
  349. background: $base-color;
  350. font-size: 20rpx;
  351. color: #fff;
  352. line-height: 32rpx;
  353. border-radius: 4rpx;
  354. position: relative;
  355. bottom: 8rpx;
  356. }
  357. .bot{
  358. padding: 28rpx 0 10rpx 4rpx;
  359. font-size: 24rpx;
  360. color: #999;
  361. }
  362. }
  363. /* 分享 */
  364. .share-wrap{
  365. background: linear-gradient(to right, #fdf5f6, #fbebf6);
  366. height: 76rpx;
  367. padding: 0 30rpx;
  368. color: $base-color;
  369. .icon{
  370. width: 90rpx;
  371. height: 30rpx;
  372. border: 1px solid $base-color;
  373. border-radius: 4rpx;
  374. position: relative;
  375. overflow: hidden;
  376. font-size: 22rpx;
  377. &:after{
  378. position:absolute;
  379. left: -20rpx;
  380. top: -12rpx;
  381. content: '';
  382. width: 50rpx;
  383. height: 50rpx;
  384. border-radius: 50%;
  385. background-color: $base-color;
  386. }
  387. }
  388. .icon-iconfontxingxing{
  389. position:relative;
  390. z-index: 1;
  391. font-size: 24rpx;
  392. margin-left: 2rpx;
  393. margin-right: 10rpx;
  394. color: #fff;
  395. }
  396. .tit{
  397. flex: 1;
  398. font-size: 26rpx;
  399. color: #666;
  400. margin-left:14rpx;
  401. }
  402. .icon-bangzhu1{
  403. padding: 10rpx;
  404. font-size: 30rpx;
  405. display: none;
  406. }
  407. .btn{
  408. padding-left: 20rpx;
  409. font-size: 24rpx;
  410. color: $base-color;
  411. }
  412. .icon-you{
  413. font-size: 22rpx;
  414. margin-left: 4rpx;
  415. color: $base-color;
  416. }
  417. }
  418. .c-list{
  419. font-size: 26rpx;
  420. color: #888;
  421. background: #fff;
  422. .row{
  423. min-height: 82rpx;
  424. padding: 16rpx 30rpx;
  425. position:relative;
  426. &:after{
  427. border-color: #eaeaea;
  428. }
  429. &:last-child:after{
  430. border: 0;
  431. }
  432. }
  433. .tit{
  434. width: 140rpx;
  435. }
  436. .con{
  437. flex: 1;
  438. color: #333;
  439. .attr{
  440. margin-right: 10rpx;
  441. }
  442. .service{
  443. margin-right: 30rpx;
  444. &:last-child{
  445. margin: 0;
  446. }
  447. }
  448. }
  449. .con-list{
  450. color: #333;
  451. text{
  452. line-height: 40rpx;
  453. }
  454. }
  455. .red{
  456. color: $base-color;
  457. }
  458. .icon-you{
  459. font-size: 24rpx;
  460. color: #999;
  461. }
  462. }
  463. /* 评价 */
  464. .rating-wrap{
  465. padding: 20rpx 30rpx 0rpx;
  466. background: #fff;
  467. margin-top: 12rpx;
  468. &.no-data{
  469. padding: 10rpx 30rpx 10rpx;
  470. }
  471. .e-header{
  472. display: flex;
  473. align-items: center;
  474. height: 70rpx;
  475. font-size: 28rpx;
  476. color: #333;
  477. }
  478. .tit{
  479. font-size: 32rpx;
  480. color: #333;
  481. font-weight: 700;
  482. margin-right: 4rpx;
  483. }
  484. .tip{
  485. flex: 1;
  486. font-size: 26rpx;
  487. color: #999;
  488. text-align: right;
  489. }
  490. .icon-you{
  491. margin-left: 8rpx;
  492. font-size: 24rpx;
  493. color: #999;
  494. }
  495. .mix-rating-item::after{
  496. border: 0;
  497. }
  498. }
  499. /* 详情 */
  500. .detail-desc{
  501. margin-top: 12rpx;
  502. background: #fff;
  503. .d-header{
  504. height: 80rpx;
  505. font-size: 30rpx;
  506. color: #333;
  507. text{
  508. margin: 0 20rpx;
  509. }
  510. &:before, &:after{
  511. content: '';
  512. width: 60rpx;
  513. border-bottom: 1px solid #ccc;
  514. }
  515. }
  516. }
  517. </style>