|
|
@@ -8,30 +8,40 @@
|
|
|
<el-option label="已拒绝" value="rejected" />
|
|
|
</el-select>
|
|
|
<el-button size="small" @click="loadData" :loading="loading">刷新</el-button>
|
|
|
+ <el-button v-if="selectedRows.length > 0 && statusFilter === 'pending'" size="small" type="success" @click="handleBatchApprove">批量通过({{ selectedRows.length }})</el-button>
|
|
|
+ <el-button v-if="selectedRows.length > 0 && statusFilter === 'pending'" size="small" type="danger" @click="showBatchRejectDialog">批量驳回({{ selectedRows.length }})</el-button>
|
|
|
</div>
|
|
|
<div class="table-scroll-wrap"><div style="overflow:auto;max-height:calc(100vh - 300px);">
|
|
|
- <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;">
|
|
|
+ <el-table :data="list" v-loading="loading" border stripe style="min-width:900px;" @selection-change="onSelectionChange">
|
|
|
+ <el-table-column type="selection" width="40" v-if="statusFilter === 'pending'" />
|
|
|
<el-table-column prop="id" label="ID" width="60" />
|
|
|
<el-table-column prop="name" label="套餐名称" min-width="180" show-overflow-tooltip />
|
|
|
- <el-table-column prop="cycleDays" label="周期" width="80">
|
|
|
+ <el-table-column prop="cycleDays" label="周期" width="70">
|
|
|
<template slot-scope="{ row }">{{ row.cycleDays }}天</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="price" label="价格" width="100">
|
|
|
+ <el-table-column prop="price" label="价格" width="90">
|
|
|
<template slot-scope="{ row }">{{ formatPrice(row.price) }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="审核状态" width="100">
|
|
|
+ <el-table-column label="审核状态" width="90">
|
|
|
<template slot-scope="{ row }">
|
|
|
- <el-tag :type="statusType(row.status)">{{ statusLabel(row.status) }}</el-tag>
|
|
|
+ <el-tag :type="statusType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="rejectReason" label="拒绝原因" min-width="150" show-overflow-tooltip>
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <span v-if="row.rejectReason" style="color:#f56c6c;">{{ row.rejectReason }}</span>
|
|
|
+ <span v-else style="color:#999;">-</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="createdAt" label="申请时间" width="160" />
|
|
|
- <el-table-column label="操作" width="180" fixed="right">
|
|
|
+ <el-table-column label="操作" width="200" fixed="right">
|
|
|
<template slot-scope="{ row }">
|
|
|
<template v-if="row.status === 'pending'">
|
|
|
+ <el-button size="mini" type="primary" @click="showDetail(row)">查看</el-button>
|
|
|
<el-button size="mini" type="success" @click="handleApprove(row)">通过</el-button>
|
|
|
- <el-button size="mini" type="danger" @click="handleReject(row)">拒绝</el-button>
|
|
|
+ <el-button size="mini" type="danger" @click="showRejectDialog(row)">驳回</el-button>
|
|
|
</template>
|
|
|
- <span v-else style="color:#999">-</span>
|
|
|
+ <el-button v-else size="mini" type="primary" @click="showDetail(row)">查看</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table></div></div>
|
|
|
@@ -42,9 +52,52 @@
|
|
|
:page-size="size"
|
|
|
:total="total"
|
|
|
layout="total, prev, pager, next"
|
|
|
- @current-change="loadData"
|
|
|
+ @current-change="onPageChange"
|
|
|
style="margin-top:12px;text-align:right;"
|
|
|
/>
|
|
|
+
|
|
|
+ <el-dialog title="套餐详情" :visible.sync="detailVisible" width="700px" top="5vh">
|
|
|
+ <div v-if="currentRow" style="max-height:60vh;overflow-y:auto;">
|
|
|
+ <el-form label-width="110px" size="small">
|
|
|
+ <el-form-item label="ID">{{ currentRow.id }}</el-form-item>
|
|
|
+ <el-form-item label="套餐名称">{{ currentRow.name }}</el-form-item>
|
|
|
+ <el-form-item label="状态"><el-tag :type="statusType(currentRow.status)">{{ statusLabel(currentRow.status) }}</el-tag></el-form-item>
|
|
|
+ <el-form-item label="周期">{{ currentRow.cycleDays }} 天</el-form-item>
|
|
|
+ <el-form-item label="价格">{{ formatPrice(currentRow.price) }}</el-form-item>
|
|
|
+ <el-form-item label="创建时间">{{ currentRow.createdAt }}</el-form-item>
|
|
|
+ <el-form-item label="详细介绍" v-if="currentRow.description">
|
|
|
+ <div style="border:1px solid #eee;padding:10px;border-radius:4px;background:#fafafa;white-space:pre-wrap;">{{ currentRow.description }}</div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="拒绝原因" v-if="currentRow.rejectReason" style="color:#f56c6c;">
|
|
|
+ {{ currentRow.rejectReason }}
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+ <span slot="footer" v-if="currentRow && currentRow.status === 'pending'">
|
|
|
+ <el-button @click="detailVisible = false">取消</el-button>
|
|
|
+ <el-button type="success" @click="handleApproveFromDialog">通过</el-button>
|
|
|
+ <el-button type="danger" @click="showRejectFromDialog">驳回</el-button>
|
|
|
+ </span>
|
|
|
+ <span slot="footer" v-else>
|
|
|
+ <el-button @click="detailVisible = false">关闭</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog title="驳回原因" :visible.sync="rejectDialogVisible" width="450px">
|
|
|
+ <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入驳回原因" />
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button @click="rejectDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmReject">确认驳回</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog title="批量驳回" :visible.sync="batchRejectDialogVisible" width="450px">
|
|
|
+ <el-input v-model="batchRejectReason" type="textarea" :rows="3" placeholder="请输入驳回原因" />
|
|
|
+ <span slot="footer">
|
|
|
+ <el-button @click="batchRejectDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="danger" @click="confirmBatchReject">确认批量驳回({{ selectedRows.length }}条)</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
@@ -60,7 +113,14 @@ export default {
|
|
|
statusFilter: '',
|
|
|
page: 1,
|
|
|
size: 20,
|
|
|
- total: 0
|
|
|
+ total: 0,
|
|
|
+ selectedRows: [],
|
|
|
+ detailVisible: false,
|
|
|
+ rejectDialogVisible: false,
|
|
|
+ batchRejectDialogVisible: false,
|
|
|
+ rejectReason: '',
|
|
|
+ batchRejectReason: '',
|
|
|
+ currentRow: null
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
@@ -72,8 +132,12 @@ export default {
|
|
|
try {
|
|
|
const res = await getPackageList({})
|
|
|
if (res.code === 200) {
|
|
|
- this.list = Array.isArray(res.data) ? res.data : (res.data && res.data.records) || []
|
|
|
- this.total = this.list.length
|
|
|
+ let data = Array.isArray(res.data) ? res.data : (res.data && res.data.records) || []
|
|
|
+ if (this.statusFilter) {
|
|
|
+ data = data.filter(item => item.status === this.statusFilter)
|
|
|
+ }
|
|
|
+ this.list = data
|
|
|
+ this.total = data.length
|
|
|
}
|
|
|
} catch (e) {
|
|
|
this.$message.error('加载失败')
|
|
|
@@ -81,6 +145,27 @@ export default {
|
|
|
this.loading = false
|
|
|
}
|
|
|
},
|
|
|
+ onPageChange(p) {
|
|
|
+ this.page = p
|
|
|
+ this.loadData()
|
|
|
+ },
|
|
|
+ onSelectionChange(rows) {
|
|
|
+ this.selectedRows = rows
|
|
|
+ },
|
|
|
+ showDetail(row) {
|
|
|
+ this.currentRow = row
|
|
|
+ this.rejectReason = ''
|
|
|
+ this.detailVisible = true
|
|
|
+ },
|
|
|
+ handleApproveFromDialog() {
|
|
|
+ if (!this.currentRow) return
|
|
|
+ this.handleApprove(this.currentRow)
|
|
|
+ this.detailVisible = false
|
|
|
+ },
|
|
|
+ showRejectFromDialog() {
|
|
|
+ this.detailVisible = false
|
|
|
+ this.rejectDialogVisible = true
|
|
|
+ },
|
|
|
async handleApprove(row) {
|
|
|
try {
|
|
|
await this.$confirm('确认通过该套餐?', '提示', { type: 'warning' })
|
|
|
@@ -91,16 +176,54 @@ export default {
|
|
|
if (e !== 'cancel') this.$message.error('操作失败')
|
|
|
}
|
|
|
},
|
|
|
- async handleReject(row) {
|
|
|
+ showRejectDialog(row) {
|
|
|
+ this.currentRow = row
|
|
|
+ this.rejectReason = ''
|
|
|
+ this.rejectDialogVisible = true
|
|
|
+ },
|
|
|
+ async confirmReject() {
|
|
|
+ if (!this.rejectReason.trim()) return this.$message.warning('请填写驳回原因')
|
|
|
+ if (!this.currentRow) return
|
|
|
+ this.rejectDialogVisible = false
|
|
|
try {
|
|
|
- await this.$confirm('确认拒绝该套餐?', '提示', { type: 'warning' })
|
|
|
- await reviewPackage({ id: row.id, action: 'reject' })
|
|
|
- this.$message.success('已拒绝')
|
|
|
+ await reviewPackage({ id: this.currentRow.id, action: 'reject', reason: this.rejectReason })
|
|
|
+ this.$message.success('已驳回')
|
|
|
+ this.loadData()
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error('操作失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ showBatchRejectDialog() {
|
|
|
+ this.batchRejectReason = ''
|
|
|
+ this.batchRejectDialogVisible = true
|
|
|
+ },
|
|
|
+ async handleBatchApprove() {
|
|
|
+ try {
|
|
|
+ await this.$confirm(`确认通过所选 ${this.selectedRows.length} 个套餐?`, '提示', { type: 'warning' })
|
|
|
+ for (const row of this.selectedRows) {
|
|
|
+ await reviewPackage({ id: row.id, action: 'approve' })
|
|
|
+ }
|
|
|
+ this.$message.success('已批量通过')
|
|
|
+ this.selectedRows = []
|
|
|
this.loadData()
|
|
|
} catch (e) {
|
|
|
if (e !== 'cancel') this.$message.error('操作失败')
|
|
|
}
|
|
|
},
|
|
|
+ async confirmBatchReject() {
|
|
|
+ if (!this.batchRejectReason.trim()) return this.$message.warning('请填写驳回原因')
|
|
|
+ this.batchRejectDialogVisible = false
|
|
|
+ try {
|
|
|
+ for (const row of this.selectedRows) {
|
|
|
+ await reviewPackage({ id: row.id, action: 'reject', reason: this.batchRejectReason })
|
|
|
+ }
|
|
|
+ this.$message.success('已批量驳回')
|
|
|
+ this.selectedRows = []
|
|
|
+ this.loadData()
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error('操作失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
formatPrice(val) {
|
|
|
if (val == null) return '-'
|
|
|
return '¥' + (val / 100).toFixed(2)
|
|
|
@@ -109,12 +232,12 @@ export default {
|
|
|
return { approved: 'success', pending: 'warning', rejected: 'danger' }[s] || 'info'
|
|
|
},
|
|
|
statusLabel(s) {
|
|
|
- return { approved: '已通过', pending: '待审核', rejected: '已拒绝' }[s] || s
|
|
|
+ return { approved: '已通过', pending: '待审核', rejected: '已驳回' }[s] || s
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
-.filter-bar { display: flex; gap: 12px; align-items: center; margin-bottom: 12px; padding: 12px; background: #fff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.08); }
|
|
|
-</style>
|
|
|
+.filter-bar { display: flex; gap: 12px; align-items: center; margin-bottom: 12px; padding: 12px; background: #fff; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.08); flex-wrap: wrap; }
|
|
|
+</style>
|