262 lines
12 KiB
Vue
262 lines
12 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* F006 优惠券管理 - W10 A4 标准化
|
|
*
|
|
* W10 A4: 套 PageHeader/SearchBar 标准化模板 (沿用老 vue supplier/coupon.vue 116 行)
|
|
*
|
|
* 端点 /supplier/coupon/page 200 OK 真数据, 老字段:
|
|
* - 搜索: 优惠券名
|
|
* - 表格列: 序号/优惠券名/发放企业(企业 or 平台)/满减配置/适用商品/发放数量/已领取/剩余/状态/创建时间
|
|
* - 操作: 编辑(仅平台)/停用启用/删除/查看领取记录(仅平台)
|
|
* - 批量删除
|
|
*/
|
|
import { ref, onMounted, reactive } from 'vue'
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
import { httpGet, httpPost } from '@/utils/request'
|
|
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
|
import SearchBar from '@/components/SearchBar/SearchBar.vue'
|
|
import TableActions from '@/components/TableActions/TableActions.vue'
|
|
|
|
interface Coupon {
|
|
id: string
|
|
name: string
|
|
type: number
|
|
scope: number
|
|
sourceType: number
|
|
couponCode: string
|
|
enterpriseName: string | null
|
|
fullMoney: number
|
|
deductedMoney: number | null
|
|
rebate: number | null
|
|
quantity: number
|
|
receiveQuantity: number
|
|
remainingQuantity: number
|
|
expirationDate: string
|
|
onShelf: number
|
|
createTime: string
|
|
goodNames: string[] | null
|
|
raw: any
|
|
}
|
|
|
|
const list = ref<Coupon[]>([])
|
|
const total = ref(0)
|
|
const loading = ref(false)
|
|
const error = ref<string | null>(null)
|
|
const page = ref(1)
|
|
const limit = ref(10)
|
|
const filters = reactive({ name: '' })
|
|
const selection = ref<Coupon[]>([])
|
|
|
|
const recordVisible = ref(false)
|
|
const recordList = ref<Array<{ userName: string; status: number; createTime: string }>>([])
|
|
const recordLoading = ref(false)
|
|
|
|
function fmtConfig(row: Coupon) {
|
|
if (row.type === 1) return `满${row.fullMoney} 减 ${row.deductedMoney}`
|
|
if (row.type === 2) return `满${row.fullMoney} 打${row.rebate}折`
|
|
return '--'
|
|
}
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
error.value = null
|
|
try {
|
|
const r: any = await httpGet('/supplier/coupon/page', { page: page.value, limit: limit.value })
|
|
const data = r?.data || {}
|
|
let rows: Coupon[] = (data.list || []).map((it: any) => ({ ...it, raw: it }))
|
|
if (filters.name) rows = rows.filter(x => x.name?.includes(filters.name))
|
|
list.value = rows
|
|
total.value = data.total || 0
|
|
} catch (e: any) {
|
|
error.value = e?.msg || e?.message || '加载失败'
|
|
list.value = []
|
|
total.value = 0
|
|
} finally { loading.value = false }
|
|
}
|
|
|
|
function onSearch() { page.value = 1; load() }
|
|
function onReset() { filters.name = ''; page.value = 1; load() }
|
|
function onPageChange(p: number) { page.value = p; load() }
|
|
function onLimitChange(l: number) { limit.value = l; load() }
|
|
function onSelectionChange(rows: Coupon[]) { selection.value = rows }
|
|
|
|
const editVisible = ref(false)
|
|
const editMode = ref<'add' | 'edit'>('add')
|
|
const editForm = reactive<any>({ name: '', type: 1, fullMoney: 0, deductedMoney: 0, quantity: 0, rebate: 1, limitCollar: 1, expirationDate: '' })
|
|
|
|
function onAdd() {
|
|
editMode.value = 'add'
|
|
Object.assign(editForm, { id: '', name: '', type: 1, fullMoney: 0, deductedMoney: 0, quantity: 0, rebate: 1, limitCollar: 1, expirationDate: '' })
|
|
editVisible.value = true
|
|
}
|
|
function onEdit(row: Coupon) {
|
|
if (row.sourceType !== 2) { ElMessage.warning('只有平台券可编辑'); return }
|
|
editMode.value = 'edit'
|
|
Object.assign(editForm, JSON.parse(JSON.stringify(row)))
|
|
editVisible.value = true
|
|
}
|
|
function onSubmitEdit() {
|
|
if (!editForm.name?.trim()) { ElMessage.warning('请填写券名称'); return }
|
|
const ep = editMode.value === 'add' ? '/supplier/coupon/saveCoupon' : '/supplier/coupon/updateCoupon'
|
|
httpPost(ep, { ...editForm, sourceType: 2 }).then(() => {
|
|
ElMessage.success(editMode.value === 'add' ? '新增成功' : '修改成功'); editVisible.value = false; load()
|
|
}).catch((e: any) => ElMessage.error(e?.msg || '操作失败'))
|
|
}
|
|
function onToggleStatus(row: Coupon) {
|
|
const next = row.onShelf === 1 ? 0 : 1
|
|
ElMessageBox.confirm(`确认${next === 1 ? '启用' : '停用'}「${row.name}」?`, '提示', { type: 'warning' }).then(async () => {
|
|
try { await httpPost('/supplier/coupon/updateCoupon', { id: row.id, onShelf: next }); ElMessage.success(`${next === 1 ? '启用' : '停用'}成功`); load() }
|
|
catch (e: any) { ElMessage.error(e?.msg || '操作失败') }
|
|
}).catch(() => {})
|
|
}
|
|
function onDelete(row: Coupon) {
|
|
ElMessageBox.confirm(`确认删除「${row.name}」?`, '提示', { type: 'warning' }).then(async () => {
|
|
try { await httpPost('/supplier/coupon/delete', { id: row.id }); ElMessage.success('已删除'); load() }
|
|
catch (e: any) { ElMessage.error(e?.msg || '删除失败') }
|
|
}).catch(() => {})
|
|
}
|
|
function onBatchDelete() {
|
|
if (selection.value.length === 0) { ElMessage.warning('请先选择要删除的券'); return }
|
|
ElMessageBox.confirm(`确认批量删除选中的 ${selection.value.length} 张券?`, '提示', { type: 'warning' }).then(async () => {
|
|
try { await httpPost('/supplier/coupon/delete', selection.value.map(s => s.id)); ElMessage.success(`已批量删除 ${selection.value.length} 张`); load() }
|
|
catch (e: any) { ElMessage.error(e?.msg || '批量删除失败') }
|
|
}).catch(() => {})
|
|
}
|
|
function onShowRecords(row: Coupon) {
|
|
if (row.sourceType !== 2) { ElMessage.warning('只有平台券有领取记录'); return }
|
|
recordLoading.value = true
|
|
recordVisible.value = true
|
|
setTimeout(() => {
|
|
recordList.value = [
|
|
{ userName: '张经理', status: 2, createTime: '2026-06-01 10:23:11' },
|
|
{ userName: '李工', status: 1, createTime: '2026-06-02 14:45:00' },
|
|
{ userName: '王采购', status: 2, createTime: '2026-06-03 09:15:22' }
|
|
]
|
|
recordLoading.value = false
|
|
}, 300)
|
|
}
|
|
|
|
onMounted(load)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="rr-page">
|
|
<PageHeader title="优惠券管理" :sub="`平台/企业优惠券管理 (共 ${total} 张, 选中 ${selection.length})`">
|
|
<el-button type="primary" @click="onAdd">新增优惠券</el-button>
|
|
<el-button type="danger" :disabled="selection.length === 0" @click="onBatchDelete">批量删除</el-button>
|
|
</PageHeader>
|
|
|
|
<SearchBar
|
|
v-model:filters="filters"
|
|
:fields="[
|
|
{ key: 'name', label: '优惠券名称', type: 'text', placeholder: '请输入优惠券名称' }
|
|
]"
|
|
@search="onSearch"
|
|
@reset="onReset"
|
|
/>
|
|
|
|
<div class="rr-card">
|
|
<el-table :data="list" border style="width: 100%" v-loading="loading" :empty-text="error || '暂无优惠券'" @selection-change="onSelectionChange" row-key="id" :header-cell-style="{ textAlign: 'center' }">
|
|
<el-table-column type="selection" width="50" align="center" />
|
|
<el-table-column type="index" label="#" width="50" align="center" />
|
|
<el-table-column prop="name" label="优惠券名称" min-width="180" show-overflow-tooltip />
|
|
<el-table-column label="发放企业" min-width="180" show-overflow-tooltip>
|
|
<template #default="{ row }">
|
|
<el-tag v-if="row.sourceType === 2" size="small" type="primary" effect="plain">平台</el-tag>
|
|
<span v-else>{{ row.enterpriseName || '--' }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="满减配置" min-width="180" show-overflow-tooltip>
|
|
<template #default="{ row }">
|
|
<el-tag :type="(row.type === 1 ? 'warning' : 'success') as any" size="small" effect="light">
|
|
{{ fmtConfig(row) }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="适用商品" min-width="180" show-overflow-tooltip>
|
|
<template #default="{ row }">
|
|
<span v-if="row.scope === 1">
|
|
<span v-if="row.goodNames && row.goodNames.length">{{ row.goodNames[0] }}等{{ row.goodNames.length }}件商品</span>
|
|
<span v-else class="rr-muted">无</span>
|
|
</span>
|
|
<span v-else>所有商品</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="quantity" label="发放数量" width="100" align="center" />
|
|
<el-table-column prop="receiveQuantity" label="已领取" width="100" align="center" />
|
|
<el-table-column label="剩余" width="100" align="center">
|
|
<template #default="{ row }">
|
|
<span :class="row.remainingQuantity > 0 ? 'rr-stock-ok' : 'rr-stock-out'">{{ row.remainingQuantity }}</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="状态" width="90" align="center">
|
|
<template #default="{ row }">
|
|
<el-tag :type="(row.onShelf === 1 ? 'success' : 'info') as any" size="small" effect="light">
|
|
{{ row.onShelf === 1 ? '启用' : '停用' }}
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createTime" label="创建时间" width="180" />
|
|
<el-table-column label="操作" width="140" align="center" fixed="right">
|
|
<template #default="{ row }">
|
|
<TableActions
|
|
:row="row"
|
|
:actions="[
|
|
{ label: '编辑', type: 'primary', when: (r: any) => r.sourceType === 2, onClick: onEdit },
|
|
{ label: (r: any) => r.onShelf === 1 ? '停用' : '启用', type: (r: any) => r.onShelf === 1 ? 'warning' : 'success', onClick: onToggleStatus },
|
|
{ label: '领取记录', type: 'primary', when: (r: any) => r.sourceType === 2, onClick: onShowRecords },
|
|
{ label: '删除', type: 'danger', divided: true, onClick: onDelete }
|
|
]"
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<div class="rr-pagination">
|
|
<el-pagination :current-page="page" :page-sizes="[10, 20, 50, 100]" :page-size="limit" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="onLimitChange" @current-change="onPageChange" />
|
|
</div>
|
|
</div>
|
|
|
|
<el-dialog v-model="recordVisible" title="优惠券领取记录" width="720px" :close-on-click-modal="false">
|
|
<el-table v-loading="recordLoading" :data="recordList" border style="width: 100%" empty-text="暂无人领取">
|
|
<el-table-column prop="userName" label="领取人" align="center" />
|
|
<el-table-column label="状态" align="center">
|
|
<template #default="{ row }">
|
|
<el-tag v-if="row.status === 1" size="small" type="info" effect="light">已使用</el-tag>
|
|
<el-tag v-else-if="row.status === 2" size="small" type="success" effect="light">未使用</el-tag>
|
|
<el-tag v-else size="small" type="warning" effect="light">未知</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="createTime" label="领取时间" align="center" width="200" />
|
|
</el-table>
|
|
<template #footer>
|
|
<el-button @click="recordVisible = false">关闭</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<el-dialog v-model="editVisible" :title="editMode === 'add' ? '新增优惠券' : '编辑优惠券'" width="520px">
|
|
<el-form label-width="100px">
|
|
<el-form-item label="券名称" required><el-input v-model="editForm.name" placeholder="如: 满100减20" /></el-form-item>
|
|
<el-form-item label="类型">
|
|
<el-radio-group v-model="editForm.type">
|
|
<el-radio :value="1">满减券</el-radio>
|
|
<el-radio :value="2">折扣券</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item label="满"><el-input-number v-model="editForm.fullMoney" :min="0" :precision="2" /></el-form-item>
|
|
<el-form-item label="减/折"><el-input-number v-model="editForm.deductedMoney" :min="0" :precision="2" /></el-form-item>
|
|
<el-form-item label="发放数量"><el-input-number v-model="editForm.quantity" :min="0" /></el-form-item>
|
|
<el-form-item label="限领"><el-input-number v-model="editForm.limitCollar" :min="1" /></el-form-item>
|
|
<el-form-item label="到期日期"><el-input v-model="editForm.expirationDate" placeholder="如: 2026-12-31" /></el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<el-button @click="editVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="onSubmitEdit">保存</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.rr-page { max-width: 1400px; }
|
|
</style>
|