|
|
@@ -4,7 +4,7 @@
|
|
|
<template #header>
|
|
|
<div class="header-title">
|
|
|
<span>券码管理</span>
|
|
|
- <span class="product-id">权益名称:{{ equityName }}</span>
|
|
|
+ <span class="product-id">{{ equityName }}</span>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -12,9 +12,12 @@
|
|
|
<el-button type="primary" @click="openImportDialog">
|
|
|
<el-icon><Upload /></el-icon>导入券码
|
|
|
</el-button>
|
|
|
+ <el-button @click="openRecycleRecordDialog">
|
|
|
+ <el-icon><Document /></el-icon>回收记录
|
|
|
+ </el-button>
|
|
|
</div>
|
|
|
|
|
|
- <el-table :data="batchList" stripe style="width: 100%">
|
|
|
+ <el-table :data="pagedBatchList" stripe style="width: 100%">
|
|
|
<el-table-column prop="importCount" label="导入数量" width="100"></el-table-column>
|
|
|
<el-table-column prop="actualImportCount" label="实际导入数量" width="120"></el-table-column>
|
|
|
<el-table-column prop="failCount" label="导入失败数量" width="120"></el-table-column>
|
|
|
@@ -45,6 +48,17 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
+
|
|
|
+ <div class="pagination-section">
|
|
|
+ <span>Total {{ batchList.length }}</span>
|
|
|
+ <el-pagination
|
|
|
+ v-model:current-page="currentPage"
|
|
|
+ v-model:page-size="pageSize"
|
|
|
+ layout="prev, pager, next, sizes, jumper"
|
|
|
+ :page-sizes="[10, 20, 50]"
|
|
|
+ :total="batchList.length"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
</el-card>
|
|
|
|
|
|
<el-dialog
|
|
|
@@ -66,15 +80,18 @@
|
|
|
|
|
|
<el-form label-width="100px" label-position="left" style="margin-top: 24px;">
|
|
|
<el-form-item label="回收数量" required>
|
|
|
- <el-input-number
|
|
|
- v-model="recycleForm.amount"
|
|
|
- :min="1"
|
|
|
- :max="recycleMaxAmount"
|
|
|
- :placeholder="`输入范围1-${recycleMaxAmount}`"
|
|
|
- :controls-position="''"
|
|
|
- style="width: 100%"
|
|
|
- ></el-input-number>
|
|
|
- <div class="recycle-tip">本批次{{ currentRecycleTypeLabel }}最多可回收数量:{{ recycleMaxAmount }}</div>
|
|
|
+ <span v-if="recycleForm.type === 'expired'">{{ recycleMaxAmount }}</span>
|
|
|
+ <template v-else>
|
|
|
+ <el-input-number
|
|
|
+ v-model="recycleForm.amount"
|
|
|
+ :min="1"
|
|
|
+ :max="recycleMaxAmount"
|
|
|
+ :placeholder="`输入范围1-${recycleMaxAmount}`"
|
|
|
+ :controls-position="''"
|
|
|
+ style="width: 100%"
|
|
|
+ ></el-input-number>
|
|
|
+ <div class="recycle-tip">本批次{{ currentRecycleTypeLabel }}最多可回收数量:{{ recycleMaxAmount }}</div>
|
|
|
+ </template>
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
|
|
|
@@ -101,7 +118,11 @@
|
|
|
<el-icon><Upload /></el-icon>选择文件
|
|
|
</el-button>
|
|
|
<template #tip>
|
|
|
- <div class="el-upload__tip">支持 .xlsx / .csv,仅可上传一个文件</div>
|
|
|
+ <div class="el-upload__tip">
|
|
|
+ <div>*只支持模板格式,请先下载模板再导入</div>
|
|
|
+ <div>*不能导入相同的券码</div>
|
|
|
+ <div>*最多支持50000条,若超过请分次导入</div>
|
|
|
+ </div>
|
|
|
<el-button type="text" @click="downloadTemplate">
|
|
|
<el-icon><Download /></el-icon>模板下载
|
|
|
</el-button>
|
|
|
@@ -116,6 +137,23 @@
|
|
|
</span>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <el-dialog title="回收记录" v-model="recycleRecordDialogVisible" width="900px">
|
|
|
+ <el-table :data="recycleRecords" stripe style="width: 100%">
|
|
|
+ <el-table-column prop="recycleTime" label="回收时间" width="180"></el-table-column>
|
|
|
+ <el-table-column prop="type" label="类型" width="150"></el-table-column>
|
|
|
+ <el-table-column prop="operator" label="操作人" width="120"></el-table-column>
|
|
|
+ <el-table-column prop="amount" label="回收数量" width="120"></el-table-column>
|
|
|
+ <el-table-column label="操作" min-width="140">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-button type="text" size="small" @click="downloadRecycleCoupons(scope.row)">下载回收券码</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="recycleRecordDialogVisible = false">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -128,15 +166,22 @@ export default {
|
|
|
return {
|
|
|
importDialogVisible: false,
|
|
|
recycleDialogVisible: false,
|
|
|
+ recycleRecordDialogVisible: false,
|
|
|
+ currentPage: 1,
|
|
|
+ pageSize: 10,
|
|
|
recycleRow: null,
|
|
|
+ recycleRecords: [
|
|
|
+ { recycleTime: '2026-05-10 14:22:08', type: '未发放券码回收', operator: '张三', amount: 120 },
|
|
|
+ { recycleTime: '2026-04-28 09:15:33', type: '已过期券码回收', operator: '李四', amount: 80 },
|
|
|
+ { recycleTime: '2026-04-12 16:40:51', type: '未发放券码回收', operator: '王五', amount: 200 }
|
|
|
+ ],
|
|
|
recycleForm: {
|
|
|
type: 'unissued',
|
|
|
amount: undefined
|
|
|
},
|
|
|
recycleTypeOptions: [
|
|
|
{ label: '未发放券码回收', value: 'unissued' },
|
|
|
- { label: '已过期券码回收', value: 'expired' },
|
|
|
- { label: '导错码券码回收', value: 'wrong' }
|
|
|
+ { label: '已过期券码回收', value: 'expired' }
|
|
|
],
|
|
|
importForm: {
|
|
|
files: []
|
|
|
@@ -185,6 +230,94 @@ export default {
|
|
|
fileStatus: '处理失败',
|
|
|
importTime: '2026-05-01 16:45:30',
|
|
|
creator: '赵六'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 1500,
|
|
|
+ actualImportCount: 1500,
|
|
|
+ failCount: 0,
|
|
|
+ remainingCount: 1280,
|
|
|
+ effectiveTime: '2026-04-10 00:00:00',
|
|
|
+ expireTime: '2026-12-31 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-04-09 09:21:05',
|
|
|
+ creator: '张三'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 600,
|
|
|
+ actualImportCount: 598,
|
|
|
+ failCount: 2,
|
|
|
+ remainingCount: 420,
|
|
|
+ effectiveTime: '2026-04-05 00:00:00',
|
|
|
+ expireTime: '2026-11-30 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-04-04 14:02:33',
|
|
|
+ creator: '李四'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 300,
|
|
|
+ actualImportCount: 300,
|
|
|
+ failCount: 0,
|
|
|
+ remainingCount: 0,
|
|
|
+ effectiveTime: '2026-03-20 00:00:00',
|
|
|
+ expireTime: '2026-09-30 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-03-19 10:45:12',
|
|
|
+ creator: '王五'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 2000,
|
|
|
+ actualImportCount: 1995,
|
|
|
+ failCount: 5,
|
|
|
+ remainingCount: 1620,
|
|
|
+ effectiveTime: '2026-03-15 00:00:00',
|
|
|
+ expireTime: '2026-12-31 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-03-14 16:30:48',
|
|
|
+ creator: '张三'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 450,
|
|
|
+ actualImportCount: 450,
|
|
|
+ failCount: 0,
|
|
|
+ remainingCount: 130,
|
|
|
+ effectiveTime: '2026-03-01 00:00:00',
|
|
|
+ expireTime: '2026-08-31 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-02-28 11:18:27',
|
|
|
+ creator: '李四'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 800,
|
|
|
+ actualImportCount: 796,
|
|
|
+ failCount: 4,
|
|
|
+ remainingCount: 510,
|
|
|
+ effectiveTime: '2026-02-20 00:00:00',
|
|
|
+ expireTime: '2026-10-31 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-02-19 15:55:09',
|
|
|
+ creator: '王五'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 1200,
|
|
|
+ actualImportCount: 1200,
|
|
|
+ failCount: 0,
|
|
|
+ remainingCount: 60,
|
|
|
+ effectiveTime: '2026-02-10 00:00:00',
|
|
|
+ expireTime: '2026-07-31 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-02-09 09:40:51',
|
|
|
+ creator: '赵六'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ importCount: 350,
|
|
|
+ actualImportCount: 348,
|
|
|
+ failCount: 2,
|
|
|
+ remainingCount: 0,
|
|
|
+ effectiveTime: '2026-01-15 00:00:00',
|
|
|
+ expireTime: '2026-06-30 23:59:59',
|
|
|
+ fileStatus: '处理成功',
|
|
|
+ importTime: '2026-01-14 13:27:36',
|
|
|
+ creator: '张三'
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
@@ -193,6 +326,10 @@ export default {
|
|
|
productId() {
|
|
|
return this.$route.params.id || ''
|
|
|
},
|
|
|
+ pagedBatchList() {
|
|
|
+ const start = (this.currentPage - 1) * this.pageSize
|
|
|
+ return this.batchList.slice(start, start + this.pageSize)
|
|
|
+ },
|
|
|
equityName() {
|
|
|
const map = {
|
|
|
'Q100001': '权益A',
|
|
|
@@ -217,7 +354,6 @@ export default {
|
|
|
const ts = row.expireTime ? new Date(row.expireTime).getTime() : 0
|
|
|
return ts && ts < Date.now() ? Number(row.remainingCount) || 0 : 0
|
|
|
}
|
|
|
- case 'wrong': return Number(row.failCount) || 0
|
|
|
default: return 0
|
|
|
}
|
|
|
}
|
|
|
@@ -280,15 +416,22 @@ export default {
|
|
|
handleExportFail(row) {
|
|
|
ElMessage.success(`已开始导出失败券码(${row.failCount} 条)`)
|
|
|
},
|
|
|
+ openRecycleRecordDialog() {
|
|
|
+ this.recycleRecordDialogVisible = true
|
|
|
+ },
|
|
|
+ downloadRecycleCoupons() {
|
|
|
+ ElMessage.success('已开始下载回收券码')
|
|
|
+ },
|
|
|
resetRecycleForm() {
|
|
|
this.recycleRow = null
|
|
|
this.recycleForm = { type: 'unissued', amount: undefined }
|
|
|
},
|
|
|
confirmRecycle() {
|
|
|
const max = this.recycleMaxAmount
|
|
|
- const amount = Number(this.recycleForm.amount)
|
|
|
+ // 已过期券码回收:数量为文本展示,直接按可回收数量全部回收
|
|
|
+ const amount = this.recycleForm.type === 'expired' ? max : Number(this.recycleForm.amount)
|
|
|
if (!amount || amount < 1) {
|
|
|
- ElMessage.warning('请输入回收数量')
|
|
|
+ ElMessage.warning(this.recycleForm.type === 'expired' ? '当前批次没有可回收的已过期券码' : '请输入回收数量')
|
|
|
return
|
|
|
}
|
|
|
if (amount > max) {
|
|
|
@@ -298,6 +441,13 @@ export default {
|
|
|
if (this.recycleRow && this.recycleForm.type === 'unissued') {
|
|
|
this.recycleRow.remainingCount = Math.max(0, Number(this.recycleRow.remainingCount) - amount)
|
|
|
}
|
|
|
+ const typeLabel = (this.recycleTypeOptions.find(o => o.value === this.recycleForm.type) || {}).label || ''
|
|
|
+ this.recycleRecords.unshift({
|
|
|
+ recycleTime: this.formatNow(),
|
|
|
+ type: typeLabel,
|
|
|
+ operator: '当前用户',
|
|
|
+ amount
|
|
|
+ })
|
|
|
ElMessage.success('回收成功')
|
|
|
this.recycleDialogVisible = false
|
|
|
}
|
|
|
@@ -375,4 +525,12 @@ export default {
|
|
|
justify-content: center;
|
|
|
gap: 24px;
|
|
|
}
|
|
|
+
|
|
|
+.pagination-section {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+ gap: 20px;
|
|
|
+ margin-top: 20px;
|
|
|
+}
|
|
|
</style>
|