Files
arenax-system-admin/src/views/activity/game-task.vue
2025-06-26 14:30:57 +08:00

442 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-container>
<el-main>
<div class="game-task-page">
<!-- 二级菜单下拉框 -->
<div class="filter-container">
<el-cascader
v-model="selectedCategories"
:options="categoryOptions"
:props="cascaderProps"
placeholder="请选择商户"
style="width: 300px;"
@change="handleCategoryChange"
@click.native="handleCascaderClick"
/>
<el-button @click="handleReset" style="margin-left: 10px;">重置</el-button>
<el-button class="refresh-btn" @click="handleRefresh" style="margin-left: auto;">
<i class="el-icon-refresh"></i>
<span>刷新</span>
</el-button>
</div>
<el-tabs v-model="activeGame" @tab-click="handleGameChange">
<el-tab-pane
v-for="game in gameList"
:key="game.id"
:label="game.gameName"
:name="game.gameId.toString()"
/>
</el-tabs>
<!-- 任务列表表格 -->
<div class="task-table-container">
<el-table v-loading="loading" :data="taskList" style="width: 100%; max-width: 100%; table-layout: fixed; overflow-x: hidden;" border>
<el-table-column prop="qqNetbarTaskRules" label="任务规则" :resizable="false" align="center" header-align="center" min-width="16%" />
<el-table-column prop="qqNetbarTaskMemo" label="任务描述" :resizable="false" align="center" header-align="center" min-width="16%" />
<el-table-column prop="qqNetbarTaskName" label="任务名称" :resizable="false" align="center" header-align="center" min-width="16%" />
<el-table-column prop="qqNetbarReward" label="奖励名称" :resizable="false" align="center" header-align="center" min-width="16%">
<template slot-scope="scope">
<span>
{{
(scope.row.rewards && scope.row.rewards.filter(r => r.rewardTypeSource === 1).map(r => r.name).join('、')) || '-'
}}
</span>
</template>
</el-table-column>
<el-table-column prop="qqNetbarTargetTime" label="目标次数" :resizable="false" align="center" header-align="center" min-width="16%" />
<el-table-column label="操作" align="center" min-width="16%">
<template slot-scope="scope">
<el-button type="primary" size="mini" @click="configureReward(scope.row)">配置奖励</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-dialog
title="配置奖励"
:visible.sync="rewardTypeDialogVisible"
width="500px"
:close-on-click-modal="false"
>
<div class="reward-preview-list" style="margin-bottom: 16px;">
<div v-if="currentTask && currentTask.rewards && currentTask.rewards.filter(r => r.rewardTypeSource === 1).length" style="margin-bottom: 8px;">
<div v-for="reward in (currentTask.rewards || []).filter(r => r.rewardTypeSource === 1)" :key="reward.id" class="reward-preview-item" style="display: flex; align-items: center; margin-bottom: 6px;">
<img v-if="reward.imageUrl" :src="reward.imageUrl" alt="奖励图片" style="width: 40px; height: 40px; object-fit: contain; border: 1px solid #eee; border-radius: 6px; margin-right: 10px;" />
<span style="flex:1;">{{ reward.name }}类型{{ reward.rewardTypeName || '-' }}数量{{ reward.grantQuantity || '-' }}</span>
<el-button type="danger" size="mini" @click="handleDeleteReward(reward)">删除</el-button>
</div>
</div>
<div v-else style="color: #999; margin-bottom: 8px;">暂无已配置奖励</div>
</div>
<el-form label-width="100px">
<el-form-item label="奖励类型">
<el-select v-model="selectedRewardTypeId" placeholder="请选择奖励类型" filterable :loading="rewardTypeLoading" style="width: 100%" @change="handleRewardTypeChange">
<el-option v-for="item in rewardTypeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="奖励">
<el-select v-model="selectedRewardId" placeholder="请选择奖励" filterable :loading="rewardListLoading" style="width: 100%">
<el-option v-for="item in rewardList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="rewardTypeDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleRewardTypeConfirm">确定</el-button>
</div>
</el-dialog>
</el-main>
</el-container>
</template>
<script>
import { getGameList } from '@/api/game'
import { getTaskList, getMerchantAndStoreList, setTaskReward, operateTaskReward } from '@/api/task'
import { getRewardTypeList } from '@/api/reward-type'
import { getRewardSystemList } from '@/api/reward'
export default {
name: 'GameTask',
data() {
return {
activeGame: '',
gameList: [],
loading: false,
// 二级菜单下拉框相关数据
selectedCategories: [],
categoryOptions: [],
cascaderProps: {
expandTrigger: 'hover'
},
// 任务列表相关数据
taskList: [],
taskLoading: false,
rewardTypeDialogVisible: false,
rewardTypeList: [],
rewardTypeLoading: false,
selectedRewardTypeId: null,
rewardList: [],
rewardListLoading: false,
selectedRewardId: null,
currentTask: null,
}
},
created() {
this.initPage()
},
methods: {
async initPage() {
// 先获取商户和门店数据并设置默认
await this.getMerchantAndStoreData(false)
// 再获取游戏列表并设置默认
await this.getGameList(false)
// 下拉框和游戏列表都加载完后,拉取任务列表
await this.getTaskList()
},
// 获取商户和门店数据
async getMerchantAndStoreData(autoGetTask = true) {
try {
const res = await getMerchantAndStoreList()
if (res.code === 0) {
this.categoryOptions = res.data.list.map(merchant => ({
value: merchant.id,
label: merchant.merchantName,
children: merchant.StoreDatas.map(store => ({
value: store.id,
label: store.storeName,
netbarAccount: store.netbarAccount
}))
}))
// 默认选中第一个商户和第一个门店
if (this.categoryOptions.length > 0 && this.categoryOptions[0].children && this.categoryOptions[0].children.length > 0) {
this.selectedCategories = [this.categoryOptions[0].value, this.categoryOptions[0].children[0].value]
if (autoGetTask) this.handleCategoryChange(this.selectedCategories)
}
} else {
this.$message.error(res.message || '获取商户和门店列表失败')
}
} catch (e) {
this.$message.error('获取商户和门店列表失败')
}
},
async getGameList(autoGetTask = true) {
this.loading = true
try {
const res = await getGameList({
page: 1,
size: 1000,
pageidx: ''
})
if (res.code === 0) {
this.gameList = res.data.list
if (this.gameList.length > 0) {
this.activeGame = this.gameList[0].gameId.toString()
}
} else {
this.$message.error(res.message || '获取游戏列表失败')
}
} catch (e) {
this.$message.error('请求失败')
} finally {
this.loading = false
}
},
// 分类选择改变
async handleCategoryChange(value) {
console.log('handleCategoryChange', value)
this.loading = true
await this.getTaskList()
this.loading = false
},
// 下拉框点击事件
async handleCascaderClick() {
await this.getMerchantAndStoreData(false)
},
handleReset() {
this.selectedCategories = []
},
async handleRefresh() {
this.loading = true
await this.getTaskList()
this.loading = false
},
handleGameChange(tab) {
// 更新游戏ID
this.activeGame = tab.name
// 切换游戏后,重新拉取任务列表
this.loading = true
this.getTaskList().finally(() => {
this.loading = false
})
},
handleSizeChange(size) {
},
handleCurrentChange(page) {
},
async getTaskList() {
this.loading = true
// 获取netbarAccount和gid
let netbarAccount = ''
let gid = ''
console.log('selectedCategories', this.selectedCategories)
if (this.selectedCategories.length === 2) {
const merchant = this.categoryOptions.find(m => m.value === this.selectedCategories[0])
if (merchant && merchant.children) {
const store = merchant.children.find(s => s.value === this.selectedCategories[1])
if (store) netbarAccount = store.netbarAccount || ''
}
}
if (this.activeGame) {
gid = this.activeGame
}
console.log('getTaskList params', { netbarAccount, gid })
if (!netbarAccount || !gid) {
this.loading = false
this.taskList = []
return
}
const res = await getTaskList({ netbarAccount, gid, num: 10, pageidx: '' })
console.log('getTaskList result', res)
if (res.code === 0) {
this.taskList = res.data.taskList || []
} else {
this.$message.error(res.message || '获取任务列表失败')
this.taskList = []
}
this.loading = false
},
async configureReward(row) {
// 只保留 rewardTypeSource === 1 的奖励
this.currentTask = {
...row,
rewards: (row.rewards || []).filter(r => r.rewardTypeSource === 1)
}
this.selectedRewardTypeId = null
this.selectedRewardId = null
this.rewardTypeDialogVisible = true
this.rewardTypeLoading = true
this.rewardList = []
this.rewardListLoading = false
// 获取奖励类型
getRewardTypeList({ page: 1, size: 1000 }).then(res => {
if (res.code === 0) {
this.rewardTypeList = res.data.list || []
} else {
this.$message.error(res.message || '获取奖励类型失败')
}
}).catch(() => {
this.$message.error('获取奖励类型失败')
}).finally(() => {
this.rewardTypeLoading = false
})
},
handleRewardTypeChange(val) {
this.selectedRewardId = null
this.rewardList = []
if (!val) return
this.rewardListLoading = true
getRewardSystemList({ rewardTypeId: val, page: 1, size: 100 }).then(res => {
if (res.code === 0) {
this.rewardList = res.data.list || []
} else {
this.$message.error(res.message || '获取奖励列表失败')
}
}).catch(() => {
this.$message.error('获取奖励列表失败')
}).finally(() => {
this.rewardListLoading = false
})
},
handleRewardTypeConfirm() {
if (!this.selectedRewardTypeId || !this.selectedRewardId || !this.currentTask) {
this.$message.error('请选择奖励类型和奖励')
return
}
const taskId = this.currentTask.qqNetbarTaskId || this.currentTask.id
operateTaskReward({
type: 1,
taskId,
rewardId: this.selectedRewardId
}).then(res => {
if (res.code === 0) {
this.$message.success('添加奖励成功')
// 前端追加并过滤,只保留 rewardTypeSource === 1
if (!this.currentTask.rewards) this.currentTask.rewards = []
const added = this.rewardList.find(r => r.id === this.selectedRewardId)
if (added && added.rewardTypeSource === 1) this.currentTask.rewards.push(added)
this.currentTask.rewards = this.currentTask.rewards.filter(r => r.rewardTypeSource === 1)
this.selectedRewardTypeId = null
this.selectedRewardId = null
this.rewardTypeDialogVisible = false
this.getTaskList()
} else {
this.$message.error(res.message || '添加奖励失败')
}
}).catch(() => {
this.$message.error('请求失败')
})
},
handleDeleteReward(reward) {
if (!this.currentTask) return
const taskId = this.currentTask.qqNetbarTaskId || this.currentTask.id
operateTaskReward({
type: 2,
taskId,
rewardId: reward.id
}).then(res => {
if (res.code === 0) {
this.$message.success('删除奖励成功')
// 前端移除,只保留 rewardTypeSource === 1
this.currentTask.rewards = (this.currentTask.rewards || []).filter(r => r.id !== reward.id && r.rewardTypeSource === 1)
} else {
this.$message.error(res.message || '删除奖励失败')
}
}).catch(() => {
this.$message.error('请求失败')
})
},
}
}
</script>
<style scoped>
.game-task-page {
background: #fff;
padding: 20px 24px 12px 24px;
border-radius: 10px;
min-height: 500px;
}
.filter-container {
margin-bottom: 20px;
display: flex;
align-items: center;
}
.task-table-container {
margin-top: 20px;
overflow-x: hidden;
position: relative;
}
/* 禁止el-table横向滚动body和header都不允许 */
.task-table-container .el-table__body-wrapper,
.task-table-container .el-table__header-wrapper,
.task-table-container .el-table__footer-wrapper {
overflow-x: hidden !important;
}
.task-table-container .el-table__body,
.task-table-container .el-table__header,
.task-table-container .el-table__footer {
overflow-x: hidden !important;
max-width: 100% !important;
}
.refresh-btn {
background: #f7f8fa;
border: 1px solid #e3e6f0;
border-radius: 6px;
height: 36px;
padding: 0 16px;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.3s ease;
&:hover {
background: #556ff6;
color: #fff;
border-color: #556ff6;
i {
transform: rotate(180deg);
}
}
i {
font-size: 14px;
transition: transform 0.3s ease;
}
span {
font-size: 14px;
font-weight: 500;
}
}
.status-tag {
display: inline-block;
padding: 2px 16px;
border-radius: 16px;
font-size: 14px;
font-weight: 500;
background: #e8f7ea;
color: #3fc06d;
}
.status-block {
background: #fbeaea;
color: #f56c6c;
}
/* 强制彻底禁止el-table横向滚动 */
::v-deep .task-table-container .el-table__body-wrapper,
::v-deep .task-table-container .el-table__header-wrapper,
::v-deep .task-table-container .el-table__footer-wrapper,
::v-deep .task-table-container .el-table__body,
::v-deep .task-table-container .el-table__header,
::v-deep .task-table-container .el-table__footer {
overflow-x: hidden !important;
max-width: 100% !important;
}
</style>