booking-consume-detail-dialog.vue 6.21 KB
<template>
  <el-dialog
    :visible.sync="visibleProxy"
    :show-close="false"
    width="520px"
    :close-on-click-modal="false"
    custom-class="booking-consume-detail-dialog"
    append-to-body
  >
    <div v-if="booking" class="inner">
      <div class="header">
        <div class="title">预约消耗单详情</div>
        <span class="close" @click="visibleProxy = false"><i class="el-icon-close"></i></span>
      </div>

      <div class="body">
        <div class="row"><span class="label">会员</span><span class="value">{{ booking.memberName || '无' }}</span></div>
        <div class="row"><span class="label">预约时间</span><span class="value">{{ booking.date }} {{ booking.startTime }}-{{ booking.endTime }}</span></div>
        <div class="row"><span class="label">房间</span><span class="value">{{ booking.roomName || '无' }}</span></div>
        <div class="row">
          <span class="label">健康师</span>
          <span class="value">
            <span v-if="therapistNames.length">{{ therapistNames.join('、') }}</span>
            <span v-else>无</span>
          </span>
        </div>
        <div class="row">
          <span class="label">项目/品项</span>
          <span class="value">
            <span v-if="itemLabels.length">{{ itemLabels.join('、') }}</span>
            <span v-else>无</span>
          </span>
        </div>
        <div class="row">
          <span class="label">状态</span>
          <span :class="['value', 'status', 'status--' + (booking.status || 'booked')]">{{ statusText }}</span>
        </div>
        <div class="row" v-if="booking.remark">
          <span class="label">备注</span><span class="value">{{ booking.remark }}</span>
        </div>
      </div>

      <div class="footer">
        <el-button size="small" @click="visibleProxy = false">关 闭</el-button>
        <el-button
          v-if="booking.status !== 'cancelled' && booking.status !== 'converted'"
          size="small"
          class="danger-btn"
          @click="$emit('cancel', booking)"
        >
          取消预约
        </el-button>
        <el-button
          v-if="booking.status === 'booked'"
          type="primary"
          size="small"
          @click="$emit('start', booking)"
        >
          开始服务
        </el-button>
        <el-button
          v-if="booking.status !== 'converted'"
          type="primary"
          plain
          size="small"
          @click="$emit('convert', booking)"
        >
          转消耗
        </el-button>
      </div>
    </div>
  </el-dialog>
</template>

<script>
export default {
  name: 'BookingConsumeDetailDialog',
  props: {
    visible: { type: Boolean, default: false },
    booking: { type: Object, default: null }
  },
  computed: {
    visibleProxy: {
      get() { return this.visible },
      set(v) { this.$emit('update:visible', v) }
    },
    therapistNames() {
      const b = this.booking || {}
      if (Array.isArray(b.therapistNames) && b.therapistNames.length) return b.therapistNames
      return []
    },
    itemLabels() {
      const b = this.booking || {}
      if (Array.isArray(b.itemLabels) && b.itemLabels.length) return b.itemLabels
      if (Array.isArray(b.items) && b.items.length) {
        return b.items.map(x => {
          const base = x.label || ''
          const cnt = (x.count != null ? x.count : x.qty)
          const suffix = cnt != null ? `×${cnt}` : ''
          return `${base}${suffix}`.trim()
        }).filter(Boolean)
      }
      return []
    },
    statusText() {
      const s = (this.booking && this.booking.status) || 'booked'
      const map = {
        booked: '已预约',
        serving: '服务中',
        converted: '已转消耗',
        cancelled: '已取消'
      }
      return map[s] || '已预约'
    }
  }
}
</script>

<style lang="scss" scoped>
.inner {
  padding: 18px 22px 14px;
}

.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
  padding: 10px 14px;
  border-radius: 14px;
  background: rgba(219, 234, 254, 0.96);
}

.title {
  font-size: 17px;
  font-weight: 600;
  color: #0f172a;
}

.close {
  cursor: pointer;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  color: #64748b;
  transition: all 0.15s;
  &:hover { background: rgba(0, 0, 0, 0.06); color: #0f172a; }
}

.body {
  padding: 4px 0 16px;
}

.row {
  display: flex;
  padding: 10px 0;
  border-bottom: 1px solid #f1f5f9;
  font-size: 14px;
}

.label {
  width: 96px;
  color: #64748b;
  flex-shrink: 0;
}

.value {
  flex: 1;
  min-width: 0;
  color: #111827;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.status {
  font-weight: 600;
}
.status--booked { color: #2563eb; }
.status--serving { color: #f97316; }
.status--converted { color: #16a34a; }
.status--cancelled { color: #ef4444; }

.footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid #f1f5f9;
}

.danger-btn {
  border-radius: 999px !important;
  background: rgba(254, 226, 226, 0.8) !important;
  color: #b91c1c !important;
  border-color: rgba(239, 68, 68, 0.25) !important;
}

::v-deep .booking-consume-detail-dialog {
  max-width: 520px;
  margin-top: 10vh !important;
  border-radius: 20px;
  padding: 0;
  background: radial-gradient(circle at 0 0, rgba(255,255,255,0.96) 0, rgba(248,250,252,0.98) 40%, rgba(241,245,249,0.98) 100%);
  box-shadow: 0 24px 48px rgba(15,23,42,0.18), 0 0 0 1px rgba(255,255,255,0.9);
  backdrop-filter: blur(22px);
  -webkit-backdrop-filter: blur(22px);
}
::v-deep .booking-consume-detail-dialog .el-dialog__header { display: none; }
::v-deep .booking-consume-detail-dialog .el-dialog__body { padding: 0; }
::v-deep .booking-consume-detail-dialog .el-button--primary {
  border-radius: 999px;
  padding: 0 18px;
  height: 30px;
  line-height: 30px;
  background: #2563eb;
  border-color: #2563eb;
  box-shadow: 0 4px 10px rgba(37, 99, 235, 0.35);
  font-size: 12px;
}
::v-deep .booking-consume-detail-dialog .el-button--default {
  border-radius: 999px;
  padding: 0 18px;
  height: 30px;
  line-height: 30px;
  background: rgba(239, 246, 255, 0.9);
  color: #2563eb;
  border-color: rgba(37, 99, 235, 0.18);
  font-size: 12px;
}
</style>