|
|
@@ -55,6 +55,8 @@
|
|
|
</el-button>
|
|
|
<el-dropdown-menu slot="dropdown">
|
|
|
<el-dropdown-item command="viewMembers" icon="el-icon-user">查看成员</el-dropdown-item>
|
|
|
+ <el-dropdown-item command="viewCoupons" icon="el-icon-ticket">优惠券列表</el-dropdown-item>
|
|
|
+ <el-dropdown-item command="viewUsage" icon="el-icon-document">使用记录</el-dropdown-item>
|
|
|
<el-dropdown-item command="issueCoupon" icon="el-icon-ticket">发放优惠券</el-dropdown-item>
|
|
|
<el-dropdown-item command="delete" icon="el-icon-delete">删除</el-dropdown-item>
|
|
|
</el-dropdown-menu>
|
|
|
@@ -174,12 +176,106 @@
|
|
|
<el-button type="primary" @click="confirmIssueCoupon" :loading="couponSubmitting">确认发放</el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 优惠券列表弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ title="优惠券列表"
|
|
|
+ :visible.sync="couponDialogVisible"
|
|
|
+ width="900px"
|
|
|
+ >
|
|
|
+ <el-form :inline="true" style="margin-bottom: 12px">
|
|
|
+ <el-form-item label="状态">
|
|
|
+ <el-select v-model="couponStatusFilter" placeholder="全部" clearable size="small" style="width:120px">
|
|
|
+ <el-option label="可用" value="AVAILABLE"></el-option>
|
|
|
+ <el-option label="已使用" value="USED"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <el-button size="small" @click="loadCoupons(currentRow.id)">刷新</el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <el-table :data="couponListData" border v-loading="couponLoading" style="width:100%" max-height="400">
|
|
|
+ <el-table-column prop="name" label="券名称" min-width="140"></el-table-column>
|
|
|
+ <el-table-column prop="type" label="类型" width="80">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag size="mini" :type="scope.row.type === 'DISCOUNT' ? '' : 'success'">{{ scope.row.type === 'DISCOUNT' ? '折扣' : '满减' }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="面值/折扣" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span v-if="scope.row.type === 'DISCOUNT' && scope.row.discountRate">{{ (scope.row.discountRate / 100).toFixed(1) }}折</span>
|
|
|
+ <span v-else-if="scope.row.value">{{ (scope.row.value / 100).toFixed(2) }}元</span>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="状态" width="80">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag size="mini" :type="scope.row.status === 'AVAILABLE' ? 'success' : 'info'">{{ scope.row.status === 'AVAILABLE' ? '可用' : '已使用' }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="receivedAt" label="领取时间" width="160">
|
|
|
+ <template slot-scope="scope">{{ formatTime(scope.row.receivedAt) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="usedAt" label="使用时间" width="160">
|
|
|
+ <template slot-scope="scope">{{ formatTime(scope.row.usedAt) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-pagination
|
|
|
+ style="margin-top: 12px; text-align: right"
|
|
|
+ @size-change="onCouponSizeChange"
|
|
|
+ @current-change="onCouponPageChange"
|
|
|
+ :current-page="couponPagination.current"
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
+ :page-size="couponPagination.size"
|
|
|
+ :total="couponPagination.total"
|
|
|
+ layout="total, sizes, prev, pager, next"
|
|
|
+ ></el-pagination>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 使用记录弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ title="使用记录"
|
|
|
+ :visible.sync="usageDialogVisible"
|
|
|
+ width="800px"
|
|
|
+ >
|
|
|
+ <el-table :data="usageListData" border v-loading="usageLoading" style="width:100%" max-height="400">
|
|
|
+ <el-table-column prop="name" label="券名称" min-width="140"></el-table-column>
|
|
|
+ <el-table-column prop="type" label="类型" width="80">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-tag size="mini" :type="scope.row.type === 'DISCOUNT' ? '' : 'success'">{{ scope.row.type === 'DISCOUNT' ? '折扣' : '满减' }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="面值/折扣" width="100">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <span v-if="scope.row.type === 'DISCOUNT' && scope.row.discountRate">{{ (scope.row.discountRate / 100).toFixed(1) }}折</span>
|
|
|
+ <span v-else-if="scope.row.value">{{ (scope.row.value / 100).toFixed(2) }}元</span>
|
|
|
+ <span v-else>-</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="orderId" label="订单号" width="120">
|
|
|
+ <template slot-scope="scope">{{ scope.row.orderId || '-' }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="usedAt" label="使用时间" width="160">
|
|
|
+ <template slot-scope="scope">{{ formatTime(scope.row.usedAt) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <el-pagination
|
|
|
+ style="margin-top: 12px; text-align: right"
|
|
|
+ @size-change="onUsageSizeChange"
|
|
|
+ @current-change="onUsagePageChange"
|
|
|
+ :current-page="usagePagination.current"
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
+ :page-size="usagePagination.size"
|
|
|
+ :total="usagePagination.total"
|
|
|
+ layout="total, sizes, prev, pager, next"
|
|
|
+ ></el-pagination>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import { getFamilyList, getFamilyMembers, updateFamily, deleteFamily } from '@/api/admin'
|
|
|
-import { getCouponList, issueFamilyCoupon } from '@/api/coupon'
|
|
|
+import { getCouponList, issueFamilyCoupon, getFamilyCoupons, getFamilyCouponUsage } from '@/api/coupon'
|
|
|
|
|
|
export default {
|
|
|
name: 'Families',
|
|
|
@@ -224,7 +320,16 @@ export default {
|
|
|
quantity: 1
|
|
|
},
|
|
|
couponOptions: [],
|
|
|
- couponSubmitting: false
|
|
|
+ couponSubmitting: false,
|
|
|
+ currentRow: null,
|
|
|
+ couponListData: [],
|
|
|
+ couponLoading: false,
|
|
|
+ couponPagination: { current: 1, size: 20, total: 0 },
|
|
|
+ couponStatusFilter: '',
|
|
|
+ usageListData: [],
|
|
|
+ usageLoading: false,
|
|
|
+ usagePagination: { current: 1, size: 20, total: 0 },
|
|
|
+ usageDialogVisible: false
|
|
|
}
|
|
|
},
|
|
|
mounted() {
|
|
|
@@ -239,6 +344,8 @@ export default {
|
|
|
handleActionCmd(row, cmd) {
|
|
|
switch (cmd) {
|
|
|
case 'viewMembers': this.handleViewMembers(row); break;
|
|
|
+ case 'viewCoupons': this.handleViewCoupons(row); break;
|
|
|
+ case 'viewUsage': this.handleViewUsage(row); break;
|
|
|
case 'issueCoupon': this.handleIssueCoupon(row); break;
|
|
|
case 'edit': this.handleEdit(row); break;
|
|
|
case 'delete': this.handleDelete(row); break;
|
|
|
@@ -386,6 +493,77 @@ export default {
|
|
|
if (!dateStr) return '-'
|
|
|
const date = new Date(dateStr)
|
|
|
return date.toLocaleString('zh-CN')
|
|
|
+ },
|
|
|
+ formatTime(t) {
|
|
|
+ return t ? t.replace('T', ' ').substring(0, 19) : '-'
|
|
|
+ },
|
|
|
+ handleViewCoupons(row) {
|
|
|
+ this.currentRow = row
|
|
|
+ this.couponPagination = { current: 1, size: 20, total: 0 }
|
|
|
+ this.couponStatusFilter = ''
|
|
|
+ this.couponListData = []
|
|
|
+ this.couponDialogVisible = true
|
|
|
+ this.loadCoupons()
|
|
|
+ },
|
|
|
+ async loadCoupons() {
|
|
|
+ if (!this.currentRow) return
|
|
|
+ this.couponLoading = true
|
|
|
+ try {
|
|
|
+ const res = await getFamilyCoupons({
|
|
|
+ familyId: this.currentRow.id,
|
|
|
+ status: this.couponStatusFilter || undefined,
|
|
|
+ page: this.couponPagination.current,
|
|
|
+ size: this.couponPagination.size
|
|
|
+ })
|
|
|
+ this.couponListData = res.data.records || []
|
|
|
+ this.couponPagination.total = res.data.total || 0
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '加载优惠券列表失败')
|
|
|
+ } finally {
|
|
|
+ this.couponLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onCouponPageChange(val) {
|
|
|
+ this.couponPagination.current = val
|
|
|
+ this.loadCoupons()
|
|
|
+ },
|
|
|
+ onCouponSizeChange(val) {
|
|
|
+ this.couponPagination.size = val
|
|
|
+ this.couponPagination.current = 1
|
|
|
+ this.loadCoupons()
|
|
|
+ },
|
|
|
+ handleViewUsage(row) {
|
|
|
+ this.currentRow = row
|
|
|
+ this.usagePagination = { current: 1, size: 20, total: 0 }
|
|
|
+ this.usageListData = []
|
|
|
+ this.usageDialogVisible = true
|
|
|
+ this.loadUsage()
|
|
|
+ },
|
|
|
+ async loadUsage() {
|
|
|
+ if (!this.currentRow) return
|
|
|
+ this.usageLoading = true
|
|
|
+ try {
|
|
|
+ const res = await getFamilyCouponUsage({
|
|
|
+ familyId: this.currentRow.id,
|
|
|
+ page: this.usagePagination.current,
|
|
|
+ size: this.usagePagination.size
|
|
|
+ })
|
|
|
+ this.usageListData = res.data.records || []
|
|
|
+ this.usagePagination.total = res.data.total || 0
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(error.message || '加载使用记录失败')
|
|
|
+ } finally {
|
|
|
+ this.usageLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onUsagePageChange(val) {
|
|
|
+ this.usagePagination.current = val
|
|
|
+ this.loadUsage()
|
|
|
+ },
|
|
|
+ onUsageSizeChange(val) {
|
|
|
+ this.usagePagination.size = val
|
|
|
+ this.usagePagination.current = 1
|
|
|
+ this.loadUsage()
|
|
|
}
|
|
|
}
|
|
|
}
|