index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <template>
  2. <app-layout>
  3. <view :class="[themeObject.back, 'f-switch']">
  4. <template v-if="!is_no_now">
  5. <image v-if="is_switch" src="../image/processing.png"></image>
  6. <image v-else src="../image/start.png"></image>
  7. <button class="button left" @click="is_switch = true"></button>
  8. <button class="button right" @click="is_switch = false"></button>
  9. </template>
  10. <view v-else style="font-size: 36rpx;text-align: center;color: #fff;line-height:100rpx">
  11. 即将开始
  12. </view>
  13. </view>
  14. <view :class="[themeObject.back, 'f-bulletin']" v-if="Object.keys(activity).length">
  15. <view>
  16. <view class="dir-left-nowrap main-between cross-center">
  17. <view class="f-title">
  18. {{activity.title}}
  19. </view>
  20. <view class="f-rule" @click="goRule">
  21. 活动规则
  22. <image src="/static/image/icon/arrow-right.png"></image>
  23. </view>
  24. </view>
  25. <view class="f-timing">
  26. 距离本场{{type === 1 ? '结束' : '开始'}}
  27. <text :class="[themeObject.color]">{{time_str.day}}</text>天
  28. <text :class="[themeObject.color]">{{time_str.hou}}</text>时
  29. <text :class="[themeObject.color]">{{time_str.min}}</text>分
  30. <text :class="[themeObject.color]">{{time_str.sec}}</text>秒
  31. </view>
  32. </view>
  33. </view>
  34. <view :class="[themeObject.back, 'f-activity']">
  35. <view class="f-item dir-left-nowrap"
  36. @click="routeDetail(item.id)"
  37. v-for="(item, index) in list" :key="index">
  38. <view class="f-image">
  39. <view class="out-dialog" v-if="item.goods_stock == 0 && appSetting.is_show_stock == '1'">
  40. <image :src="appSetting.is_use_stock == '1' ? appImg.plugins_out : appSetting.sell_out_pic"></image>
  41. </view>
  42. <image class='f-pic' :src="item.goodsWarehouse.cover_pic"></image>
  43. </view>
  44. <view class="cont">
  45. <view class="dir-left-nowrap cross-center" style="overflow: hidden">
  46. <view v-if="item.discount_type === 1" :class="getTheme + '-m-back f-discount box-grow-0 ' +getTheme">{{Number(item.min_discount)}}折</view>
  47. <view v-else :class="getTheme + '-m-back f-discount box-grow-0 ' +getTheme">减{{item.min_discount}}元</view>
  48. <view class="f-name t-omit">{{item.goodsWarehouse.name}}</view>
  49. </view>
  50. <view class="app-percentage" :class="getTheme + '-m-back-l ' +getTheme">
  51. <view :class="[themeObject.back]" :style="{width: `${item.percentage}%`}"></view>
  52. </view>
  53. <view class="f-num main-between">
  54. <text>{{item.sales}}</text>
  55. <text :class="[themeObject.color]">仅剩{{item.goods_stock}}{{item.goodsWarehouse.unit}}</text>
  56. </view>
  57. <view class="f-content dir-top-nowrap main-right">
  58. <view v-if="item.is_level">
  59. <app-member-price :theme="themeObject" :price="item.level_price"></app-member-price>
  60. </view>
  61. <app-sup-vip
  62. :is_vip_card_user="item.vip_card_appoint.is_vip_card_user"
  63. margin="4rpx 0 0"
  64. v-if="item.vip_card_appoint.discount"
  65. :discount="item.vip_card_appoint.discount"
  66. ></app-sup-vip>
  67. <view class="dir-left-nowrap main-between cross-bottom">
  68. <view class="f-price dir-top-nowrap">
  69. <text :class="themeObject.color">{{item.price_content}}</text>
  70. <text>¥{{item.original_price}}</text>
  71. </view>
  72. <button
  73. :class="[ getTheme + ' f-button', item.goods_stock === 0 && type === 1 ? 'f-sold-out' : themeObject.back]"
  74. @click.stop="grab(item)">
  75. {{type === 1 ? '马上抢' : '查看商品'}}
  76. </button>
  77. </view>
  78. </view>
  79. </view>
  80. </view>
  81. </view>
  82. <u-attr
  83. :goods="detail"
  84. v-model="attrShow"
  85. :themeObject="themeObject"
  86. :checked="selectAttr"
  87. :right-func="true"
  88. :left-func="true"
  89. @leftFunc="leftFunc"
  90. @rightFunc="rightFunc"
  91. @check="onAttr"
  92. >
  93. </u-attr>
  94. <view
  95. v-if="!Object.keys(activity).length"
  96. :style="{height: (windowHeight - switch_height) + 'px'}"
  97. :class="[getTheme + '-m-back', getTheme, 'f-empty']">
  98. <view class="f-empty-con">
  99. <view class="f-no">
  100. <app-no-goods background="#efefef" :title="title"></app-no-goods>
  101. </view>
  102. </view>
  103. </view>
  104. </app-layout>
  105. </template>
  106. <script>
  107. import { mapGetters, mapState } from "vuex";
  108. import appNoGoods from "../../../components/page-component/app-no-goods/app-no-goods.vue";
  109. import uAttr from '../../../components/page-component/goods/u-attr.vue';
  110. export default {
  111. name: "index",
  112. data() {
  113. return {
  114. is_switch: true,
  115. page: 1,
  116. type: 1,
  117. activity: {},
  118. timing: null,
  119. time_str: {
  120. day: '00',
  121. hou: '00',
  122. min: '00',
  123. sec: '00'
  124. },
  125. list: [],
  126. page_count: 1,
  127. detail: null,
  128. attr_groups: [],
  129. attrShow: 0,
  130. selectAttr: {},
  131. switch_height: 0,
  132. title: `暂无进行中活动`,
  133. is_no_now: false,
  134. going: true
  135. }
  136. },
  137. methods: {
  138. async getList() {
  139. const e = await this.$request({
  140. url: this.$api.flash_sale.list,
  141. data: {
  142. type: this.type,
  143. page: this.page
  144. }
  145. });
  146. if (e.code === 0) {
  147. let { activity, list, next_activity, pagination } = e.data;
  148. if (this.type === 1) {
  149. this.activity = activity;
  150. if (Object.keys(activity).length === 0) {
  151. this.is_no_now = true;
  152. this.type = 2;
  153. const w = await this.$request({
  154. url: this.$api.flash_sale.list,
  155. data: {
  156. type: this.type,
  157. page: this.page
  158. }
  159. });
  160. let { list, next_activity, pagination } = w.data;
  161. this.activity = next_activity;
  162. this.list.push(...list);
  163. this.page_count = pagination.page_count;
  164. } else {
  165. this.is_no_now = false;
  166. this.list.push(...list);
  167. this.page_count = pagination.page_count;
  168. }
  169. } else {
  170. this.activity = next_activity;
  171. this.list.push(...list);
  172. this.page_count = pagination.page_count;
  173. }
  174. }
  175. },
  176. set_time(time_at) {
  177. clearInterval(this.timing);
  178. let time_str = new Date(time_at.replace(/-/g, '/'));
  179. this.now_time(time_str);
  180. this.timing = setInterval(() => {
  181. this.now_time(time_str);
  182. }, 1000);
  183. },
  184. now_time(time_str) {
  185. let time = time_str.getTime() - new Date().getTime();
  186. if (time < 0) {
  187. clearInterval(this.timing);
  188. }
  189. let day = parseInt(time/1000/60/60/24);
  190. let hou = parseInt((time/1000/60/60)%24);
  191. let min = parseInt((time/1000/60)%60);
  192. let sec = parseInt((time/1000)%60);
  193. this.time_str.day = day < 10 ? '0' + day : day;
  194. this.time_str.hou = hou < 10 ? '0' + hou : hou;
  195. this.time_str.min = min < 10 ? '0' + min : min;
  196. this.time_str.sec = sec < 10 ? '0' + sec : sec;
  197. },
  198. grab(item) {
  199. console.log(item)
  200. if (this.type === 1) {
  201. this.detail = null;
  202. if (item.goods_stock !== 0) {
  203. this.selectAttr = {};
  204. this.detail = item;
  205. this.attrShow = true;
  206. }
  207. } else {
  208. uni.navigateTo({
  209. url: `/plugins/flash_sale/goods/goods?id=${item.id}`
  210. })
  211. }
  212. },
  213. onAttr(data) {
  214. this.selectAttr = data;
  215. },
  216. routeDetail(id) {
  217. uni.navigateTo({
  218. url: `/plugins/flash_sale/goods/goods?id=${id}`
  219. })
  220. },
  221. goRule() {
  222. uni.navigateTo({
  223. url: `/pages/rules/index?url=${encodeURIComponent(this.$api.flash_sale.list)}&key=content`
  224. });
  225. },
  226. rightFunc(data) {
  227. uni.navigateTo({
  228. url: `/pages/order-submit/order-submit?mch_list=${JSON.stringify([data])}`
  229. });
  230. },
  231. leftFunc(number) {
  232. this.$request({
  233. url: this.$api.flash_sale.add_cart,
  234. data: {
  235. flash_goods_id: this.selectAttr.goods_id,
  236. attr_id: this.selectAttr.id,
  237. num: number
  238. },
  239. method: 'post'
  240. }).then(e => {
  241. uni.showToast({
  242. title: e.msg,
  243. type: 'success'
  244. });
  245. });
  246. }
  247. },
  248. mounted() {
  249. let query = wx.createSelectorQuery();
  250. this.$nextTick().then(() => {
  251. query.select('.f-switch').boundingClientRect(rect=>{
  252. this.switch_height = rect.height;
  253. }).exec();
  254. });
  255. // #ifdef MP-WEIXIN
  256. wx.showShareMenu({
  257. menus: ['shareAppMessage', 'shareTimeline']
  258. })
  259. // #endif
  260. },
  261. onReachBottom() {
  262. if (this.page < this.page_count) {
  263. this.page++;
  264. this.getList();
  265. }
  266. },
  267. watch: {
  268. is_switch: {
  269. handler(boolean) {
  270. if (this.going) {
  271. this.going = false;
  272. this.list = [];
  273. this.page = 1;
  274. if (boolean) {
  275. this.type = 1;
  276. this.title = '暂无进行中活动';
  277. } else {
  278. this.type = 2;
  279. this.title = '暂无下一场活动';
  280. }
  281. this.getList().then(() => {
  282. if (this.type === 1) {
  283. let end_at = this.activity.end_at;
  284. end_at ? this.set_time(end_at) : null;
  285. } else {
  286. let start_at = this.activity.start_at;
  287. start_at ? this.set_time(start_at) : null;
  288. }
  289. this.going = true;
  290. });
  291. }
  292. },
  293. immediate: true
  294. }
  295. },
  296. computed: {
  297. ...mapGetters('mallConfig', {
  298. getTheme: 'getTheme'
  299. }),
  300. ...mapState('gConfig', {
  301. windowHeight: state => state.systemInfo.windowHeight
  302. }),
  303. ...mapState({
  304. appImg: state => state.mallConfig.__wxapp_img.mall,
  305. appSetting: state => state.mallConfig.mall.setting
  306. }),
  307. themeObject:function() {
  308. // let theme = th
  309. return {
  310. back: this.getTheme + '-m-back ' + this.getTheme,
  311. theme: this.getTheme,
  312. color: this.getTheme + '-m-text ' + this.getTheme,
  313. sBack: this.getTheme + '-s-back ' + this.getTheme
  314. }
  315. },
  316. },
  317. components: {
  318. 'app-no-goods': appNoGoods,
  319. uAttr
  320. },
  321. onShareAppMessage() {
  322. return this.$shareAppMessage({
  323. path: '/plugins/flash_sale/index/index',
  324. title: this.$children[0].navigationBarTitle
  325. });
  326. },
  327. // #ifdef MP-WEIXIN
  328. onShareTimeline() {
  329. // 分享朋友圈beta
  330. return this.$shareTimeline({
  331. title:this.$children[0].navigationBarTitle
  332. });
  333. },
  334. // #endif
  335. }
  336. </script>
  337. <style scoped lang="scss">
  338. .f-switch {
  339. width: 750upx;
  340. height: 100upx;
  341. position: relative;
  342. }
  343. .f-switch image {
  344. width: 750upx;
  345. height: 100upx;
  346. position: absolute;
  347. top: -1upx;
  348. }
  349. .f-switch .button {
  350. width: 375upx;
  351. height: 100upx;
  352. position: absolute;
  353. background: transparent;
  354. padding: 0;
  355. margin: 0;
  356. border: none;
  357. }
  358. .f-switch .right {
  359. right: 0;
  360. }
  361. .f-switch .left {
  362. left: 0;
  363. }
  364. .f-bulletin {
  365. padding: 20upx 24upx;
  366. .f-title {
  367. font-size: 32upx;
  368. font-weight: bold;
  369. color: #353535;
  370. }
  371. .f-rule {
  372. font-size: 26upx;
  373. color: #666;
  374. vertical-align: middle;
  375. >image {
  376. width: 12upx;
  377. height: 22upx;
  378. margin-left: 14upx;
  379. }
  380. }
  381. .f-timing {
  382. margin-top: 18upx;
  383. font-size: 26upx;
  384. color: #353535;
  385. >text:first-child {
  386. margin-left: 18upx;
  387. }
  388. }
  389. }
  390. .f-bulletin>view {
  391. background-color: #fff;
  392. border-radius: 15upx;
  393. height: 147upx;
  394. padding: 32upx 24upx;
  395. }
  396. .f-activity {
  397. padding: 0 24upx;
  398. overflow: auto;
  399. position: absolute;
  400. width: 750upx;
  401. min-height: calc(100% - 287rpx);
  402. .f-item {
  403. background-color: #fff;
  404. border-radius: 15upx;
  405. padding: 24upx;
  406. margin-bottom: 20upx;
  407. .f-pic {
  408. width: 240upx;
  409. height: 240upx;
  410. border-radius: 8upx;
  411. }
  412. >view.cont {
  413. width: 394upx;
  414. margin-left: 24upx;
  415. position: relative;
  416. }
  417. .f-name {
  418. font-size: 28upx;
  419. color: #353535;
  420. min-width: 304upx;
  421. }
  422. .f-discount {
  423. height: 26upx;
  424. color: #fff;
  425. font-size: 20upx;
  426. margin-right: 10upx;
  427. text-align: center;
  428. line-height: 26upx;
  429. border-radius: 7upx;
  430. padding: 0 5upx;
  431. }
  432. .f-num {
  433. font-size: 24upx;
  434. margin-top: 10upx;
  435. }
  436. .f-num>text:first-child {
  437. color: #666;
  438. }
  439. .f-button {
  440. height: 68upx;
  441. line-height: 68upx;
  442. width: 164upx;
  443. border-radius: 34upx;
  444. font-size: 26upx;
  445. color: #fff;
  446. margin: 0;
  447. padding: 0;
  448. text-align: center;
  449. border: none;
  450. }
  451. .f-content {
  452. position: absolute;
  453. bottom: 0;
  454. width: 394upx;
  455. }
  456. .f-price>text:first-child {
  457. font-size: 32upx;
  458. }
  459. .f-price>text:last-child {
  460. font-size: 25upx;
  461. color: #999;
  462. text-decoration: line-through;
  463. }
  464. .app-percentage {
  465. width: #{395rpx};
  466. height: #{20rpx};
  467. border-radius: #{12rpx};
  468. margin-top: #{10rpx};
  469. overflow: hidden;
  470. >view {
  471. height: #{20rpx};
  472. border-radius: #{12rpx};
  473. }
  474. }
  475. .f-sold-out {
  476. background-color: #cdcdcd;
  477. }
  478. }
  479. }
  480. .f-empty {
  481. width: 750upx;
  482. padding: 20upx 24upx;
  483. .f-empty-con {
  484. height: 100%;
  485. background: #efefef;
  486. border-radius: 15upx;
  487. position: relative;
  488. }
  489. .f-no {
  490. position: absolute;
  491. top: 30%;
  492. left: 50%;
  493. transform: translate(-50%, -50%);
  494. }
  495. }
  496. .out-dialog {
  497. border-top-left-radius: 15upx;
  498. border-top-right-radius: 15upx;
  499. width: 240upx;
  500. height: 240upx;
  501. position: absolute;
  502. top: 0;
  503. z-index: 10;
  504. left: 0;
  505. background-color: rgba(0,0,0,.5);
  506. image {
  507. width: 240upx;
  508. height: 240upx;
  509. }
  510. }
  511. .f-image {
  512. position: relative;
  513. }
  514. </style>