index.vue 4.29 KB
<template>
  <view class="button-bar">
    <view v-if="showMoreButton" class="more-link" @click="toggleMore">更多</view>
    <view class="button-grid" :style="{ gridTemplateColumns: gridColumns }">
      <view v-for="(btn, idx) in visibleButtons" :key="idx" class="action-button" :class="buttonClass(btn)" :style="buttonStyle(btn)" @click="emitClick(btn)">{{ btn.text }}</view>
    </view>
    <view v-if="moreVisible" class="popover-mask" @click="closeMore"></view>
    <view v-if="moreVisible" class="more-popover" :style="popoverStyle()">
      <view v-for="(btn, idx) in moreButtons" :key="idx" class="menu-item" :class="menuClass(btn)" @click="emitMoreClick(btn)">{{ btn.text }}</view>
    </view>
  </view>
</template>

<script>
export default {
  name: 'DetailButtons',
  props: {
    buttons: { type: Array, default: () => [] }
  },
  data() {
    return { moreVisible: false }
  },
  computed: {
    effectiveButtons() {
      const list = this.buttons || []
      return list.filter(it => it && it.visible !== false)
    },
    showMoreButton() {
      return (this.effectiveButtons || []).length >= 4
    },
    visibleButtons() {
      const list = this.effectiveButtons || []
      if (list.length <= 3) return list
      return list.slice(0, 2)
    },
    moreButtons() {
      const list = this.effectiveButtons || []
      return list.length >= 4 ? list.slice(2) : []
    },
    gridColumns() {
      const len = (this.effectiveButtons || []).length
      if (this.showMoreButton) return '1fr 1fr'
      if (len <= 1) return '1fr'
      if (len === 2) return '1fr 1fr'
      return '1fr 1fr 1fr'
    }
  },
  methods: {
    toggleMore() { this.moreVisible = !this.moreVisible },
    closeMore() { this.moreVisible = false },
    emitClick(btn) {
      if (!btn || btn.disabled) return
      this.$emit('click', btn)
    },
    emitMoreClick(btn) { this.emitClick(btn); this.closeMore() },
    buttonClass(btn) {
      const v = (btn && btn.variant) || 'outline'
      let cls = ''
      if (v === 'primary') cls = 'action-button--primary'
      else if (v === 'danger') cls = 'action-button--danger'
      else cls = 'action-button--outline'
      if (btn && btn.disabled) cls += ' is-disabled'
      return cls
    },
    buttonStyle(btn) { return (btn && btn.style) ? btn.style : {} },
    menuClass(btn) {
      const v = (btn && btn.variant) || 'outline'
      if (v === 'danger') return 'menu-item--danger'
      return 'menu-item--primary'
    },
    menuStyle(btn) { return (btn && btn.style) ? btn.style : {} },
    popoverStyle() { return { left: '24rpx' } }
  }
}
</script>

<style lang="scss" scoped>
  .button-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: #fff;
    padding: 32rpx;
    box-shadow: 0 -8rpx 24rpx rgba(0, 0, 0, 0.03);
    padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
    display: flex;
    align-items: center;
    gap: 24rpx;
    position: fixed;
    z-index: 10;
  }

  .more-link {
    color: $theme-primary;
    font-size: 28rpx;
    line-height: 1;
    padding: 20rpx 0;
  }

  .button-grid {
    flex: 1;
    display: grid;
    gap: 16rpx;
  }

  .action-button {
    text-align: center;
    height: 80rpx;
    line-height: 80rpx;
    border-radius: 12rpx;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .action-button--outline { 
    border: 2rpx solid $theme-primary; color: $theme-primary; background: #fff; 
    line-height: 78rpx;
  }
  .action-button--primary { 
    border: 2rpx solid $theme-primary; background: $theme-primary; color: #fff; 
    line-height: 78rpx;
  }
  .action-button--danger { 
    border: 2rpx solid #ff4d4f; background: #ff4d4f; color: #fff; 
    line-height: 78rpx;
  }
  .is-disabled { opacity: 0.5; }

  .popover-mask {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    background: transparent;
    z-index: 9;
  }

  .more-popover {
    position: absolute;
    bottom: calc(100% + 12rpx);
    background: #fff;
    border-radius: 12rpx;
    box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.06);
    padding: 8rpx 0;
    min-width: 360rpx;
    z-index: 11;
  }

  .menu-item {
    padding: 16rpx 20rpx;
    font-size: 28rpx;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  .menu-item--primary { color: $theme-primary; }
  .menu-item--danger { color: #ff4d4f; }
</style>