product.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. <template>
  2. <view class="container">
  3. <!-- 轮播图 -->
  4. <top-swiper :imgList="imgList"></top-swiper>
  5. <!-- 标题 -->
  6. <product-content :goodsObjact="goodsObjact"></product-content>
  7. <!-- 规格信息 -->
  8. <fresh-detail @click='specOPne' :specSelected="specSelected"></fresh-detail>
  9. <!-- 图文详情 -->
  10. <content-text :description="description"></content-text>
  11. <!-- 底部高度撑开 -->
  12. <view class="contentBottomHeight"></view>
  13. <!-- 底部操作菜单 -->
  14. <product-bottom :systemStore='system_store' @buy="buy" :goodsObjact="goodsObjact" :goodsid="goodsid"
  15. @specOPne="specOPne"></product-bottom>
  16. <!-- 规格-模态层弹窗 -->
  17. <view class="popup spec" :class="specClass" @touchmove.stop.prevent="stopPrevent" @click="toggleSpec">
  18. <!-- 遮罩层 -->
  19. <view class="mask"></view>
  20. <view class="layer attr-content" @click.stop="stopPrevent">
  21. <image class="close" src="../../static/icon/goodsExit.png" mode="aspectFit" @click="toggleSpec">
  22. </image>
  23. <view class="a-t">
  24. <image @click="showImg(true)" :src="actionGoodsType.actionImage"></image>
  25. <view class="right">
  26. <text class="name">{{ goodsObjact.store_name }}</text>
  27. <text class="price">
  28. <text class="font-size-base margin-r-10">
  29. ¥
  30. </text>
  31. <text>
  32. {{ actionGoodsType.actionPrice*goodsNumber}}
  33. </text>
  34. <text class="font-color-gray font-size-sm margin-l-10">
  35. 库存:{{actionGoodsType.goodsStore}}
  36. </text>
  37. </text>
  38. </view>
  39. </view>
  40. <view v-if="many==2" v-for="(item, index) in specList" :key="index" class="attr-list">
  41. <text>{{ item.attr_name }}</text>
  42. <view class="item-list flex">
  43. <text v-for="(childItem, childIndex) in item.attr_value" :key="childIndex" class="tit"
  44. :class="{ selected: childItem.check }" @click="selectSpec(childItem, item, index)">
  45. {{ childItem.attr }}
  46. </text>
  47. </view>
  48. </view>
  49. <view v-if="many==1" class="attr-list">
  50. <text>规格</text>
  51. <view class="item-list flex">
  52. <text class="tit selected">
  53. 默认
  54. </text>
  55. </view>
  56. </view>
  57. <view class="attr-list">
  58. <text>购买数量</text>
  59. <view class="item-list">
  60. <uni-number-box class="step" :isMin="true" v-model="goodsNumber"
  61. :max="actionGoodsType.goodsNumberMax"></uni-number-box>
  62. </view>
  63. </view>
  64. <button class="btn" @click.stop="buy" v-show="buys_show">完成</button>
  65. <button class="btn" style="background-color: #999999;" v-show="buys_shows">售罄</button>
  66. </view>
  67. </view>
  68. </view>
  69. </template>
  70. <script>
  71. import {
  72. goodsDetail,
  73. cartAdd
  74. } from '@/api/product.js';
  75. import {
  76. mapState
  77. } from 'vuex';
  78. import store from '@/store/index.js';
  79. import {
  80. saveUrl
  81. } from '@/utils/loginUtils.js';
  82. // #ifdef H5
  83. import {
  84. weixindata
  85. } from '@/utils/wxAuthorized';
  86. // #endif
  87. // 头部轮播图
  88. import topSwiper from './common/topSwiper.vue';
  89. // 标题
  90. import productContent from './common/productContent.vue';
  91. // 规格信息
  92. import freshDetail from './common/freshDetail.vue';
  93. // 图文详情
  94. import contentText from './common/contentText.vue';
  95. // 底部按钮
  96. import productBottom from './common/productBottom.vue';
  97. export default {
  98. components: {
  99. topSwiper,
  100. productContent,
  101. freshDetail,
  102. contentText,
  103. productBottom,
  104. },
  105. data() {
  106. return {
  107. specList: [],
  108. buys_show: true,
  109. buys_shows: false,
  110. specSelected: [], //选中的分类
  111. specClass: 'none', //显示隐藏弹窗
  112. many: 1, //1是单规格 2是多规格
  113. reply: '', //评论
  114. list: '', //商品详情的数据
  115. type: 1, //默认支付方式add为
  116. goodsType: 0,
  117. goodsNumber: 1, //购买数量
  118. goodsid: '', //商品id
  119. description: '', //商品描述
  120. goodsObjact: {
  121. percent: 1
  122. }, //保存商品数据
  123. //图片循环
  124. imgList: [],
  125. // 对比对象
  126. good_list: '', //猜你喜欢列表
  127. userInfo: '',
  128. // 选中商品的分类
  129. actionGoodsType: {
  130. goodsNumberMax: 0, //最大可购买数量
  131. goodsStore: 0, //选中库存
  132. actionImage: '', //默认选中图片
  133. actionPrice: 0, //默认选中商品价格
  134. day_deducted: 0, //选中商品的服务费用
  135. deal_price_num: 0, //选中商品的扣款天数
  136. uniqueId: '', //选中规格商品id
  137. },
  138. system_store: {}, //商家信息
  139. };
  140. },
  141. async onLoad(options) {
  142. let obj = this;
  143. obj.userInfo = uni.getStorageSync('userInfo');
  144. //保存商品id
  145. this.goodsid = options.id;
  146. this.goodsType = options.type;
  147. // 判断有无人邀请
  148. if (options.spread) {
  149. // 存储邀请人
  150. uni.setStorageSync('spread', options.spread);
  151. }
  152. saveUrl();
  153. this.goodsDetail();
  154. // 注册邀请信息
  155. // #ifdef H5
  156. let bool = uni.getStorageSync('weichatBrowser') || '';
  157. if (bool) {
  158. weixindata();
  159. }
  160. // #endif
  161. },
  162. computed: {
  163. ...mapState(['weichatObj', 'baseURL', 'urlFile']),
  164. ...mapState('shop', ['shopDetail'])
  165. },
  166. // 分享
  167. onShareAppMessage(options) {
  168. // 设置菜单中的转发按钮触发转发事件时的转发内容
  169. let pages = getCurrentPages(); //获取加载的页面
  170. let currentPage = pages[pages.length - 1]; //获取当前页面的对象
  171. let url = currentPage.route; //当前页面url
  172. let item = currentPage.options; //如果要获取url中所带的参数可以查看options
  173. let shareObj = {
  174. title: this.goodsObjact.store_name + ' 价格:' + this.goodsObjact.price, // 默认是小程序的名称(可以写slogan等)
  175. path: url + '?id=' + item.id + '&spread=' + this.userInfo.uid, // 默认是当前页面,必须是以‘/’开头的完整路径
  176. imageUrl: this.goodsObjact.image,
  177. success: function(res) {
  178. // 转发成功之后的回调
  179. if (res.errMsg == 'shareAppMessage:ok') {}
  180. },
  181. fail: function() {
  182. // 转发失败之后的回调
  183. if (res.errMsg == 'shareAppMessage:fail cancel') {
  184. // 用户取消转发
  185. } else if (res.errMsg == 'shareAppMessage:fail') {
  186. // 转发失败,其中 detail message 为详细失败信息
  187. }
  188. }
  189. };
  190. return shareObj;
  191. },
  192. methods: {
  193. showImg(bool) {
  194. uni.navigateTo({
  195. url: './showImg?imgs=' + JSON.stringify([this.actionGoodsType.actionImage]) + '&current=' + 0,
  196. animationType: "fade-in"
  197. })
  198. },
  199. //选择规格
  200. selectSpec(item, arr, ind) {
  201. arr.attr_value.forEach(function(e) {
  202. e.check = false;
  203. });
  204. item.check = true;
  205. let obj = this;
  206. obj.specSelected[ind] = item.attr;
  207. // 重置触发修改事件
  208. obj.specSelected = obj.specSelected.map((e) => {
  209. return e
  210. });
  211. let str = obj.specSelected.join(',');
  212. let goodItemAction = obj.actionGoodsType
  213. // 获取当前选中的对象
  214. if (obj.productValue[str]) {
  215. obj.buys_show = true;
  216. obj.buys_shows = false;
  217. goodItemAction.actionPrice = obj.productValue[str].price;
  218. goodItemAction.goodsNumberMax = obj.productValue[str].stock;
  219. goodItemAction.actionImage = obj.productValue[str].image;
  220. goodItemAction.uniqueId = obj.productValue[str].unique;
  221. goodItemAction.goodsStore = obj.productValue[str].stock;
  222. console.log(goodItemAction.goodsStore, 'kc')
  223. goodItemAction.day_deducted = obj.productValue[str].day_deducted; //每天扣款
  224. if (+obj.productValue[str].day_deducted) {
  225. goodItemAction.deal_price_num = Math.ceil((obj.productValue[str].deal_price - obj.productValue[str]
  226. .price) / obj.productValue[str].day_deducted); //每天扣款
  227. } else {
  228. goodItemAction.deal_price_num = 0
  229. }
  230. // 扣款天数
  231. } else {
  232. (obj.buys_show = false), (obj.buys_shows = true);
  233. }
  234. if (goodItemAction.goodsStore == 0) {
  235. obj.buys_show = false;
  236. obj.buys_shows = true;
  237. }
  238. // obj.specSelected[ind] = item.attr;
  239. },
  240. // 打開彈窗
  241. specOPne(type = 1) {
  242. let obj = this;
  243. obj.type = type;
  244. obj.specClass = 'show';
  245. },
  246. //规格弹窗开关
  247. toggleSpec(str) {
  248. if (this.specClass === 'show') {
  249. this.specClass = 'hide';
  250. setTimeout(() => {
  251. this.specClass = 'none';
  252. }, 250);
  253. } else if (this.specClass === 'none') {
  254. this.specClass = 'show';
  255. }
  256. // 保存当前购买类型
  257. this.type = str;
  258. },
  259. //领取优惠券
  260. Getcoupon() {
  261. uni.navigateTo({
  262. url: '/pages/coupon/getcoupon'
  263. });
  264. },
  265. //详情页
  266. navToDetailPage(item) {
  267. let id = item.id;
  268. uni.navigateTo({
  269. url: '/pages/product/product?id=' + id
  270. });
  271. },
  272. // 跳转页面
  273. // navTo(url) {
  274. // uni.navigateTo({
  275. // url: '/pages/product/reply?id=' + this.goodsid
  276. // });
  277. // },
  278. // 获取商品详情
  279. goodsDetail() {
  280. let obj = this;
  281. // 获取普通商品信息
  282. goodsDetail({}, this.goodsid).then(function({
  283. data
  284. }) {
  285. obj.list = data;
  286. //保存猜你喜欢列表
  287. obj.good_list = data.good_list;
  288. //保存评论列表
  289. obj.reply = data.reply;
  290. let goods = data.storeInfo;
  291. obj.goodsObjact = goods;
  292. //保存商家信息
  293. obj.system_store = data.system_store;
  294. if (obj.goodsObjact.description != null) {
  295. obj.description = obj.goodsObjact.description.replace(/\<img/gi, '<img class="rich-img"');
  296. } //小程序商品详情图超出屏幕问题
  297. obj.imgList = goods.slider_image; //保存轮播图
  298. obj.specList = data.productAttr; //保存分类列表
  299. if (obj.specList.length > 0) {
  300. obj.many = 2; //多规格
  301. obj.specList = data.productAttr; //保存产品属性
  302. obj.productValue = data.productValue; //保存属性值
  303. obj.specSelected = []; //初始化默认选择对象
  304. for (let i = 0; i < obj.specList.length; i++) {
  305. // 设置默认数据
  306. let attrValue = obj.specList[i].attr_value[0];
  307. attrValue.check = true;
  308. obj.specSelected.push(attrValue.attr);
  309. }
  310. let str = obj.specSelected.join(',');
  311. let goodItemAction = obj.actionGoodsType
  312. // 设置默认值
  313. goodItemAction.actionPrice = obj.productValue[str].price;
  314. goodItemAction.goodsNumberMax = obj.productValue[str].stock;
  315. goodItemAction.actionImage = obj.productValue[str].image;
  316. goodItemAction.uniqueId = obj.productValue[str].unique;
  317. goodItemAction.goodsStore = obj.productValue[str].stock;
  318. goodItemAction.day_deducted = obj.productValue[str].day_deducted; //每天扣款
  319. goodItemAction.deal_price_num = Math.ceil((obj.productValue[str].deal_price - obj
  320. .productValue[str].price) / obj.productValue[str].day_deducted); //每天扣款
  321. // 扣款天数
  322. } else {
  323. obj.many = 1; //单规格
  324. obj.actionGoodsType.actionPrice = goods.price; //保存默认选中商品价格
  325. obj.actionGoodsType.actionImage = goods.image_base; //保存默认选中商品图片
  326. obj.actionGoodsType.goodsNumberMax = goods.stock; //保存默认选中最大可购买商品数量
  327. obj.actionGoodsType.goodsStore = goods.stock;
  328. }
  329. });
  330. },
  331. // 立即购买
  332. buy() {
  333. let obj = this;
  334. // 创建传值对象
  335. let data = {
  336. cartNum: obj.goodsNumber, //商品数量
  337. productId: obj.goodsid, //商品编号
  338. uniqueId: obj.actionGoodsType.uniqueId,
  339. };
  340. // 判断是否需要读取非默认商家id
  341. // 判断是否加入购物车
  342. if (obj.type == 2) {
  343. data.new = 0;
  344. } else {
  345. data.new = 1;
  346. }
  347. uni.showLoading({
  348. title: '请求中',
  349. mask: true
  350. });
  351. cartAdd(data)
  352. .then(function(e) {
  353. uni.hideLoading()
  354. let da = e.data;
  355. // 不是购物车跳转支付页面
  356. if (obj.type == 1) {
  357. // 跳转到支付页
  358. let url = '/pages/order/createOrder?id=' + da.cartId
  359. uni.navigateTo({
  360. url: url,
  361. fail(res) {
  362. console.log(res, '错误')
  363. }
  364. });
  365. }
  366. // 购物车不跳转购物车页面
  367. if (obj.type == 2) {
  368. uni.showToast({
  369. title: '成功加入购物车',
  370. type: 'top',
  371. duration: 2000,
  372. icon: 'none'
  373. });
  374. }
  375. obj.toggleSpec()
  376. })
  377. .catch(e => {
  378. console.log(e);
  379. });
  380. },
  381. // 阻止触发上级事件
  382. stopPrevent() {}
  383. }
  384. };
  385. </script>
  386. <style lang="scss">
  387. .alertImgBox {
  388. display: flex;
  389. justify-content: center;
  390. align-items: center;
  391. width: 100%;
  392. .alertImg {
  393. width: 730rpx;
  394. }
  395. }
  396. /* 弹出层 */
  397. .popup {
  398. position: fixed;
  399. left: 0;
  400. top: 0;
  401. right: 0;
  402. bottom: 0;
  403. z-index: 99;
  404. &.show {
  405. display: block;
  406. .mask {
  407. animation: showPopup 0.2s linear both;
  408. }
  409. .layer {
  410. animation: showLayer 0.2s linear both;
  411. }
  412. }
  413. &.hide {
  414. .mask {
  415. animation: hidePopup 0.2s linear both;
  416. }
  417. .layer {
  418. animation: hideLayer 0.2s linear both;
  419. }
  420. }
  421. &.none {
  422. display: none;
  423. }
  424. .mask {
  425. position: fixed;
  426. top: 0;
  427. width: 100%;
  428. height: 100%;
  429. z-index: 1;
  430. background-color: rgba(0, 0, 0, 0.4);
  431. }
  432. .layer {
  433. position: fixed;
  434. z-index: 99;
  435. bottom: 0;
  436. width: 100%;
  437. min-height: 35vh;
  438. border-radius: 10rpx 10rpx 0 0;
  439. background-color: #fff;
  440. .btn {
  441. height: 76rpx;
  442. line-height: 66rpx;
  443. border-radius: 100rpx;
  444. background: linear-gradient(90deg, #3C82E6, #5395F5);
  445. font-size: $font-base + 2rpx;
  446. color: #FFF;
  447. margin: 30rpx auto 20rpx;
  448. }
  449. }
  450. @keyframes showPopup {
  451. 0% {
  452. opacity: 0;
  453. }
  454. 100% {
  455. opacity: 1;
  456. }
  457. }
  458. @keyframes hidePopup {
  459. 0% {
  460. opacity: 1;
  461. }
  462. 100% {
  463. opacity: 0;
  464. }
  465. }
  466. @keyframes showLayer {
  467. 0% {
  468. transform: translateY(120%);
  469. }
  470. 100% {
  471. transform: translateY(0%);
  472. }
  473. }
  474. @keyframes hideLayer {
  475. 0% {
  476. transform: translateY(0);
  477. }
  478. 100% {
  479. transform: translateY(120%);
  480. }
  481. }
  482. }
  483. /* 规格选择弹窗 */
  484. .attr-content {
  485. padding: 50rpx;
  486. position: relative;
  487. .close {
  488. width: 36rpx;
  489. height: 36rpx;
  490. position: absolute;
  491. top: 50rpx;
  492. right: 50rpx;
  493. }
  494. .a-t {
  495. display: flex;
  496. image {
  497. border: 1px solid $page-color-light;
  498. width: 170rpx;
  499. height: 170rpx;
  500. flex-shrink: 0;
  501. border-radius: 8rpx;
  502. }
  503. .right {
  504. display: flex;
  505. flex-direction: column;
  506. padding-left: 24rpx;
  507. font-size: $font-sm + 2rpx;
  508. color: $font-color-base;
  509. line-height: 42rpx;
  510. width: 75%;
  511. .price {
  512. font-size: 44rpx;
  513. color: $color-red;
  514. margin: 10rpx 0rpx;
  515. flex-grow: 1;
  516. width: 100%;
  517. }
  518. .name {
  519. font-size: 32rpx;
  520. color: $font-color-dark;
  521. height: 50rpx;
  522. overflow: hidden;
  523. text-overflow: ellipsis;
  524. white-space: nowrap;
  525. display: block;
  526. }
  527. .selected-text {
  528. margin-right: 10rpx;
  529. }
  530. }
  531. }
  532. .attr-list {
  533. display: flex;
  534. flex-direction: column;
  535. font-size: $font-base + 2rpx;
  536. color: $font-color-base;
  537. padding-top: 30rpx;
  538. }
  539. .item-list {
  540. padding: 20rpx 0 0;
  541. display: flex;
  542. flex-wrap: wrap;
  543. justify-content: flex-start;
  544. .tit {
  545. min-width: 200rpx;
  546. display: flex;
  547. align-items: center;
  548. justify-content: center;
  549. background: #eee;
  550. margin-bottom: 20rpx;
  551. margin-right: 16rpx;
  552. border-radius: 5rpx;
  553. height: 60rpx;
  554. padding: 0 20rpx;
  555. font-size: $font-base;
  556. color: $font-color-dark;
  557. }
  558. .selected {
  559. background: #E1F4EA;
  560. color: $color-green;
  561. border: 1px solid $color-green;
  562. }
  563. }
  564. }
  565. //默认商品底部高度
  566. .goodsBottom {
  567. height: 160rpx;
  568. }
  569. page {
  570. background: #f0f0f0;
  571. }
  572. //秒杀、拼团底部高度
  573. .contentBottomHeight {
  574. height: 110rpx;
  575. }
  576. //默认商品底部高度
  577. .goodsBottom {
  578. height: 160rpx;
  579. }
  580. /deep/ .iconenter {
  581. font-size: $font-base + 2rpx;
  582. color: #888;
  583. }
  584. /deep/ .con_image {
  585. width: 130rpx;
  586. height: 130rpx;
  587. display: inline-block;
  588. padding: 15rpx;
  589. image {
  590. width: 100%;
  591. height: 100%;
  592. }
  593. }
  594. /* 商品详情中限制图片大小 */
  595. /deep/ .rich-img {
  596. width: 100% !important;
  597. height: auto;
  598. }
  599. </style>