product-item.vue 4.54 KB
<template>
  <div class="product-item" :class="{['has-tip']: value.tip}">
    <div class="item-content" :style="itemStyle" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd">
      <div class="tip" v-if="value.tip">超出建议售价将被限制超出建议售价将被限制展示</div>
      <div class="info">
        <div class="left">
          <span class="size">{{value.goodsInfo.sizeName}}</span>
          <span class="l-size"></span>
          <span class="unit">码</span>
        </div>
        <div class="middle">
          <p class="size-store">¥{{value.goodsInfo.price}},{{value.goodsInfo.storageNum}}个库存</p>
          <p class="low-price">当前最低价¥{{value.goodsInfo.leastPrice}}</p>
        </div>
        <div class="right">
          <Button class="chg-price" @click="onChgPrice">调 价</Button>
        </div>
      </div>
    </div>
    <div ref="options" class="item-options">
      <Button class="btn-no-sale" @click="onNoSale">不卖了</Button>
    </div>
  </div>
</template>

<script>
import {Button} from 'cube-ui';

export default {
  name: 'ProductItem',
  data() {
    return {
      distance: 0,
      startX: 0,
      startY: 0,
      move: false,
      transition: true
    };
  },
  computed: {
    itemStyle() {
      return {
        transition: this.transition ? void 0 : 'none 0s ease 0s',
        transform: this.move ? `translate3d(${this.distance}px, 0px, 0px)` : void 0
      };
    }
  },
  props: {
    value: Object,
    slideValue: Object
  },
  mounted() {

  },
  watch: {
    slideValue(val) {
      if (this.distance !== 0 && this.value !== val) {
        this.distance = 0;
        this.timeout = setTimeout(() => {
          this.move = false;
        }, 500);
      }
    }
  },
  methods: {
    onNoSale() {
      this.$emit('on-no-sale', this.value.goodsInfo);
    },
    onChgPrice() {
      this.$emit('on-change-price', this.value.goodsInfo);
    },
    onTouchStart(evt) {
      const {clientX, clientY} = evt.touches[0];

      this.startX = clientX - this.distance;
      this.startY = clientY;
      if (this.timeout) {
        clearTimeout(this.timeout);
      }
    },
    onTouchMove(evt) {
      this.transition = false;
      const {clientX, clientY} = evt.touches[0];
      let distance = clientX - this.startX;

      if (Math.abs(clientY - this.startY) > 20 && this.distance === 0) {
        this.startX = 0;
        return;
      }

      if (0 - distance > 20) {
        this.$emit('on-slide', this.value);
        this.move = true;
      }

      if (this.distance + distance > 0) {
        return;
      }
      if (this.move) {
        this.distance = distance;
      }
    },
    onTouchEnd() {
      const optionsWidth = this.$refs.options.clientWidth;

      if (0 - this.distance > optionsWidth) {
        this.transition = true;
        this.distance = 0 - optionsWidth;
      } else if (0 - this.distance < optionsWidth) {
        this.transition = true;
        this.distance = 0;
      }
      if (this.distance === 0) {
        this.timeout = setTimeout(() => {
          this.move = false;
        }, 500);
      }
    }
  },
  components: {Button}
};
</script>

<style lang="scss" scoped>
.product-item {
  width: 100%;
  border-bottom: 1px solid #eee;
  position: relative;
}

.item-content {
  padding-left: 40px;
  padding-right: 40px;
  position: relative;
  z-index: 2;
  background-color: #fff;
  padding-top: 20px;
  padding-bottom: 40px;
  transition: transform 0.5s cubic-bezier(0.36, 0.66, 0.04, 1);
}

.item-options {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 1;

  .btn-no-sale {
    height: 100%;
    line-height: 1;
    background-color: #eee;
    width: 160px;
    font-size: 28px;
    color: #000;

    &:active {
      opacity: 0.7;
    }
  }
}

.tip {
  color: #d0021b;
}

.info {
  width: 100%;
  display: flex;
  padding-top: 20px;
}

.left {
  width: 160px;
  display: flex;
  height: 56px;
  align-items: flex-end;

  .size {
    font-size: 56px;
    line-height: 56px;
    margin-right: 6px;
  }

  .l-size {
    align-self: flex-end;
    margin-right: 6px;
  }

  .unit {
    align-self: flex-start;
    margin-right: 6px;
  }
}

.middle {
  flex: 1;

  .size-store {
    font-size: 28px;
  }

  .low-price {
    color: #999;
    margin-top: 6px;
  }
}

.right {
  width: 130px;
  text-align: right;
  display: flex;
  align-items: center;

  .chg-price {
    width: 130px;
    height: 60px;
    line-height: 60px;
    padding-top: 0;
    padding-bottom: 0;
    background-color: #08314d;
    font-size: 28px;

    &:active {
      opacity: 0.7;
    }
  }
}
</style>