效果图:

代码结构如下:

代码:

uni-swipe-action.vue

<template>
  <view>
    <slot></slot>
  </view>
</template>

<script>
  /**
   * SwipeAction 滑动操作
   * @description 通过滑动触发选项的容器
   * @tutorial https://ext.dcloud.net.cn/plugin?id=181
   */
  export default {
    data() {
      return {};
    },
    provide() {
      return {
        swipeaction: this
      }
    },
    created() {
      this.children = []
    },
    methods: {
      closeOther(vm) {
        let children = this.children
        children.forEach((item, index) => {
          if (vm === item) return
          // 支付宝执行以下操作
          // #ifdef MP-ALIPAY
          if (item.isopen) {
            item.close()
          }
          // #endif

          // app vue 端、h5 、微信、支付宝  执行以下操作
          // #ifdef APP-VUE || H5 || MP-WEIXIN
          let position = item.position[0]
          let show = position.show
          if (show) {
            position.show = false
          }
          // #endif

          // nvue 执行以下操作
          // #ifdef APP-NVUE || MP-BAIDU || MP-QQ || MP-TOUTIAO
          item.close()
          // #endif
        })
      }
    }
  }
</script>

<style>

</style>

bindingx.js

const BindingX = uni.requireNativePlugin('bindingx');
const dom = uni.requireNativePlugin('dom');
const animation = uni.requireNativePlugin('animation');

export default {
  data() {
    return {
      right: 0,
      button: [],
      preventGesture: false
    }
  },

  watch: {
    show(newVal) {
      if (!this.position || JSON.stringify(this.position) === '{}') return;
      if (this.autoClose) return
      if (this.isInAnimation) return
      if (newVal) {
        this.open()
      } else {
        this.close()
      }
    },
  },
  created() {
    if (this.swipeaction.children !== undefined) {
      this.swipeaction.children.push(this)
    }
  },
  mounted() {
    this.boxSelector = this.getEl(this.$refs['selector-box-hock']);
    this.selector = this.getEl(this.$refs['selector-content-hock']);
    this.buttonSelector = this.getEl(this.$refs['selector-button-hock']);
    this.position = {}
    this.x = 0
    setTimeout(() => {
      this.getSelectorQuery()
    }, 200)
  },
  beforeDestroy() {
    if (this.timing) {
      BindingX.unbind({
        token: this.timing.token,
        eventType: 'timing'
      })
    }
    if (this.eventpan) {
      BindingX.unbind({
        token: this.eventpan.token,
        eventType: 'pan'
      })
    }
    this.swipeaction.children.forEach((item, index) => {
      if (item === this) {
        this.swipeaction.children.splice(index, 1)
      }
    })
  },
  methods: {
    onClick(index, item) {
      this.$emit('click', {
        content: item,
        index
      })
    },
    touchstart(e) {
      if (this.isInAnimation) return
      if (this.stop) return
      this.stop = true
      if (this.autoClose) {
        this.swipeaction.closeOther(this)
      }
      let endWidth = this.right
      let boxStep = `(x+${this.x})`
      let pageX = `${boxStep}> ${-endWidth} && ${boxStep} < 0?${boxStep}:(x+${this.x} < 0? ${-endWidth}:0)`

      let props = [{
        element: this.selector,
        property: 'transform.translateX',
        expression: pageX
      }]

      let left = 0
      for (let i = 0; i < this.options.length; i++) {
        let buttonSelectors = this.getEl(this.$refs['button-hock'][i]);
        if (this.button.length === 0 || !this.button[i] || !this.button[i].width) return
        let moveMix = endWidth - left
        left += this.button[i].width
        let step = `(${this.x}+x)/${endWidth}`
        let moveX = `(${step}) * ${moveMix}`
        let pageButtonX = `${moveX}&& (x+${this.x} > ${-endWidth})?${moveX}:${-moveMix}`
        props.push({
          element: buttonSelectors,
          property: 'transform.translateX',
          expression: pageButtonX
        })
      }

      this.eventpan = this._bind(this.boxSelector, props, 'pan', (e) => {
        if (e.state === 'end') {
          this.x = e.deltaX + this.x;
          if (this.x < -endWidth) {
            this.x = -endWidth
          }
          if (this.x > 0) {
            this.x = 0
          }
          this.stop = false
          this.bindTiming();
        }
      })
    },
    touchend(e) {
      this.$nextTick(() => {
        if (this.isopen && !this.isDrag && !this.isInAnimation) {
          this.close()
        }
      })
    },
    bindTiming() {
      if (this.isopen) {
        this.move(this.x, -this.right)
      } else {
        this.move(this.x, -40)
      }
    },
    move(left, value) {
      if (left >= value) {
        this.close()
      } else {
        this.open()
      }
    },
    /**
     * 开启swipe
     */
    open() {
      this.animation(true)
    },
    /**
     * 关闭swipe
     */
    close() {
      this.animation(false)
    },
    /**
     * 开启关闭动画
     * @param {Object} type
     */
    animation(type) {
      this.isDrag = true
      let endWidth = this.right
      let time = 200
      this.isInAnimation = true;

      let exit = `t>${time}`;
      let translate_x_expression = `easeOutExpo(t,${this.x},${type?(-endWidth-this.x):(-this.x)},${time})`
      let props = [{
        element: this.selector,
        property: 'transform.translateX',
        expression: translate_x_expression
      }]

      let left = 0
      for (let i = 0; i < this.options.length; i++) {
        let buttonSelectors = this.getEl(this.$refs['button-hock'][i]);
        if (this.button.length === 0 || !this.button[i] || !this.button[i].width) return
        let moveMix = endWidth - left
        left += this.button[i].width
        let step = `${this.x}/${endWidth}`
        let moveX = `(${step}) * ${moveMix}`
        let pageButtonX = `easeOutExpo(t,${moveX},${type ? -moveMix + '-' + moveX: 0 + '-' + moveX},${time})`
        props.push({
          element: buttonSelectors,
          property: 'transform.translateX',
          expression: pageButtonX
        })
      }

      this.timing = BindingX.bind({
        eventType: 'timing',
        exitExpression: exit,
        props: props
      }, (e) => {
        if (e.state === 'end' || e.state === 'exit') {
          this.x = type ? -endWidth : 0
          this.isInAnimation = false;

          this.isopen = this.isopen || false
          if (this.isopen !== type) {
            this.$emit('change', type)
          }
          this.isopen = type
          this.isDrag = false
        }
      });
    },
    /**
     * 绑定  BindingX
     * @param {Object} anchor
     * @param {Object} props
     * @param {Object} fn
     */
    _bind(anchor, props, eventType, fn) {
      return BindingX.bind({
        anchor,
        eventType,
        props
      }, (e) => {
        typeof(fn) === 'function' && fn(e)
      });
    },
    /**
     * 获取ref
     * @param {Object} el
     */
    getEl(el) {
      return el.ref
    },
    /**
     * 获取节点信息
     */
    getSelectorQuery() {
      dom.getComponentRect(this.$refs['selector-content-hock'], (data) => {
        if (this.position.content) return
        this.position.content = data.size
      })
      for (let i = 0; i < this.options.length; i++) {
        dom.getComponentRect(this.$refs['button-hock'][i], (data) => {
          if (!this.button) {
            this.button = []
          }
          if (this.options.length === this.button.length) return
          this.button.push(data.size)
          this.right += data.size.width
          if (this.autoClose) return
          if (this.show) {
            this.open()
          }
        })
      }
    }
  }
}

index.wxs

/**
 * 监听页面内值的变化,主要用于动态开关swipe-action
 * @param {Object} newValue
 * @param {Object} oldValue
 * @param {Object} ownerInstance
 * @param {Object} instance
 */
function sizeReady(newValue, oldValue, ownerInstance, instance) {
  var state = instance.getState()
  state.position = JSON.parse(newValue)
  if (!state.position || state.position.length === 0) return
  var show = state.position[0].show
  state.left = state.left || state.position[0].left;
  // 通过用户变量,开启或关闭
  if (show) {
    openState(true, instance, ownerInstance)
  } else {
    openState(false, instance, ownerInstance)
  }
}

/**
 * 开始触摸操作
 * @param {Object} e
 * @param {Object} ins
 */
function touchstart(e, ins) {
  var instance = e.instance;
  var state = instance.getState();
  var pageX = e.touches[0].pageX;
  // 开始触摸时移除动画类
  instance.removeClass('ani');
  var owner = ins.selectAllComponents('.button-hock')
  for (var i = 0; i < owner.length; i++) {
    owner[i].removeClass('ani');
  }
  // state.position = JSON.parse(instance.getDataset().position);
  state.left = state.left || state.position[0].left;
  // 获取最终按钮组的宽度
  state.width = pageX - state.left;
  ins.callMethod('closeSwipe')
}

/**
 * 开始滑动操作
 * @param {Object} e
 * @param {Object} ownerInstance
 */
function touchmove(e, ownerInstance) {
  var instance = e.instance;
  var disabled = instance.getDataset().disabled
  var state = instance.getState()
  // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
  disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;

  if (disabled) return
  var pageX = e.touches[0].pageX;
  move(pageX - state.width, instance, ownerInstance)
}

/**
 * 结束触摸操作
 * @param {Object} e
 * @param {Object} ownerInstance
 */
function touchend(e, ownerInstance) {
  var instance = e.instance;
  var disabled = instance.getDataset().disabled
  var state = instance.getState()

  // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复
  disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false;

  if (disabled) return
  // 滑动过程中触摸结束,通过阙值判断是开启还是关闭
  moveDirection(state.left, -40, instance, ownerInstance)
}

/**
 * 设置移动距离
 * @param {Object} value
 * @param {Object} instance
 * @param {Object} ownerInstance
 */
function move(value, instance, ownerInstance) {
  var state = instance.getState()
  // 获取可滑动范围
  var x = Math.max(-state.position[1].width, Math.min((value), 0));
  state.left = x;
  instance.setStyle({
    transform: 'translateX(' + x + 'px)',
    '-webkit-transform': 'translateX(' + x + 'px)'
  })
  // 折叠按钮动画
  buttonFold(x, instance, ownerInstance)
}

/**
 * 移动方向判断
 * @param {Object} left
 * @param {Object} value
 * @param {Object} ownerInstance
 * @param {Object} ins
 */
function moveDirection(left, value, ins, ownerInstance) {
  var state = ins.getState()
  var position = state.position
  var isopen = state.isopen
  if (!position[1].width) {
    openState(false, ins, ownerInstance)
    return
  }
  // 如果已经是打开状态,进行判断是否关闭,还是保留打开状态
  if (isopen) {
    if (-left <= position[1].width) {
      openState(false, ins, ownerInstance)
    } else {
      openState(true, ins, ownerInstance)
    }
    return
  }
  // 如果是关闭状态,进行判断是否打开,还是保留关闭状态
  if (left <= value) {
    openState(true, ins, ownerInstance)
  } else {
    openState(false, ins, ownerInstance)
  }
}

/**
 * 设置按钮移动距离
 * @param {Object} value
 * @param {Object} instance
 * @param {Object} ownerInstance
 */
function buttonFold(value, instance, ownerInstance) {
  var ins = ownerInstance.selectAllComponents('.button-hock');
  var state = instance.getState();
  var position = state.position;
  var arr = [];
  var w = 0;
  for (var i = 0; i < ins.length; i++) {
    if (!ins[i].getDataset().button) return
    var btnData = JSON.parse(ins[i].getDataset().button)

    // fix by mehaotian TODO 在 app-vue 中,字符串转对象,需要转两次,这里先这么兼容
    if (typeof(btnData) === 'string') {
      btnData = JSON.parse(btnData)
    }

    var button = btnData[i] && btnData[i].width || 0
    w += button
    arr.push(-w)
    // 动态计算按钮组每个按钮的折叠动画移动距离
    var distance = arr[i - 1] + value * (arr[i - 1] / position[1].width)
    if (i != 0) {
      ins[i].setStyle({
        transform: 'translateX(' + distance + 'px)',
      })
    }
  }
}

/**
 * 开启状态
 * @param {Boolean} type
 * @param {Object} ins
 * @param {Object} ownerInstance
 */
function openState(type, ins, ownerInstance) {
  var state = ins.getState()
  var position = state.position
  if (state.isopen === undefined) {
    state.isopen = false
  }
  // 只有状态有改变才会通知页面改变状态
  if (state.isopen !== type) {
    // 通知页面,已经打开
    ownerInstance.callMethod('change', {
      open: type
    })
  }
  // 设置打开和移动状态
  state.isopen = type


  // 添加动画类
  ins.addClass('ani');
  var owner = ownerInstance.selectAllComponents('.button-hock')
  for (var i = 0; i < owner.length; i++) {
    owner[i].addClass('ani');
  }
  // 设置最终移动位置
  move(type ? -position[1].width : 0, ins, ownerInstance)

}

module.exports = {
  sizeReady: sizeReady,
  touchstart: touchstart,
  touchmove: touchmove,
  touchend: touchend
}

mpalipay.js

export default {
  data() {
    return {
      isshow: false,
      viewWidth: 0,
      buttonWidth: 0,
      disabledView: false,
      x: 0,
      transition: false
    }
  },
  watch: {
    show(newVal) {
      if (this.autoClose) return
      if (newVal) {
        this.open()
      } else {
        this.close()
      }
    },
  },
  created() {
    if (this.swipeaction.children !== undefined) {
      this.swipeaction.children.push(this)
    }
  },
  beforeDestroy() {
    this.swipeaction.children.forEach((item, index) => {
      if (item === this) {
        this.swipeaction.children.splice(index, 1)
      }
    })
  },
  mounted() {
    this.isopen = false
    this.transition = true
    setTimeout(() => {
      this.getQuerySelect()
    }, 50)

  },
  methods: {
    onClick(index, item) {
      this.$emit('click', {
        content: item,
        index
      })
    },
    touchstart(e) {
      let {
        pageX,
        pageY
      } = e.changedTouches[0]
      this.transition = false
      this.startX = pageX
      if (this.autoClose) {
        this.swipeaction.closeOther(this)
      }
    },
    touchmove(e) {
      let {
        pageX,
      } = e.changedTouches[0]
      this.slide = this.getSlide(pageX)
      if (this.slide === 0) {
        this.disabledView = false
      }

    },
    touchend(e) {
      this.stop = false
      this.transition = true
      if (this.isopen) {
        if (this.moveX === -this.buttonWidth) {
          this.close()
          return
        }
        this.move()
      } else {
        if (this.moveX === 0) {
          this.close()
          return
        }
        this.move()
      }
    },
    open() {
      this.x = this.moveX
      this.$nextTick(() => {
        this.x = -this.buttonWidth
        this.moveX = this.x

        if(!this.isopen){
          this.isopen = true
          this.$emit('change', true)
        }
      })
    },
    close() {
      this.x = this.moveX
      this.$nextTick(() => {
        this.x = 0
        this.moveX = this.x
        if(this.isopen){
          this.isopen = false
          this.$emit('change', false)
        }
      })
    },
    move() {
      if (this.slide === 0) {
        this.open()
      } else {
        this.close()
      }
    },
    onChange(e) {
      let x = e.detail.x
      this.moveX = x
      if (x >= this.buttonWidth) {
        this.disabledView = true
        this.$nextTick(() => {
          this.x = this.buttonWidth
        })
      }
    },
    getSlide(x) {
      if (x >= this.startX) {
        this.startX = x
        return 1
      } else {
        this.startX = x
        return 0
      }

    },
    getQuerySelect() {
      const query = uni.createSelectorQuery().in(this);
      query.selectAll('.viewWidth-hook').boundingClientRect(data => {

        this.viewWidth = data[0].width
        this.buttonWidth = data[1].width
        this.transition = false
        this.$nextTick(() => {
          this.transition = true
        })

        if (!this.buttonWidth) {
          this.disabledView = true
        }

        if (this.autoClose) return
        if (this.show) {
          this.open()
        }
      }).exec();

    }
  }
}

mpother.js

// #ifdef APP-NVUE
const dom = weex.requireModule('dom');
// #endif
export default {
  data() {
    return {
      uniShow: false,
      left: 0
    }
  },
  computed: {
    moveLeft() {
      return `translateX(${this.left}px)`
    }
  },
  watch: {
    show(newVal) {
      if (!this.position || JSON.stringify(this.position) === '{}') return;
      if (this.autoClose) return
      if (newVal) {
        this.$emit('change', true)
        this.open()
      } else {
        this.$emit('change', false)
        this.close()
      }
    }
  },
  mounted() {
    this.position = {}
    if (this.swipeaction.children !== undefined) {
      this.swipeaction.children.push(this)
    }
    setTimeout(() => {
      this.getSelectorQuery()
    }, 100)
  },
  beforeDestoy() {
    this.swipeaction.children.forEach((item, index) => {
      if (item === this) {
        this.swipeaction.children.splice(index, 1)
      }
    })
  },
  methods: {
    onClick(index, item) {
      this.$emit('click', {
        content: item,
        index
      })
      this.close()
    },
    touchstart(e) {
      const {
        pageX
      } = e.touches[0]
      if (this.disabled) return
      const left = this.position.content.left
      if (this.autoClose) {
        this.swipeaction.closeOther(this)
      }
      this.width = pageX - left
      if (this.isopen) return
      if (this.uniShow) {
        this.uniShow = false
        this.isopen = true
        this.openleft = this.left + this.position.button.width
      }
    },
    touchmove(e, index) {
      if (this.disabled) return
      const {
        pageX
      } = e.touches[0]
      this.setPosition(pageX)
    },
    touchend() {
      if (this.disabled) return
      if (this.isopen) {
        this.move(this.openleft, 0)
        return
      }
      this.move(this.left, -40)
    },
    setPosition(x, y) {
      if (!this.position.button.width) {
        return
      }
      // this.left = x - this.width
      this.setValue(x - this.width)
    },
    setValue(value) {
      // 设置最大最小值
      this.left = Math.max(-this.position.button.width, Math.min(parseInt(value), 0))
      this.position.content.left = this.left
      if (this.isopen) {
        this.openleft = this.left + this.position.button.width
      }
    },
    move(left, value) {
      if (left >= value) {
        this.$emit('change', false)
        this.close()
      } else {
        this.$emit('change', true)
        this.open()
      }
    },
    open() {
      this.uniShow = true
      this.left = -this.position.button.width
      this.setValue(-this.position.button.width)
    },
    close() {
      this.uniShow = true
      this.setValue(0)
      setTimeout(() => {
        this.uniShow = false
        this.isopen = false
      }, 300)
    },
    getSelectorQuery() {
      // #ifndef APP-NVUE
      const views = uni.createSelectorQuery()
        .in(this)
      views
        .selectAll('.selector-query-hock')
        .boundingClientRect(data => {
          this.position.content = data[1]
          this.position.button = data[0]
          if (this.autoClose) return
          if (this.show) {
            this.open()
          } else {
            this.close()
          }
        })
        .exec()
      // #endif
      // #ifdef APP-NVUE
      dom.getComponentRect(this.$refs['selector-content-hock'], (data) => {
        if (this.position.content) return
        this.position.content = data.size
      })
      dom.getComponentRect(this.$refs['selector-button-hock'], (data) => {
        if (this.position.button) return
        this.position.button = data.size
        if (this.autoClose) return
        if (this.show) {
          this.open()
        } else {
          this.close()
        }
      })
      // #endif
    }
  }
}

mpwxs.js

export default {
  data() {
    return {
      position: [],
      button: []
    }
  },
  computed: {
    pos() {
      return JSON.stringify(this.position)
    },
    btn() {
      return JSON.stringify(this.button)
    }
  },
  watch: {
    show(newVal) {
      if (this.autoClose) return
      let valueObj = this.position[0]
      if (!valueObj) {
        this.init()
        return
      }
      valueObj.show = newVal
      this.$set(this.position, 0, valueObj)
    }
  },
  created() {
    if (this.swipeaction.children !== undefined) {
      this.swipeaction.children.push(this)
    }
  },
  mounted() {
    this.init()

  },
  beforeDestroy() {
    this.swipeaction.children.forEach((item, index) => {
      if (item === this) {
        this.swipeaction.children.splice(index, 1)
      }
    })
  },
  methods: {
    init() {
      
      setTimeout(() => {
        this.getSize()
        this.getButtonSize()
      }, 50)
    },
    closeSwipe(e) {
      if (!this.autoClose) return
      this.swipeaction.closeOther(this)
    },
    
    change(e) {
      this.$emit('change', e.open)
      let valueObj = this.position[0]
      if (valueObj.show !== e.open) {
        valueObj.show = e.open
        this.$set(this.position, 0, valueObj)
      }
    },
    onClick(index, item) {
      this.$emit('click', {
        content: item,
        index
      })
    },
    getSize() {
      const views = uni.createSelectorQuery().in(this)
      views
        .selectAll('.selector-query-hock')
        .boundingClientRect(data => {
          if (this.autoClose) {
            data[0].show = false
          } else {
            data[0].show = this.show
          }
          this.position = data
        })
        .exec()
    },
    getButtonSize() {
      const views = uni.createSelectorQuery().in(this)
      views
        .selectAll('.button-hock')
        .boundingClientRect(data => {
          this.button = data
        })
        .exec()
    }
  }
}

uni-swipe-action-item.vue

<template>
  <view class="uni-swipe">
    <!-- 在微信小程序 app vue端 h5 使用wxs 实现-->
    <!-- #ifdef APP-VUE || MP-WEIXIN || H5 -->
    <view class="uni-swipe_content">
      <view :data-disabled="disabled" :data-position="pos" :change:prop="swipe.sizeReady" :prop="pos" class="uni-swipe_move-box selector-query-hock move-hock"
       @touchstart="swipe.touchstart" @touchmove="swipe.touchmove" @touchend="swipe.touchend" @change="change">
        <view class="uni-swipe_box">
          <slot />
        </view>
        <view ref="selector-button-hock" class="uni-swipe_button-group selector-query-hock move-hock">
          <view v-for="(item,index) in options" :data-button="btn" :key="index" :style="{
              backgroundColor: 'transparent',
              fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
            }"
           class="uni-swipe_button button-hock" @click.stop="onClick(index,item)"><text class="uni-swipe_button-text" :style="{
             color: item.style && item.style.color ? item.style.color : '#FFFFFF',
             width:'80rpx',
             height:'80rpx',
             backgroundColor:item.style && item.style.backgroundColor ? item.style.backgroundColor : 'red',
             borderRadius:'40rpx',
             backgroundImage: item.style && item.style.backgroundImage ? item.style.backgroundImage : ''}">{{ item.text }}</text></view>
        </view>
      </view>
    </view>
    <!-- #endif -->

    <!--  app nvue端 使用 bindingx -->
    <!-- #ifdef APP-NVUE -->
    <view ref="selector-box-hock" class="uni-swipe_content" @horizontalpan="touchstart" @touchend="touchend">
      <view ref="selector-button-hock" class="uni-swipe_button-group selector-query-hock move-hock" :style="{width:right+'px'}">
        <view ref="button-hock" v-for="(item,index) in options" :key="index" :style="{
      backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',left: right+'px'}"
         class="uni-swipe_button " @click.stop="onClick(index,item)"><text class="uni-swipe_button-text" :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'}">{{ item.text }}</text></view>
      </view>
      <view ref='selector-content-hock' class="uni-swipe_move-box selector-query-hock">
        <view class="uni-swipe_box">
          <slot />
        </view>
      </view>
    </view>
    <!-- #endif -->

    <!-- 在非 app 端、非微信小程序、支付宝小程序、h5端使用 js -->
    <!-- #ifndef APP-PLUS || MP-WEIXIN || MP-ALIPAY || H5 -->
    <view class="uni-swipe_content">
      <view ref="selector-button-hock" class="uni-swipe_button-group selector-query-hock move-hock">
        <view v-for="(item,index) in options" :data-button="btn" :key="index" :style="{
        backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
        fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
      }"
         class="uni-swipe_button button-hock" @click.stop="onClick(index,item)"><text class="uni-swipe_button-text" :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text></view>
      </view>
      <view ref='selector-content-hock' class="selector-query-hock" @touchstart="touchstart" @touchmove="touchmove"
       @touchend="touchend" :class="{'ani':uniShow}" :style="{transform:moveLeft}">
        <view class="uni-swipe_move-box"  >
          <view class="uni-swipe_box">
            <slot />
          </view>
        </view>
      </view>
    </view>
    <!-- #endif -->
    <!-- #ifdef MP-ALIPAY -->
    <view class="uni-swipe-box" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
      <view class="viewWidth-hook">
        <movable-area v-if="viewWidth !== 0" class="movable-area" :style="{width:(viewWidth-buttonWidth)+'px'}">
          <movable-view class="movable-view" direction="horizontal" :animation="!transition" :style="{width:viewWidth+'px'}"
           :class="[transition?'transition':'']" :x="x" :disabled="disabledView" @change="onChange">
            <view class="movable-view-box">
              <slot></slot>
            </view>
          </movable-view>
        </movable-area>
      </view>
      <view ref="selector-button-hock" class="uni-swipe_button-group viewWidth-hook">
        <view v-for="(item,index) in options" :data-button="btn" :key="index" :style="{
          backgroundColor: item.style && item.style.backgroundColor ? item.style.backgroundColor : '#C7C6CD',
          fontSize: item.style && item.style.fontSize ? item.style.fontSize : '16px'
        }"
         class="uni-swipe_button button-hock" @click.stop="onClick(index,item)"><text class="uni-swipe_button-text" :style="{color: item.style && item.style.color ? item.style.color : '#FFFFFF',}">{{ item.text }}</text></view>
      </view>
    </view>
    <!-- #endif -->
  </view>
</template>
<script src="./index.wxs" module="swipe" lang="wxs"></script>
<script>
  // #ifdef APP-VUE|| MP-WEIXIN || H5
  import mpwxs from './mpwxs'
  // #endif

  // #ifdef APP-NVUE
  import bindingx from './bindingx.js'
  // #endif

  // #ifndef APP-PLUS|| MP-WEIXIN || MP-ALIPAY ||  H5
  import mixins from './mpother'
  // #endif

  // #ifdef MP-ALIPAY
  import mpalipay from './mpalipay'
  // #endif

  /**
   * SwipeActionItem 滑动操作子组件
   * @description 通过滑动触发选项的容器
   * @tutorial https://ext.dcloud.net.cn/plugin?id=181
   * @property {Boolean} show = [true|false] 开启关闭组件,auto-close = false 时生效
   * @property {Boolean} disabled = [true|false] 是否禁止滑动
   * @property {Boolean} autoClose = [true|false] 其他组件开启的时候,当前组件是否自动关闭
   * @property {Array} options 组件选项内容及样式
   * @event {Function} click 点击选项按钮时触发事件,e = {content,index} ,content(点击内容)、index(下标)
   * @event {Function} change 组件打开或关闭时触发,true:开启状态;false:关闭状态
   */

  export default {
    // #ifdef APP-VUE|| MP-WEIXIN||H5
    mixins: [mpwxs],
    // #endif

    // #ifdef APP-NVUE
    mixins: [bindingx],
    // #endif

    // #ifndef APP-PLUS|| MP-WEIXIN || MP-ALIPAY ||  H5
    mixins: [mixins],
    // #endif

    // #ifdef MP-ALIPAY
    mixins: [mpalipay],
    // #endif

    props: {
      /**
       * 按钮内容
       */
      options: {
        type: Array,
        default () {
          return []
        }
      },
      /**
       * 禁用
       */
      disabled: {
        type: Boolean,
        default: false
      },
      /**
       * 变量控制开关
       */
      show: {
        type: Boolean,
        default: false
      },
      /**
       * 是否自动关闭
       */
      autoClose: {
        type: Boolean,
        default: true
      }
    },
    inject: ['swipeaction']


  }
</script>
<style lang="scss" scoped>
  .uni-swipe {
    overflow: hidden;
  }

  .uni-swipe-box {
    position: relative;
    width: 100%;
  }

  .uni-swipe_content {
    flex: 1;
    position: relative;
  }

  .uni-swipe_move-box {
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
    position: relative;
    flex-direction: row;
  }

  .uni-swipe_box {
    /* #ifndef APP-NVUE */
    display: flex;
    flex-direction: row;
    width: 100%;
    flex-shrink: 0;
    /* #endif */
    /* #ifdef APP-NVUE */
    flex: 1;
    /* #endif */
    font-size: 14px;
    background-color: #fff;
  }

  .uni-swipe_button-group {
    /* #ifndef APP-VUE|| MP-WEIXIN||H5 */
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 0;
    /* #endif */
    /* #ifndef APP-NVUE */
    display: flex;
    flex-shrink: 0;
    /* #endif */
    flex-direction: row;
  }

  .uni-swipe_button {
    /* #ifdef APP-NVUE */
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    /* #endif */
    /* #ifndef APP-NVUE */
    display: flex;
    /* #endif */
    flex-direction: row;
    justify-content: center;
    align-items: center;
    padding: 0 20px;
  }

  .uni-swipe_button-text {
    /* #ifndef APP-NVUE */
    flex-shrink: 0;
    /* #endif */
    font-size: 14px;
    background-position: center;
    background-size: contain;
  }

  .ani {
    transition-property: transform;
    transition-duration: 0.3s;
    transition-timing-function: cubic-bezier(0.165, 0.84, 0.44, 1);
  }

  /* #ifdef MP-ALIPAY */
  .movable-area {
    width: 300px;
    height: 100%;
    height: 45px;
  }

  .movable-view {
    position: relative;
    width: 160%;
    height: 45px;
    z-index: 2;
  }
  .transition {
    transition: all 0.3s;
  }

  .movable-view-box {
    width: 100%;
    height: 100%;
    background-color: #fff;
  }
  /* #endif */
</style>

index.vue

<template>
  <view class="content">
    <view class="list" v-for="(item, index) in list" :key="index">
      <uni-swipe-action>
        <uni-swipe-action-item :options="options" @click="deleteItem(index)">
          <div class="item">{{item}}</div>
        </uni-swipe-action-item>
      </uni-swipe-action>
    </view>
  </view>
</template>

<script>
  import uniSwipeAction from '@/components/uni-swipe-action/uni-swipe-action.vue'
  import uniSwipeActionItem from '@/components/uni-swipe-action-item/uni-swipe-action-item.vue'

  export default {
    components: {
      uniSwipeAction,
      uniSwipeActionItem
    },
    data() {
      return {
        list: [
          'one one one one one',
          'two two two two two',
          'three three three three three',
          'four four four four four',
          'five five five five five',
          'six six six six six',
          'seven seven seven seven seven',
          'eight eight eight eight eight'
        ],
        options: [{
          style: {
            backgroundImage: 'url("/static/delete.png")'
          }
        }],
      }
    },
    methods: {
      deleteItem (index) {
        this.list.splice(index, 1)
      }
    }
  }
</script>

<style>
  .content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background: #F2F2F2;
  }

  .list {
    width: 750rpx;
    margin: 20rpx 0 0 0;
  }

  .item {
    font-size: 36rpx;
    color: #000000;
  }
</style>

 

Logo

前往低代码交流专区

更多推荐