app-diy-m-goods-list.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. <template>
  2. <view class="app-diy-m-goods-list" :style="{backgroundColor: backgroundColor}">
  3. <view class="m-diy-list__box dir-top-nowrap">
  4. <view class="m-label box-grow-0 dir-left-nowrap cross-center main-between"
  5. :style="{background: 'linear-gradient(to right, ' + mBgColor +', '+ ( mBgType === 'gradient' ? mBgGradientColor: mBgColor) + ')'}">
  6. <view class="box-grow-0 dir-left-nowrap cross-center">
  7. <view class="title" :style="{color: mColor}">{{ mTitle }}</view>
  8. <template v-if="tempData.open_date || after_title">
  9. <view class="desc">{{ timer_text }}</view>
  10. <view v-if="timer" class="time dir-left-nowrap cross-center">
  11. <template v-if="timer.day">
  12. <view class="box main-center cross-center"
  13. :style="{color: mTimeColor,backgroundColor:mTimeBgColor}">{{ timer.day }}
  14. </view>
  15. <view class="colon main-center cross-center">:</view>
  16. </template>
  17. <view class="box main-center cross-center"
  18. :style="{color: mTimeColor,backgroundColor:mTimeBgColor}">{{ timer.hour }}
  19. </view>
  20. <view class="colon main-center cross-center">:</view>
  21. <view class="box main-center cross-center"
  22. :style="{color: mTimeColor,backgroundColor:mTimeBgColor}">{{ timer.min }}
  23. </view>
  24. <view class="colon main-center cross-center">:</view>
  25. <view class="box main-center cross-center"
  26. :style="{color: mTimeColor,backgroundColor:mTimeBgColor}">{{ timer.sec }}
  27. </view>
  28. </view>
  29. <view v-if="after_title" class="desc">{{ after_title }}</view>
  30. </template>
  31. </view>
  32. <view class="m-label-right box-grow-0 dir-left-nowrap cross-center" @click="jumpMore">
  33. <view>更多</view>
  34. <view class="bg"></view>
  35. </view>
  36. </view>
  37. <view class="m-goods" :style="{backgroundColor:mGoodsBgColor}">
  38. <scroll-view v-if="tempData.list && tempData.list.length" scroll-x>
  39. <view class="dir-left-nowrap">
  40. <view class="m-goods-box dir-top-nowrap box-grow-0" v-for="(goods,index) in tempData.list"
  41. :key="index"
  42. @click="jump(goods)">
  43. <!-- 商品名称 -->
  44. <view class="sell-out-pi">
  45. <view class="u-cover-box">
  46. <view class="u-out-dialog" v-if="isShowStock(goods)">
  47. <image class="pic-url"
  48. :src="appSetting.is_use_stock == '1' ? appImg.plugins_out : appSetting.sell_out_pic"
  49. ></image>
  50. </view>
  51. <image class="pic-url box-grow-0 u-cover" v-bind:src="goods.cover_pic"></image>
  52. </view>
  53. </view>
  54. <view class="goods-end"
  55. v-if="showProgressBar || showGoodsName || isShowOriginalPrice(goods) || showGoodsPrice">
  56. <view v-if="showGoodsName" class="goods-name t-omit-two">
  57. <text class="g-tag"
  58. :style="{'background-color': theme.background_o,'color': theme.color}">
  59. {{ signAlone.title }}
  60. </text>
  61. {{ goods.name }}
  62. </view>
  63. <template v-if="sign === 'flash-sale' && showProgressBar">
  64. <view class="u-margin goods-fold flash-sale main-center cross-center"
  65. :style="{'background-color': theme.background}">
  66. {{goods.discount_type == 1 ? goods.min_discount + '折' : '减' + goods.min_discount + '元'}}
  67. </view>
  68. <view class="u-margin goods-progress" :style="{'background-color': theme.background_l}">
  69. <view class="goods-progress-view"
  70. :style="{width: `${goods.percentage}%`,'background-color':`${theme.background}`}"></view>
  71. </view>
  72. <view class="u-margin goods-num">{{ goods.sales }}</view>
  73. </template>
  74. <template v-if="showGoodsPrice">
  75. <view class="u-margin" v-if="isShowMemPrice(goods)">
  76. <app-member-price
  77. :theme="theme"
  78. v-bind:price="goods.level_price"
  79. ></app-member-price>
  80. </view>
  81. <view class="u-margin" v-if="isShowVip(goods)">
  82. <app-sup-vip
  83. v-bind:is_vip_card_user="goods.vip_card_appoint.is_vip_card_user"
  84. v-bind:discount="goods.vip_card_appoint.discount"
  85. ></app-sup-vip>
  86. </view>
  87. <view class="u-margin dir-left-wrap cross-center">
  88. <view class="goods-price" :style="{'color': theme.color}">
  89. {{ goods.price_content }}
  90. </view>
  91. <view v-if="isShowOriginalPrice(goods)"
  92. class="goods-under-line-price">¥{{ goods.original_price }}
  93. </view>
  94. </view>
  95. </template>
  96. </view>
  97. <view class="tag">
  98. <app-image :img-src="goodsTagPicUrl" width="64rpx" height="64rpx"></app-image>
  99. </view>
  100. </view>
  101. </view>
  102. </scroll-view>
  103. <view v-else class="empty cross-center dir-left-wrap">
  104. <view class="empty-bg"></view>
  105. <view class="empty-text">{{ signAlone.empty_title }}</view>
  106. </view>
  107. </view>
  108. </view>
  109. </view>
  110. </template>
  111. <script>
  112. import {mapGetters, mapState} from "vuex";
  113. import appPrice from "../../page-component/goods/app-price.vue";
  114. import appGoodsTimer from "./app-goods-timer.vue";
  115. import appDiyCompositionImage from './app-diy-composition-image';
  116. export default {
  117. name: "app-diy-m-goods-list",
  118. components: {
  119. 'app-price': appPrice,
  120. 'app-goods-timer': appGoodsTimer,
  121. appDiyCompositionImage
  122. },
  123. props: {
  124. list: {
  125. type: Array,
  126. default() {
  127. return [];
  128. }
  129. },
  130. mTitle: String,
  131. mColor: {
  132. type: String,
  133. default: '#FFFFFF',
  134. },
  135. mBgType: {
  136. type: String,
  137. default: 'gradient',
  138. },
  139. mBgColor: {
  140. type: String,
  141. default: '#FF366F',
  142. },
  143. mBgGradientColor: {
  144. type: String,
  145. default: '#FF4242',
  146. },
  147. mTimeColor: {
  148. type: String,
  149. default: '#353535',
  150. },
  151. mTimeBgColor: {
  152. type: String,
  153. default: '#FFFFFF',
  154. },
  155. mGoodsBgColor: {
  156. type: String,
  157. default: '#FFE7E7',
  158. },
  159. showProgressBar: {
  160. type: [Boolean, String],
  161. default: false,
  162. },
  163. showGoodsName: {
  164. type: [Boolean, String],
  165. default: true,
  166. },
  167. showGoodsPrice: {
  168. type: [Boolean, String],
  169. default: true,
  170. },
  171. showGoodsTag: {
  172. type: [Boolean, String],
  173. default: false,
  174. },
  175. customizeGoodsTag: {
  176. type: [Boolean, String],
  177. default: '#FFFFFF',
  178. },
  179. goodsTagPicUrl: {
  180. type: String,
  181. default: '',
  182. },
  183. isUnderLinePrice: {
  184. type: [Boolean, String],
  185. default: true,
  186. },
  187. backgroundColor: {
  188. type: String,
  189. default: '#FFFFFF',
  190. },
  191. sign: String,
  192. theme: Object,
  193. mData: Object,
  194. },
  195. data() {
  196. return {
  197. timer: null,
  198. timer_text: '',
  199. tempData: this.mData,
  200. after_title: '',
  201. mTimeIntegral: null,
  202. };
  203. },
  204. watch: {
  205. mData: {
  206. handler: function (data) {
  207. this.tempData = data;
  208. },
  209. immediate: true,
  210. },
  211. },
  212. computed: {
  213. ...mapState({
  214. appImg: state => state.mallConfig.__wxapp_img.mall,
  215. appSetting: state => state.mallConfig.mall.setting,
  216. platform: function (state) {
  217. return state.gConfig.systemInfo.platform;
  218. }
  219. }),
  220. ...mapGetters('mallConfig', {
  221. getVideo: 'getVideo'
  222. }),
  223. newData() {
  224. return this.list;
  225. },
  226. signAlone() {
  227. switch (this.sign) {
  228. case 'miaosha':
  229. return {
  230. 'sign': this.sign,
  231. 'title': '秒杀',
  232. 'empty_title': '暂无秒杀活动',
  233. 'more_url': '/plugins/miaosha/advance/advance',
  234. }
  235. case 'flash-sale':
  236. return {
  237. 'sign': this.sign,
  238. 'title': '限时抢购',
  239. 'empty_title': '暂无抢购活动',
  240. 'more_url': '/plugins/flash_sale/index/index',
  241. }
  242. default:
  243. break;
  244. }
  245. },
  246. },
  247. mounted() {
  248. this.$nextTick(() => {
  249. setTimeout(() => {
  250. if (this.signAlone.sign === 'miaosha') {
  251. this.sTime();
  252. }
  253. if (this.signAlone.sign === 'flash-sale') {
  254. this.fTime();
  255. }
  256. })
  257. })
  258. },
  259. beforeDestroy() {
  260. clearInterval(this.mTimeIntegral);
  261. },
  262. methods: {
  263. // 是否展示会员价
  264. isShowMemPrice(goods) {
  265. return goods.is_level === 1 && goods.is_negotiable !== 1 ? 1 : 0;
  266. },
  267. // 是否展示超级会员价
  268. isShowVip(goods) {
  269. return goods.vip_card_appoint && goods.vip_card_appoint.discount > 0 && goods.is_negotiable !== 1 ? 1 : 0;
  270. },
  271. // 是否展示售罄
  272. isShowStock(goods) {
  273. return this.appSetting.is_show_stock === 1 && goods.goods_stock === 0 ? 1 : 0;
  274. },
  275. set_time(time_at) {
  276. clearInterval(this.mTimeIntegral);
  277. let timer = new Date(time_at.replace(/-/g, '/'));
  278. this.now_time(timer);
  279. this.mTimeIntegral = setInterval(() => {
  280. this.now_time(timer);
  281. }, 1000);
  282. },
  283. now_time(timer) {
  284. let time = timer.getTime() - new Date().getTime();
  285. if (time < 0) {
  286. clearInterval(this.mTimeIntegral);
  287. }
  288. let day = parseInt(time / 1000 / 60 / 60 / 24);
  289. let hou = parseInt((time / 1000 / 60 / 60) % 24);
  290. let min = parseInt((time / 1000 / 60) % 60);
  291. let sec = parseInt((time / 1000) % 60);
  292. this.timer = {
  293. day: day < 10 ? "0" + day : day,
  294. hour: hou < 10 ? "0" + hou : hou,
  295. min: min < 10 ? "0" + min : min,
  296. sec: sec < 10 ? "0" + sec : sec
  297. };
  298. },
  299. fTime() {
  300. if (this.tempData.activity) {
  301. this.after_title = '结束';
  302. this.set_time(this.tempData.activity.end_at);
  303. } else if (this.tempData.next_activity) {
  304. this.after_title = '开始';
  305. //TODO ddddddd待优化
  306. if (this.tempData.next_activity && this.tempData.next_activity.start_at) {
  307. this.set_time(this.tempData.next_activity.start_at);
  308. }
  309. }
  310. },
  311. sTime() {
  312. let timenow = new Date();//获取当前时间
  313. if ((new Date(this.tempData.open_date)).getDate() != timenow.getDate()) {
  314. this.timer_text = '预告 ' + this.tempData.open_date + ' ' + this.tempData.open_time + '点场';
  315. } else if (this.tempData.open_time != timenow.getHours()) {
  316. this.timer_text = '预告 ' + this.tempData.open_time + '点场';
  317. } else {
  318. this.timer_text = this.tempData.open_time + '点场';
  319. let timelog = this.tempData.date_time * 1000 - timenow.getTime();
  320. this.mTimeIntegral = setInterval(() => {
  321. timelog -= 1000;
  322. if (timelog <= 0) {
  323. clearInterval(this.mTimeIntegral);
  324. return;
  325. }
  326. let hour = parseInt((timelog / 1000 / 60 / 60));
  327. let min = parseInt((timelog / 1000 / 60) % 60);
  328. let sec = parseInt((timelog / 1000) % 60);
  329. this.timer = {
  330. hour: hour < 10 ? "0" + hour : hour,
  331. min: min < 10 ? "0" + min : min,
  332. sec: sec < 10 ? "0" + sec : sec
  333. };
  334. }, 1000);
  335. }
  336. },
  337. jumpMore() {
  338. uni.navigateTo({
  339. url: this.signAlone.more_url,
  340. });
  341. },
  342. jump(data) {
  343. let isNav = false;
  344. // #ifdef MP-ALIPAY || MP-WEIXIN || MP-TOUTIAO
  345. isNav = true;
  346. // #endif
  347. // #ifdef H5 || MP-BAIDU
  348. isNav = false;
  349. // #endif
  350. if (isNav && data.video_url && this.getVideo == 1) {
  351. uni.navigateTo({
  352. url: `/pages/goods/video?goods_id=${id}&sign=${data.sign}`
  353. });
  354. } else {
  355. uni.navigateTo({
  356. url: data.page_url
  357. });
  358. }
  359. },
  360. isShowOriginalPrice(goods) {
  361. return this.isUnderLinePrice && goods.original_price && this.showGoodsPrice && goods.is_negotiable !== 1;
  362. },
  363. }
  364. }
  365. </script>
  366. <style scoped lang="scss">
  367. .app-diy-m-goods-list {
  368. padding: 20px 0;
  369. .m-diy-list__box {
  370. padding: 0 #{20rpx};
  371. }
  372. .m-label {
  373. width: 100%;
  374. height: #{80rpx};
  375. padding: 0 #{24rpx};
  376. border-radius: #{16rpx} #{16rpx} 0 0;
  377. .title {
  378. font-size: #{28rpx};
  379. }
  380. .desc {
  381. color: #ffffff;
  382. font-size: #{24rpx};
  383. margin-left: #{20rpx};
  384. }
  385. .time {
  386. margin-left: #{12rpx};
  387. .colon {
  388. color: #ffffff;
  389. width: #{22rpx};
  390. }
  391. .box {
  392. font-size: 11px;
  393. height: #{36rpx};
  394. width: #{40rpx};
  395. border-radius: #{4rpx};
  396. background: #FFFFFF;
  397. font-weight: bold;
  398. }
  399. }
  400. .m-label-right {
  401. font-size: #{26rpx};
  402. color: #FFFFFF;
  403. .bg {
  404. background-image: url("../../../static/image/icon/arrow-right-white.png");
  405. background-repeat: no-repeat;
  406. background-size: 100% 100%;
  407. width: #{12rpx};
  408. height: #{22rpx};
  409. display: block;
  410. margin-left: #{12rpx};
  411. }
  412. }
  413. }
  414. .m-goods {
  415. padding: #{20rpx} 0 #{20rpx} #{24rpx};
  416. width: 100%;
  417. border-radius: 0 0 #{16rpx} #{16rpx};
  418. .empty {
  419. background: #FFFFFF;
  420. padding: 0 #{56rpx};
  421. border-radius: #{16rpx};
  422. height: #{180rpx};
  423. margin-right: #{20rpx};
  424. .empty-bg {
  425. background-image: url("../../../static/image/icon/empty.png");
  426. background-repeat: no-repeat;
  427. background-size: 100% 100%;
  428. height: #{100rpx};
  429. width: #{100rpx}
  430. }
  431. .empty-text {
  432. margin-left: #{54rpx};
  433. font-size: #{28rpx};
  434. color: #353535;
  435. }
  436. }
  437. .m-goods-box {
  438. margin-right: #{12rpx};
  439. position: relative;
  440. background-color: #FFFFFF;
  441. width: #{260rpx};
  442. border-radius: #{16rpx};
  443. .tag {
  444. position: absolute;
  445. left: 0;
  446. top: 0;
  447. z-index: 10;
  448. width: #{64rpx};
  449. height: #{64rpx};
  450. }
  451. .u-cover-box {
  452. position: relative;
  453. width: #{260rpx};
  454. height: #{260rpx};
  455. }
  456. .u-margin {
  457. margin-top: #{5rpx};
  458. }
  459. .u-out-dialog {
  460. width: #{260rpx};
  461. height: #{260rpx};
  462. position: absolute;
  463. top: 0;
  464. left: 0;
  465. z-index: 10;
  466. background-color: rgba(0, 0, 0, .5);
  467. border-radius: #{16rpx} #{16rpx} 0 0;
  468. }
  469. .pic-url {
  470. height: #{260rpx};
  471. width: 100%;
  472. display: block;
  473. border-radius: #{16rpx} #{16rpx} 0 0;
  474. }
  475. .goods-end {
  476. width: 100%;
  477. padding: #{20rpx} #{14rpx};
  478. .goods-name {
  479. font-size: #{24rpx};
  480. color: #353535;
  481. }
  482. .g-tag {
  483. padding: 0 #{10rpx};
  484. margin-right: #{8rpx};
  485. font-size: $uni-font-size-weak-two;
  486. border-radius: #{28rpx};
  487. text-align: center;
  488. }
  489. .goods-fold {
  490. padding: #{5rpx} #{10rpx};
  491. font-size: #{20rpx};
  492. color: #fff;
  493. line-height: 1;
  494. border-radius: #{14rpx};
  495. margin-right: #{10rpx};
  496. display: inline-block;
  497. }
  498. .goods-progress {
  499. width: 100%;
  500. height: #{20rpx};
  501. border-radius: #{20rpx};
  502. //background: #ff9f9f;
  503. overflow: hidden;
  504. margin-top: #{4rpx};
  505. .goods-progress-view {
  506. width: 50%;
  507. height: 100%;
  508. border-radius: inherit;
  509. //background-color: #ff4544;
  510. }
  511. }
  512. .goods-num {
  513. font-size: #{20rpx};
  514. color: #999999;
  515. }
  516. .goods-price {
  517. font-size: #{28rpx};
  518. }
  519. .goods-under-line-price {
  520. font-size: #{20rpx};
  521. margin-left: #{10rpx};
  522. color: #999999;
  523. text-decoration: line-through;
  524. }
  525. }
  526. }
  527. }
  528. }
  529. </style>