feat: add rocket configuration page
This commit is contained in:
parent
e989b13e92
commit
3364e53445
19
src/api/rocket-config.js
Normal file
19
src/api/rocket-config.js
Normal file
@ -0,0 +1,19 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询全部火箭等级配置
|
||||
export function listRocketConfigs(params) {
|
||||
return request({
|
||||
url: '/admin/rocket/config/list',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 更新指定火箭等级的配置
|
||||
export function updateRocketConfig(data) {
|
||||
return request({
|
||||
url: '/admin/rocket/config/update',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@ -44,6 +44,7 @@ export default {
|
||||
'参数配置': 'Parameter Settings',
|
||||
'幸运礼物规则配置': 'Lucky Gift Rule Configuration',
|
||||
'幸运礼物规格配置': 'Lucky Gift Specification Configuration',
|
||||
'火箭配置': 'Rocket Configuration',
|
||||
'用户观看广告配置': 'User Ad View Configuration',
|
||||
'礼物管理': 'Gift Management',
|
||||
'IM账号管理': 'IM Account Management',
|
||||
|
||||
@ -8003,6 +8003,53 @@ export default {
|
||||
resetSuccess: 'Reset successful',
|
||||
yoloWalletConfigShared: 'The remaining YOLO settings share the USD wallet configuration.'
|
||||
}
|
||||
},
|
||||
rocketConfig: {
|
||||
alert: 'A rocket launches after reaching the configured level threshold. Rewards in the same pool currently have equal probability.',
|
||||
refresh: 'Refresh',
|
||||
level: 'Rocket Level',
|
||||
levelValue: 'Level {level} Rocket',
|
||||
maxEnergy: 'Energy Threshold',
|
||||
maxEnergyPlaceholder: 'Enter a positive integer',
|
||||
maxEnergyHelp: 'Thresholds should increase from level 1 through level 3.',
|
||||
rewardPool: 'Random Reward Pool',
|
||||
emptyReward: 'No rewards configured',
|
||||
status: 'Status',
|
||||
enabled: 'Enabled',
|
||||
disabled: 'Disabled',
|
||||
updatedAt: 'Updated At',
|
||||
actions: 'Actions',
|
||||
edit: 'Edit',
|
||||
editTitle: 'Edit {level}',
|
||||
rewardHelp: 'One reward is selected from the pool with equal probability.',
|
||||
addReward: 'Add Reward',
|
||||
rewardNumber: 'Reward {number}',
|
||||
removeReward: 'Remove',
|
||||
rewardType: 'Type',
|
||||
rewardName: 'Name',
|
||||
quantity: 'Quantity/Days',
|
||||
detailType: 'Prop Type',
|
||||
detailTypePlaceholder: 'Enter a prop type recognized by the server',
|
||||
cover: 'Cover URL',
|
||||
coverPlaceholder: 'Optional; shown in the claim result',
|
||||
goldAmount: 'Gold Amount',
|
||||
resourceId: 'Resource ID',
|
||||
cancel: 'Cancel',
|
||||
save: 'Save',
|
||||
positiveIntegerRequired: 'Enter a positive integer',
|
||||
maxEnergyRequired: 'Enter an energy threshold',
|
||||
statusRequired: 'Select a status',
|
||||
rewardTypeRequired: 'Select a reward type',
|
||||
rewardContentRequired: 'Enter a gold amount or resource ID',
|
||||
rewardNameRequired: 'Enter a reward name',
|
||||
detailTypeRequired: 'Enter a prop type',
|
||||
rewardRequired: 'Configure at least one reward',
|
||||
types: {
|
||||
gold: 'Gold',
|
||||
props: 'Prop',
|
||||
badge: 'Badge',
|
||||
propCoupon: 'Prop Coupon'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8000,6 +8000,53 @@ export default {
|
||||
resetSuccess: '重置成功',
|
||||
yoloWalletConfigShared: 'YOLO其余配置共用美金钱包的配置'
|
||||
}
|
||||
},
|
||||
rocketConfig: {
|
||||
alert: '火箭能量达到对应等级阈值后可发射;同一等级奖励池中的奖励当前为等概率随机。',
|
||||
refresh: '刷新',
|
||||
level: '火箭等级',
|
||||
levelValue: '{level}级火箭',
|
||||
maxEnergy: '能量阈值',
|
||||
maxEnergyPlaceholder: '请输入大于0的整数',
|
||||
maxEnergyHelp: '各等级阈值应按一级、二级、三级递增配置。',
|
||||
rewardPool: '随机奖励池',
|
||||
emptyReward: '暂未配置奖励',
|
||||
status: '状态',
|
||||
enabled: '启用',
|
||||
disabled: '禁用',
|
||||
updatedAt: '更新时间',
|
||||
actions: '操作',
|
||||
edit: '编辑',
|
||||
editTitle: '编辑{level}',
|
||||
rewardHelp: '领取时从奖励池中等概率随机一项。',
|
||||
addReward: '添加奖励',
|
||||
rewardNumber: '奖励 {number}',
|
||||
removeReward: '删除',
|
||||
rewardType: '类型',
|
||||
rewardName: '名称',
|
||||
quantity: '数量/天数',
|
||||
detailType: '道具类型',
|
||||
detailTypePlaceholder: '请输入服务端识别的道具类型',
|
||||
cover: '封面地址',
|
||||
coverPlaceholder: '选填,用于领取结果展示',
|
||||
goldAmount: '金币数量',
|
||||
resourceId: '资源ID',
|
||||
cancel: '取消',
|
||||
save: '保存',
|
||||
positiveIntegerRequired: '请输入大于0的整数',
|
||||
maxEnergyRequired: '请输入能量阈值',
|
||||
statusRequired: '请选择状态',
|
||||
rewardTypeRequired: '请选择奖励类型',
|
||||
rewardContentRequired: '请输入金币数量或资源ID',
|
||||
rewardNameRequired: '请输入奖励名称',
|
||||
detailTypeRequired: '请输入道具类型',
|
||||
rewardRequired: '至少需要配置一项奖励',
|
||||
types: {
|
||||
gold: '金币',
|
||||
props: '道具',
|
||||
badge: '徽章',
|
||||
propCoupon: '道具券'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -854,6 +854,12 @@ export const mockRouters = [
|
||||
component: () => import('@/views/game/lucky-gift/standard-config/index'),
|
||||
meta: { title: '幸运礼物规格配置', icon: 'form' }
|
||||
},
|
||||
{
|
||||
path: '/rocket/config',
|
||||
name: 'RocketConfig',
|
||||
component: () => import('@/views/rocket/config/index'),
|
||||
meta: { title: '火箭配置', icon: 'form' }
|
||||
},
|
||||
{
|
||||
path: '/game/lucky-gift/give-record',
|
||||
name: 'GameDoubleLayerFruit',
|
||||
|
||||
409
src/views/rocket/config/index.vue
Normal file
409
src/views/rocket/config/index.vue
Normal file
@ -0,0 +1,409 @@
|
||||
<template>
|
||||
<div class="app-container rocket-config-page">
|
||||
<el-alert
|
||||
:title="$t('pages.rocketConfig.alert')"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
/>
|
||||
|
||||
<div class="filter-container">
|
||||
<el-button
|
||||
class="filter-item"
|
||||
type="primary"
|
||||
icon="el-icon-refresh"
|
||||
:loading="listLoading"
|
||||
@click="loadConfigs"
|
||||
>
|
||||
{{ $t('pages.rocketConfig.refresh') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="listLoading"
|
||||
:data="configList"
|
||||
fit
|
||||
border
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column prop="levelName" :label="$t('pages.rocketConfig.level')" align="center" width="140">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.levelName || formatLevel(scope.row.level) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="maxEnergy" :label="$t('pages.rocketConfig.maxEnergy')" align="center" width="180" />
|
||||
<el-table-column :label="$t('pages.rocketConfig.rewardPool')" min-width="360">
|
||||
<template slot-scope="scope">
|
||||
<el-tag
|
||||
v-for="(reward, index) in scope.row.rewardConfig || []"
|
||||
:key="index"
|
||||
class="reward-tag"
|
||||
size="small"
|
||||
>
|
||||
{{ formatReward(reward) }}
|
||||
</el-tag>
|
||||
<span v-if="!scope.row.rewardConfig || scope.row.rewardConfig.length === 0" class="empty-text">
|
||||
{{ $t('pages.rocketConfig.emptyReward') }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('pages.rocketConfig.status')" align="center" width="110">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
|
||||
{{ scope.row.status === 1 ? $t('pages.rocketConfig.enabled') : $t('pages.rocketConfig.disabled') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('pages.rocketConfig.updatedAt')" align="center" width="180">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.updatedAt | dateFormat }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('pages.rocketConfig.actions')" align="center" width="100" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" icon="el-icon-edit" @click="openEdit(scope.row)">
|
||||
{{ $t('pages.rocketConfig.edit') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog
|
||||
:title="$t('pages.rocketConfig.editTitle', { level: formatLevel(form.level) })"
|
||||
:visible.sync="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="900px"
|
||||
@closed="resetForm"
|
||||
>
|
||||
<el-form ref="configForm" :model="form" :rules="rules" label-width="130px">
|
||||
<el-form-item :label="$t('pages.rocketConfig.level')">
|
||||
<el-input :value="formatLevel(form.level)" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('pages.rocketConfig.maxEnergy')" prop="maxEnergy">
|
||||
<el-input
|
||||
v-model.trim="form.maxEnergy"
|
||||
:placeholder="$t('pages.rocketConfig.maxEnergyPlaceholder')"
|
||||
/>
|
||||
<div class="form-help">{{ $t('pages.rocketConfig.maxEnergyHelp') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('pages.rocketConfig.status')" prop="status">
|
||||
<el-radio-group v-model="form.status">
|
||||
<el-radio :label="1">{{ $t('pages.rocketConfig.enabled') }}</el-radio>
|
||||
<el-radio :label="0">{{ $t('pages.rocketConfig.disabled') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('pages.rocketConfig.rewardPool')" required>
|
||||
<div class="reward-toolbar">
|
||||
<span class="form-help">{{ $t('pages.rocketConfig.rewardHelp') }}</span>
|
||||
<el-button type="primary" size="mini" icon="el-icon-plus" @click="addReward">
|
||||
{{ $t('pages.rocketConfig.addReward') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-for="(reward, index) in form.rewardConfig" :key="index" class="reward-editor">
|
||||
<div class="reward-editor__header">
|
||||
<strong>{{ $t('pages.rocketConfig.rewardNumber', { number: index + 1 }) }}</strong>
|
||||
<el-button type="text" class="danger-text" @click="removeReward(index)">
|
||||
{{ $t('pages.rocketConfig.removeReward') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="6">
|
||||
<el-form-item
|
||||
class="inner-form-item"
|
||||
:label="$t('pages.rocketConfig.rewardType')"
|
||||
:prop="'rewardConfig.' + index + '.type'"
|
||||
:rules="rewardRules.type"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-select v-model="reward.type" style="width: 100%" @change="handleRewardTypeChange(reward)">
|
||||
<el-option
|
||||
v-for="item in rewardTypes"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item
|
||||
class="inner-form-item"
|
||||
:label="contentLabel(reward.type)"
|
||||
:prop="'rewardConfig.' + index + '.content'"
|
||||
:rules="rewardRules.content"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-input v-model.trim="reward.content" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item
|
||||
class="inner-form-item"
|
||||
:label="$t('pages.rocketConfig.rewardName')"
|
||||
:prop="'rewardConfig.' + index + '.name'"
|
||||
:rules="rewardRules.name"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-input v-model.trim="reward.name" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="reward.type !== 'GOLD'" :span="6">
|
||||
<el-form-item
|
||||
class="inner-form-item"
|
||||
:label="$t('pages.rocketConfig.quantity')"
|
||||
:prop="'rewardConfig.' + index + '.quantity'"
|
||||
:rules="rewardRules.quantity"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-input v-model.trim="reward.quantity" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="12">
|
||||
<el-col v-if="reward.type === 'PROPS'" :span="12">
|
||||
<el-form-item
|
||||
class="inner-form-item"
|
||||
:label="$t('pages.rocketConfig.detailType')"
|
||||
:prop="'rewardConfig.' + index + '.detailType'"
|
||||
:rules="rewardRules.detailType"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-input v-model.trim="reward.detailType" :placeholder="$t('pages.rocketConfig.detailTypePlaceholder')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="reward.type === 'PROPS' ? 12 : 24">
|
||||
<el-form-item class="inner-form-item" :label="$t('pages.rocketConfig.cover')" label-width="80px">
|
||||
<el-input v-model.trim="reward.cover" :placeholder="$t('pages.rocketConfig.coverPlaceholder')" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-empty
|
||||
v-if="form.rewardConfig.length === 0"
|
||||
:description="$t('pages.rocketConfig.emptyReward')"
|
||||
:image-size="80"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">{{ $t('pages.rocketConfig.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="submitLoading" @click="submitForm">
|
||||
{{ $t('pages.rocketConfig.save') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listRocketConfigs, updateRocketConfig } from '@/api/rocket-config'
|
||||
|
||||
const createEmptyReward = () => ({
|
||||
type: 'GOLD',
|
||||
content: '',
|
||||
name: '',
|
||||
quantity: 1,
|
||||
detailType: '',
|
||||
cover: ''
|
||||
})
|
||||
|
||||
const createEmptyForm = () => ({
|
||||
id: null,
|
||||
level: null,
|
||||
maxEnergy: '',
|
||||
status: 1,
|
||||
rewardConfig: []
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'RocketConfig',
|
||||
data() {
|
||||
const validatePositiveInteger = (rule, value, callback) => {
|
||||
if (!/^\d+$/.test(String(value || '')) || Number(value) <= 0) {
|
||||
callback(new Error(this.$t('pages.rocketConfig.positiveIntegerRequired')))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
|
||||
return {
|
||||
listLoading: false,
|
||||
submitLoading: false,
|
||||
dialogVisible: false,
|
||||
configList: [],
|
||||
form: createEmptyForm(),
|
||||
rules: {
|
||||
maxEnergy: [
|
||||
{ required: true, message: this.$t('pages.rocketConfig.maxEnergyRequired'), trigger: 'blur' },
|
||||
{ validator: validatePositiveInteger, trigger: 'blur' }
|
||||
],
|
||||
status: [
|
||||
{ required: true, message: this.$t('pages.rocketConfig.statusRequired'), trigger: 'change' }
|
||||
]
|
||||
},
|
||||
rewardRules: {
|
||||
type: [{ required: true, message: this.$t('pages.rocketConfig.rewardTypeRequired'), trigger: 'change' }],
|
||||
content: [{ required: true, message: this.$t('pages.rocketConfig.rewardContentRequired'), trigger: 'blur' }],
|
||||
name: [{ required: true, message: this.$t('pages.rocketConfig.rewardNameRequired'), trigger: 'blur' }],
|
||||
quantity: [{ validator: validatePositiveInteger, trigger: 'blur' }],
|
||||
detailType: [{ required: true, message: this.$t('pages.rocketConfig.detailTypeRequired'), trigger: 'blur' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rewardTypes() {
|
||||
return [
|
||||
{ value: 'GOLD', label: this.$t('pages.rocketConfig.types.gold') },
|
||||
{ value: 'PROPS', label: this.$t('pages.rocketConfig.types.props') },
|
||||
{ value: 'BADGE', label: this.$t('pages.rocketConfig.types.badge') },
|
||||
{ value: 'PROP_COUPON', label: this.$t('pages.rocketConfig.types.propCoupon') }
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadConfigs()
|
||||
},
|
||||
methods: {
|
||||
loadConfigs() {
|
||||
this.listLoading = true
|
||||
listRocketConfigs().then(response => {
|
||||
this.configList = response.body || []
|
||||
}).finally(() => {
|
||||
this.listLoading = false
|
||||
})
|
||||
},
|
||||
formatLevel(level) {
|
||||
return this.$t('pages.rocketConfig.levelValue', { level: level || '-' })
|
||||
},
|
||||
formatReward(reward) {
|
||||
const type = this.rewardTypes.find(item => item.value === reward.type)
|
||||
const typeName = type ? type.label : reward.type
|
||||
const amount = reward.type === 'GOLD' ? reward.content : reward.quantity
|
||||
return `${reward.name || typeName} (${amount || '-'})`
|
||||
},
|
||||
contentLabel(type) {
|
||||
return type === 'GOLD'
|
||||
? this.$t('pages.rocketConfig.goldAmount')
|
||||
: this.$t('pages.rocketConfig.resourceId')
|
||||
},
|
||||
openEdit(row) {
|
||||
// 深拷贝奖励池,避免弹窗中的编辑直接污染列表数据。
|
||||
const rewardConfig = (row.rewardConfig || []).map(item => Object.assign(createEmptyReward(), JSON.parse(JSON.stringify(item))))
|
||||
this.form = {
|
||||
id: row.id,
|
||||
level: row.level,
|
||||
maxEnergy: String(row.maxEnergy || ''),
|
||||
status: row.status,
|
||||
rewardConfig
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => this.$refs.configForm && this.$refs.configForm.clearValidate())
|
||||
},
|
||||
resetForm() {
|
||||
this.form = createEmptyForm()
|
||||
if (this.$refs.configForm) {
|
||||
this.$refs.configForm.clearValidate()
|
||||
}
|
||||
},
|
||||
addReward() {
|
||||
this.form.rewardConfig.push(createEmptyReward())
|
||||
},
|
||||
removeReward(index) {
|
||||
this.form.rewardConfig.splice(index, 1)
|
||||
},
|
||||
handleRewardTypeChange(reward) {
|
||||
if (reward.type === 'GOLD') {
|
||||
reward.quantity = 1
|
||||
reward.detailType = ''
|
||||
}
|
||||
},
|
||||
submitForm() {
|
||||
if (this.form.rewardConfig.length === 0) {
|
||||
this.$message.warning(this.$t('pages.rocketConfig.rewardRequired'))
|
||||
return
|
||||
}
|
||||
|
||||
this.$refs.configForm.validate(valid => {
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
|
||||
// 仅提交服务端当前真正生效的配置,未使用字段继续保留数据库原值。
|
||||
const payload = {
|
||||
id: this.form.id,
|
||||
maxEnergy: this.form.maxEnergy,
|
||||
status: this.form.status,
|
||||
rewardConfig: this.form.rewardConfig.map(item => {
|
||||
const reward = Object.assign({}, item)
|
||||
reward.content = String(reward.content)
|
||||
if (reward.type !== 'GOLD') {
|
||||
reward.quantity = Number(reward.quantity)
|
||||
}
|
||||
return reward
|
||||
})
|
||||
}
|
||||
|
||||
this.submitLoading = true
|
||||
updateRocketConfig(payload).then(() => {
|
||||
this.$opsMessage.success()
|
||||
this.dialogVisible = false
|
||||
this.loadConfigs()
|
||||
}).finally(() => {
|
||||
this.submitLoading = false
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.rocket-config-page {
|
||||
.filter-container {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.reward-tag {
|
||||
margin: 3px 6px 3px 0;
|
||||
}
|
||||
|
||||
.empty-text,
|
||||
.form-help {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.reward-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.reward-editor {
|
||||
margin-bottom: 14px;
|
||||
padding: 14px 14px 0;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.reward-editor__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.inner-form-item {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.danger-text {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Loading…
x
Reference in New Issue
Block a user