transfer-card-list-dialog.vue 10.5 KB
<template>
  <el-dialog
    :visible.sync="visibleProxy"
    :show-close="false"
    width="90%"
    :close-on-click-modal="false"
    custom-class="transfer-card-list-dialog"
    append-to-body
  >
    <div class="dialog-inner">
      <div class="dialog-header">
        <div class="dialog-title">转卡明细</div>
        <span class="dialog-close" @click="visibleProxy = false"><i class="el-icon-close"></i></span>
      </div>

      <div class="dialog-search">
        <el-form @submit.native.prevent :inline="true" size="small" label-width="80px">
          <el-form-item label="转卡时间">
            <el-date-picker
              v-model="query.transferTime"
              type="daterange"
              value-format="timestamp"
              format="yyyy-MM-dd"
              start-placeholder="开始"
              end-placeholder="结束"
              style="width:220px"
            />
          </el-form-item>
          <el-form-item label="会员">
            <el-select
              v-model="query.memberId"
              filterable
              remote
              reserve-keyword
              clearable
              placeholder="搜索会员"
              :remote-method="searchMember"
              :loading="memberLoading"
              style="width:200px"
            >
              <el-option v-for="m in memberOptions" :key="m.value" :label="m.label" :value="m.value" />
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="search">查询</el-button>
            <el-button @click="reset">重置</el-button>
          </el-form-item>
        </el-form>
      </div>

      <div class="dialog-content">
        <el-table
          v-loading="loading"
          :data="list"
          border
          size="small"
          :header-cell-style="{ background:'#f8fafc', color:'#64748b', fontWeight:600 }"
        >
          <el-table-column label="转出会员" min-width="120" show-overflow-tooltip>
            <template slot-scope="{ row }">
              <span class="cell-nowrap"><i class="el-icon-user cell-icon cell-icon--from"></i>{{ emptyText(row.transferFromMemberName) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="转入会员" min-width="120" show-overflow-tooltip>
            <template slot-scope="{ row }">
              <span class="cell-nowrap"><i class="el-icon-user cell-icon cell-icon--to"></i>{{ emptyText(row.transferToMemberName) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="转卡金额" width="120" align="right">
            <template slot-scope="{ row }">
              <span class="cell-nowrap amount-text">¥{{ formatMoney(row.transferAmount) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="转卡时间" width="160" show-overflow-tooltip>
            <template slot-scope="{ row }">
              <span class="cell-nowrap">{{ formatDateTime(row.transferTime) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="退卡单号" min-width="140" show-overflow-tooltip>
            <template slot-scope="{ row }">
              <span class="cell-nowrap">{{ emptyText(row.refundCardId) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="开单单号" min-width="140" show-overflow-tooltip>
            <template slot-scope="{ row }">
              <span class="cell-nowrap">{{ emptyText(row.billingCardId) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="备注" min-width="160" show-overflow-tooltip>
            <template slot-scope="{ row }">
              <span class="cell-nowrap">{{ emptyText(row.remarks) }}</span>
            </template>
          </el-table-column>
        </el-table>
      </div>

      <div class="dialog-footer">
        <el-pagination
          background
          layout="total, sizes, prev, pager, next, jumper"
          :total="total"
          :page-size.sync="listQuery.pageSize"
          :current-page.sync="listQuery.currentPage"
          :page-sizes="[10, 20, 50, 100]"
          @size-change="initData"
          @current-change="initData"
        />
      </div>
    </div>
  </el-dialog>
</template>

<script>
import { getCardTransferLogList } from '@/api/lqCardTransferLog'
import { getKhxxList } from '@/api/lqKhxx'

export default {
  name: 'TransferCardListDialog',
  props: {
    visible: { type: Boolean, default: false },
    currentStoreId: { type: String, default: '' }
  },
  data() {
    return {
      loading: false,
      memberLoading: false,
      memberOptions: [],
      list: [],
      total: 0,
      query: {
        transferTime: undefined,
        memberId: undefined
      },
      listQuery: { currentPage: 1, pageSize: 10, sort: 'desc', sidx: '' }
    }
  },
  computed: {
    visibleProxy: {
      get() { return this.visible },
      set(v) { this.$emit('update:visible', v) }
    },
    storeId() {
      return this.currentStoreId || (this.$store.getters.storeInfo || {}).storeId || ''
    }
  },
  watch: {
    visible(v) {
      if (v) {
        if (!this.memberOptions.length) this.searchMember('')
        this.initData()
      }
    }
  },
  methods: {
    emptyText(val) {
      return val != null && String(val).trim() !== '' ? val : '无'
    },
    formatDateTime(val) {
      if (!val) return '无'
      if (typeof val === 'string' && val.includes('-')) return val.replace('T', ' ').substring(0, 19)
      const d = new Date(val)
      if (Number.isNaN(d.getTime())) return '无'
      const pad = n => String(n).padStart(2, '0')
      return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
    },
    formatMoney(v) {
      return v != null ? Number(v).toFixed(2) : '0.00'
    },
    buildTransferTimeRange(dateRange) {
      if (!dateRange || dateRange.length !== 2) {
        return { transferStartTime: undefined, transferEndTime: undefined }
      }
      const start = new Date(dateRange[0])
      start.setHours(0, 0, 0, 0)
      const end = new Date(dateRange[1])
      end.setHours(23, 59, 59, 999)
      return {
        transferStartTime: start.toISOString(),
        transferEndTime: end.toISOString()
      }
    },
    async searchMember(kw) {
      this.memberLoading = true
      try {
        const res = await getKhxxList({
          currentPage: 1,
          pageSize: 30,
          keyWord: (kw || '').trim()
        })
        const list = res.data?.list || []
        this.memberOptions = list.map(m => {
          const name = m.khmc || m.name || '无'
          const phone = m.sjh || m.mobile || ''
          return {
            value: m.id,
            label: phone ? `${name}(${this.$maskMobile(phone)})` : name
          }
        })
      } catch (e) {
        this.memberOptions = []
      } finally {
        this.memberLoading = false
      }
    },
    buildListQuery() {
      const q = {
        ...this.listQuery,
        storeId: this.storeId,
        isEffective: 1
      }
      const { transferStartTime, transferEndTime } = this.buildTransferTimeRange(this.query.transferTime)
      if (transferStartTime) q.transferStartTime = transferStartTime
      if (transferEndTime) q.transferEndTime = transferEndTime
      if (this.query.memberId) {
        q.transferFromMemberId = this.query.memberId
      }
      return q
    },
    async initData() {
      if (!this.storeId) {
        this.list = []
        this.total = 0
        return
      }
      this.loading = true
      try {
        const res = await getCardTransferLogList(this.buildListQuery())
        this.list = res.data?.list || []
        this.total = res.data?.pagination?.total || 0
      } catch (e) {
        this.list = []
        this.total = 0
      } finally {
        this.loading = false
      }
    },
    search() {
      this.listQuery.currentPage = 1
      this.initData()
    },
    reset() {
      this.query = {
        transferTime: undefined,
        memberId: undefined
      }
      this.listQuery = { currentPage: 1, pageSize: 10, sort: 'desc', sidx: '' }
      this.initData()
    }
  }
}
</script>

<style lang="scss" scoped>
::v-deep .transfer-card-list-dialog {
  max-width: 1600px;
  margin-top: 3vh !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 .el-dialog__header { display: none; }
::v-deep .el-dialog__body { padding: 0; }
.dialog-inner { display: flex; flex-direction: column; max-height: 92vh; }
.dialog-header {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 18px 22px 0;
  padding: 10px 14px;
  border-radius: 14px;
  background: rgba(204,251,241,0.96);
}
.dialog-title { font-size: 17px; font-weight: 600; color: #0f172a; }
.dialog-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; }
}
.dialog-search { flex-shrink: 0; padding: 12px 22px 4px; }
.dialog-content { flex: 1; min-height: 0; overflow: auto; padding: 0 22px; }
.dialog-footer {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  padding: 10px 22px 14px;
  border-top: 1px solid rgba(229,231,235,0.6);
}
.cell-nowrap { white-space: nowrap; }
.cell-icon { margin-right: 4px; }
.cell-icon--from { color: #409EFF; }
.cell-icon--to { color: #67C23A; }
.amount-text { color: #409EFF; font-weight: 600; }
::v-deep .transfer-card-list-dialog .el-input__inner {
  border-radius: 999px;
  height: 32px;
  line-height: 32px;
  border-color: #e5e7eb;
  background-color: #f9fafb;
  &:focus {
    border-color: #2563eb;
    box-shadow: 0 0 0 1px rgba(37,99,235,0.18);
  }
}
::v-deep .transfer-card-list-dialog .el-button--primary {
  border-radius: 999px;
  background: #2563eb;
  border-color: #2563eb;
  box-shadow: 0 4px 10px rgba(37,99,235,0.35);
}
::v-deep .transfer-card-list-dialog .el-button--default { border-radius: 999px; }
::v-deep .transfer-card-list-dialog .el-form-item { margin-bottom: 8px; }
::v-deep .transfer-card-list-dialog .el-form-item__label { white-space: nowrap; }
::v-deep .transfer-card-list-dialog .el-range-editor.el-input__inner { border-radius: 999px; }
</style>