98 lines
8.5 KiB
Vue
98 lines
8.5 KiB
Vue
<template>
|
||
<div class="app-container">
|
||
<el-form v-show="showSearch" ref="queryForm" :model="queryParams" size="small" :inline="true">
|
||
<el-form-item label="任务名称" prop="taskName"><el-input v-model="queryParams.taskName" clearable placeholder="任务名称" @keyup.enter.native="handleQuery" /></el-form-item>
|
||
<el-form-item label="用户" prop="userName"><el-input v-model="queryParams.userName" clearable placeholder="账号或昵称" @keyup.enter.native="handleQuery" /></el-form-item>
|
||
<el-form-item label="状态" prop="status"><el-select v-model="queryParams.status" clearable placeholder="全部"><el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
|
||
<el-form-item label="周期" prop="cycleKey"><el-input v-model="queryParams.cycleKey" clearable placeholder="ONCE或2026-09-16" /></el-form-item>
|
||
<el-form-item><el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></el-form-item>
|
||
</el-form>
|
||
<right-toolbar :show-search.sync="showSearch" @queryTable="getList" />
|
||
|
||
<el-table v-loading="loading" :data="submissionList">
|
||
<el-table-column label="提交单号" prop="submissionNo" min-width="185" />
|
||
<el-table-column label="任务" prop="taskName" min-width="160" show-overflow-tooltip />
|
||
<el-table-column label="用户" min-width="130"><template slot-scope="scope"><div>{{ scope.row.nickName || scope.row.userName }}</div><small class="muted">ID: {{ scope.row.userId }}</small></template></el-table-column>
|
||
<el-table-column label="周期" prop="cycleKey" align="center" width="110" />
|
||
<el-table-column label="积分" prop="rewardPoints" align="center" width="75" />
|
||
<el-table-column label="提交次数" prop="attemptCount" align="center" width="85" />
|
||
<el-table-column label="状态" align="center" width="100"><template slot-scope="scope"><el-tag :type="statusType(scope.row.status)" size="small">{{ statusLabel(scope.row.status) }}</el-tag></template></el-table-column>
|
||
<el-table-column label="提交时间" prop="lastSubmitTime" width="165" />
|
||
<el-table-column label="操作" align="center" width="225" fixed="right">
|
||
<template slot-scope="scope">
|
||
<el-button v-hasPermi="['app:pointTaskReview:query']" type="text" icon="el-icon-view" size="mini" @click="handleView(scope.row)">详情</el-button>
|
||
<el-button v-if="scope.row.status === 0" v-hasPermi="['app:pointTaskReview:edit']" type="text" icon="el-icon-check" size="mini" @click="handleApprove(scope.row)">通过</el-button>
|
||
<el-button v-if="scope.row.status === 0" v-hasPermi="['app:pointTaskReview:edit']" type="text" icon="el-icon-close" size="mini" @click="openReason(scope.row, 'reject')">驳回</el-button>
|
||
<el-button v-if="scope.row.status === 1" v-hasPermi="['app:pointTaskReview:revoke']" type="text" icon="el-icon-refresh-left" size="mini" @click="openReason(scope.row, 'revoke')">撤销</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
|
||
|
||
<el-dialog title="任务提交详情" :visible.sync="detailOpen" width="820px" append-to-body>
|
||
<el-descriptions v-if="detail.id" :column="2" border>
|
||
<el-descriptions-item label="提交单号" :span="2">{{ detail.submissionNo }}</el-descriptions-item>
|
||
<el-descriptions-item label="任务名称">{{ detail.taskName }}</el-descriptions-item>
|
||
<el-descriptions-item label="奖励积分">{{ detail.rewardPoints }}</el-descriptions-item>
|
||
<el-descriptions-item label="用户">{{ detail.nickName || detail.userName }}({{ detail.userId }})</el-descriptions-item>
|
||
<el-descriptions-item label="任务周期">{{ detail.cycleKey }}</el-descriptions-item>
|
||
<el-descriptions-item label="状态"><el-tag :type="statusType(detail.status)" size="small">{{ statusLabel(detail.status) }}</el-tag></el-descriptions-item>
|
||
<el-descriptions-item label="提交次数">{{ detail.attemptCount }}</el-descriptions-item>
|
||
<el-descriptions-item label="用户说明" :span="2"><div class="pre-wrap">{{ detail.remark || '-' }}</div></el-descriptions-item>
|
||
<el-descriptions-item v-if="detail.rejectReason" label="驳回/撤销原因" :span="2">{{ detail.rejectReason }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
<h4>任务截图</h4>
|
||
<div class="image-list">
|
||
<div v-for="item in detail.attachments || []" :key="item.id" class="image-card" :class="{ history: item.isCurrent !== 1 }">
|
||
<el-image :src="item.previewUrl" :preview-src-list="previewUrls" fit="cover" />
|
||
<div>{{ item.isCurrent === 1 ? '当前截图' : '历史第' + item.submitBatch + '次' }}</div>
|
||
</div>
|
||
</div>
|
||
<div slot="footer"><el-button @click="detailOpen=false">关闭</el-button></div>
|
||
</el-dialog>
|
||
|
||
<el-dialog :title="reasonMode === 'reject' ? '驳回任务' : '撤销积分奖励'" :visible.sync="reasonOpen" width="520px" append-to-body>
|
||
<el-input v-model="reason" type="textarea" :rows="4" maxlength="300" show-word-limit :placeholder="reasonMode === 'reject' ? '请填写审核未通过原因' : '请填写撤销原因'" />
|
||
<el-alert v-if="reasonMode === 'revoke'" title="撤销将扣回已发放积分;用户积分不足时操作会失败。" type="warning" :closable="false" show-icon style="margin-top:15px" />
|
||
<div slot="footer"><el-button @click="reasonOpen=false">取消</el-button><el-button type="primary" :loading="submitting" @click="submitReason">确定</el-button></div>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listPointTaskSubmission, getPointTaskSubmission, approvePointTaskSubmission, rejectPointTaskSubmission, revokePointTaskSubmission } from '@/api/app/pointTask'
|
||
|
||
export default {
|
||
name: 'PointTaskReview',
|
||
data() {
|
||
return {
|
||
loading: false, submitting: false, showSearch: true, total: 0, submissionList: [], detailOpen: false, detail: {}, reasonOpen: false, reason: '', reasonMode: 'reject', currentRow: {},
|
||
statusOptions: [{ value: 0, label: '待审核' }, { value: 1, label: '已通过' }, { value: 2, label: '已驳回' }, { value: 3, label: '已撤销' }],
|
||
queryParams: { pageNum: 1, pageSize: 10, taskName: undefined, userName: undefined, status: undefined, cycleKey: undefined }
|
||
}
|
||
},
|
||
computed: { previewUrls() { return (this.detail.attachments || []).map(item => item.previewUrl).filter(Boolean) } },
|
||
created() { this.getList() },
|
||
methods: {
|
||
getList() { this.loading = true; listPointTaskSubmission(this.queryParams).then(res => { this.submissionList = res.rows || []; this.total = res.total || 0 }).finally(() => { this.loading = false }) },
|
||
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
|
||
resetQuery() { this.resetForm('queryForm'); this.handleQuery() },
|
||
handleView(row) { getPointTaskSubmission(row.id).then(res => { this.detail = res.data; this.detailOpen = true }) },
|
||
handleApprove(row) { this.$modal.confirm('确认通过该任务并发放 ' + row.rewardPoints + ' 积分吗?').then(() => approvePointTaskSubmission(row.id)).then(() => { this.$modal.msgSuccess('审核通过,积分已发放'); this.getList() }).catch(() => {}) },
|
||
openReason(row, mode) { this.currentRow = row; this.reasonMode = mode; this.reason = ''; this.reasonOpen = true },
|
||
submitReason() { const reason = String(this.reason || '').trim(); if (!reason) { this.$modal.msgError('请填写原因'); return } this.submitting = true; const action = this.reasonMode === 'reject' ? rejectPointTaskSubmission : revokePointTaskSubmission; action(this.currentRow.id, reason).then(() => { this.$modal.msgSuccess(this.reasonMode === 'reject' ? '已驳回' : '奖励已撤销'); this.reasonOpen = false; this.getList() }).finally(() => { this.submitting = false }) },
|
||
statusLabel(status) { const item = this.statusOptions.find(v => v.value === status); return item ? item.label : '未知' },
|
||
statusType(status) { return { 0: 'warning', 1: 'success', 2: 'danger', 3: 'info' }[status] || 'info' }
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.muted { color: #909399; }
|
||
.pre-wrap { white-space: pre-wrap; }
|
||
.image-list { display: flex; flex-wrap: wrap; gap: 14px; }
|
||
.image-card { width: 150px; text-align: center; color: #606266; }
|
||
.image-card .el-image { width: 150px; height: 150px; border-radius: 6px; }
|
||
.image-card.history { opacity: .58; }
|
||
</style>
|