| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- <template>
- <div class="coupon-management admin-page">
- <el-card>
- <div slot="header" class="admin-page-header">
- <span class="admin-page-title">优惠券管理</span>
- <div class="admin-page-actions">
- <el-input v-model="keyword" placeholder="搜索优惠券..." prefix-icon="el-icon-search" clearable @keyup.enter.native="handleSearch" @clear="handleSearch" />
- <el-button type="primary" size="small" @click="handleCreate">新建优惠券</el-button>
- <el-button type="success" size="small" @click="handleBatchIssue">批量发放</el-button>
- </div>
- </div>
- <div class="table-scroll-wrap-sm">
- <div style="overflow:auto;max-height:calc(100vh - 300px);">
- <el-table style="max-height:calc(100vh - 300px);" :data="list" v-loading="loading" border stripe>
- <el-table-column prop="id" label="ID" width="70" />
- <el-table-column prop="name" label="名称" min-width="140" show-overflow-tooltip />
- <el-table-column label="类型" width="90">
- <template slot-scope="{ row }">
- <el-tag size="mini">{{ row.type }}</el-tag>
- </template>
- </el-table-column>
- <el-table-column label="面值(元)" width="90">
- <template slot-scope="{ row }">{{ (row.value / 100).toFixed(2) }}</template>
- </el-table-column>
- <el-table-column label="使用门槛(元)" width="110">
- <template slot-scope="{ row }">{{ row.minSpend ? (row.minSpend / 100).toFixed(2) : '无' }}</template>
- </el-table-column>
- <el-table-column prop="applicableTo" label="适用对象" width="110">
- <template slot-scope="{ row }">{{ applicableLabel(row.applicableTo) }}</template>
- </el-table-column>
- <el-table-column label="有效期" width="180">
- <template slot-scope="{ row }">{{ row.validFrom }} ~ {{ row.validUntil }}</template>
- </el-table-column>
- <el-table-column prop="totalCount" label="总量" width="70" />
- <el-table-column prop="usedCount" label="已用" width="70" />
- <el-table-column label="状态" width="80">
- <template slot-scope="{ row }">
- <el-tag :type="row.status === 'active' ? 'success' : 'info'" size="mini">
- {{ row.status === 'active' ? '启用' : '停用' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="180" fixed="right">
- <template slot-scope="{ row }">
- <el-button size="mini" type="primary" @click="handleEdit(row)">编辑</el-button>
- <el-dropdown trigger="hover" @command="(cmd) => handleActionCmd(row, cmd)">
- <el-button size="mini">
- 更多<i class="el-icon-arrow-down el-icon--right"></i>
- </el-button>
- <el-dropdown-menu slot="dropdown">
- <el-dropdown-item command="delete" icon="el-icon-delete">删除</el-dropdown-item>
- </el-dropdown-menu>
- </el-dropdown>
- </template>
- </el-table-column>
- </el-table></div>
- </div>
- <el-pagination
- @current-change="onPageChange"
- @size-change="onPageChange"
- :current-page="page"
- :page-size="size"
- :total="total"
- layout="total, prev, pager, next"
- class="pagination-wrap"
- />
- </el-card>
- <el-dialog :visible.sync="formDialogVisible" :title="isEdit ? '编辑优惠券' : '新建优惠券'" width="550px">
- <el-form :model="form" label-width="120px" ref="couponForm">
- <el-form-item label="优惠券名称" required>
- <el-input v-model="form.name" placeholder="请输入优惠券名称" />
- </el-form-item>
- <el-form-item label="类型" required>
- <el-select v-model="form.type" placeholder="请选择类型" style="width: 100%">
- <el-option label="FIXED" value="FIXED" />
- </el-select>
- </el-form-item>
- <el-form-item label="面值(元)" required>
- <el-input-number v-model="form.valueYuan" :min="0.01" :step="0.5" :precision="2" style="width: 200px" />
- </el-form-item>
- <el-form-item label="使用门槛(元)">
- <el-input-number v-model="form.minSpendYuan" :min="0" :step="10" :precision="2" style="width: 200px" />
- </el-form-item>
- <el-form-item label="适用对象">
- <el-select v-model="form.applicableTo" placeholder="请选择适用对象" style="width: 100%">
- <el-option label="全部商品" value="ALL" />
- <el-option label="会员服务" value="MEMBERSHIP" />
- <el-option label="特定商品" value="PRODUCT" />
- <el-option label="测评服务" value="ASSESSMENT" />
- </el-select>
- </el-form-item>
- <el-form-item label="有效期起" required>
- <el-date-picker v-model="form.validFrom" type="date" placeholder="选择开始日期" value-format="yyyy-MM-dd" style="width: 100%" />
- </el-form-item>
- <el-form-item label="有效期止" required>
- <el-date-picker v-model="form.validUntil" type="date" placeholder="选择结束日期" value-format="yyyy-MM-dd" style="width: 100%" />
- </el-form-item>
- <el-form-item label="发放总量" required>
- <el-input-number v-model="form.totalCount" :min="1" :step="100" style="width: 200px" />
- </el-form-item>
- </el-form>
- <div slot="footer">
- <el-button @click="formDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="handleSubmit" :loading="submitting">保存</el-button>
- </div>
- </el-dialog>
- <el-dialog title="批量发放优惠券" :visible.sync="batchDialogVisible" width="500px">
- <el-form label-width="120px">
- <el-form-item label="选择优惠券" required>
- <el-select v-model="batchCouponId" placeholder="请选择优惠券" style="width: 100%">
- <el-option v-for="c in couponOptions" :key="c.id" :label="c.name" :value="c.id" />
- </el-select>
- </el-form-item>
- <el-form-item label="用户ID列表" required>
- <el-input v-model="batchUserIds" type="textarea" :rows="6" placeholder="每行一个用户ID 例如: 1001 1002 1003" />
- </el-form-item>
- </el-form>
- <div slot="footer">
- <el-button @click="batchDialogVisible = false">取消</el-button>
- <el-button type="primary" @click="confirmBatchIssue" :loading="batchSubmitting">确认发放</el-button>
- </div>
- </el-dialog>
- </div>
- </template>
- <script>
- import { getCouponList, createCoupon, updateCoupon, deleteCoupon, batchIssueCoupons } from '@/api/coupon'
- export default {
- name: 'CouponManagement',
- data() {
- return {
- list: [],
- loading: false,
- page: 1,
- size: 20,
- total: 0,
- formDialogVisible: false,
- isEdit: false,
- submitting: false,
- form: this.getEmptyForm(),
- batchDialogVisible: false,
- batchCouponId: '',
- batchUserIds: '',
- batchSubmitting: false,
- couponOptions: [],
- keyword: ''
- }
- },
- created() {
- this.loadData()
- },
- methods: {
- getEmptyForm() {
- return {
- id: null,
- name: '',
- type: 'FIXED',
- valueYuan: null,
- minSpendYuan: null,
- applicableTo: 'ALL',
- validFrom: '',
- validUntil: '',
- totalCount: null
- }
- },
- applicableLabel(val) {
- const map = { ALL: '全部商品', MEMBERSHIP: '会员服务', PRODUCT: '特定商品', ASSESSMENT: '测评服务' }
- return map[val] || val
- },
- async loadData() {
- this.loading = true
- try {
- const res = await getCouponList({ page: this.page, size: this.size, keyword: this.keyword || undefined })
- if (res.data) {
- this.list = res.data.records || res.data.list || res.data
- this.total = res.data.total || this.list.length
- }
- } catch (e) {
- console.error(e)
- } finally {
- this.loading = false
- }
- },
- handleSearch() {
- this.page = 1
- this.loadData()
- },
- onPageChange(p) {
- if (p) this.page = p
- this.loadData()
- },
- handleCreate() {
- this.isEdit = false
- this.form = this.getEmptyForm()
- this.formDialogVisible = true
- },
- handleEdit(row) {
- this.isEdit = true
- this.form = {
- id: row.id,
- name: row.name,
- type: row.type,
- valueYuan: row.value / 100,
- minSpendYuan: row.minSpend ? row.minSpend / 100 : null,
- applicableTo: row.applicableTo,
- validFrom: row.validFrom,
- validUntil: row.validUntil,
- totalCount: row.totalCount
- }
- this.formDialogVisible = true
- },
- async handleDelete(row) {
- try {
- await this.$confirm('确定删除该优惠券吗?', '提示', { type: 'warning' })
- await deleteCoupon(row.id)
- this.$message.success('删除成功')
- this.loadData()
- } catch (e) {
- if (e !== 'cancel') this.$message.error('删除失败')
- }
- },
- async handleSubmit() {
- if (!this.form.name || !this.form.valueYuan || !this.form.totalCount || !this.form.validFrom || !this.form.validUntil) {
- this.$message.warning('请填写完整信息')
- return
- }
- this.submitting = true
- try {
- const payload = {
- name: this.form.name,
- type: this.form.type,
- value: Math.round(this.form.valueYuan * 100),
- minSpend: this.form.minSpendYuan ? Math.round(this.form.minSpendYuan * 100) : 0,
- applicableTo: this.form.applicableTo,
- validFrom: this.form.validFrom,
- validUntil: this.form.validUntil,
- totalCount: this.form.totalCount
- }
- if (this.isEdit) payload.id = this.form.id
- if (this.isEdit) {
- await updateCoupon(payload)
- } else {
- await createCoupon(payload)
- }
- this.$message.success('保存成功')
- this.formDialogVisible = false
- this.loadData()
- } catch (e) {
- this.$message.error('保存失败')
- } finally {
- this.submitting = false
- }
- },
- async handleBatchIssue() {
- this.batchCouponId = ''
- this.batchUserIds = ''
- this.batchDialogVisible = true
- try {
- const res = await getCouponList({ page: 1, size: 999 })
- this.couponOptions = res.data.records || res.data.list || res.data || []
- } catch (e) {
- console.error(e)
- }
- },
- async confirmBatchIssue() {
- if (!this.batchCouponId) {
- this.$message.warning('请选择优惠券')
- return
- }
- const ids = this.batchUserIds.split('\n').map(s => s.trim()).filter(Boolean)
- if (ids.length === 0) {
- this.$message.warning('请输入至少一个用户ID')
- return
- }
- this.batchSubmitting = true
- try {
- await batchIssueCoupons({ couponId: this.batchCouponId, userIds: ids })
- this.$message.success(`成功向 ${ids.length} 个用户发放优惠券`)
- this.batchDialogVisible = false
- this.loadData()
- } catch (e) {
- this.$message.error('发放失败')
- } finally {
- this.batchSubmitting = false
- }
- },
- handleActionCmd(row, cmd) {
- switch (cmd) {
- case 'delete':
- this.handleDelete(row)
- break
- }
- }
- }
- }
- </script>
- <style scoped>
- .coupon-management { padding: 20px; }
- </style>
|