Files
dafuweng-saiadmin6.x/saiadmin-artd/src/views/plugin/dice/reward/index/modules/weight-test-dialog.vue
2026-03-13 15:53:19 +08:00

382 lines
13 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>
<ElDialog
v-model="visible"
title="一键测试权重"
width="560px"
:close-on-click-modal="false"
destroy-on-close
@close="onClose"
>
<ElForm ref="formRef" :model="form" label-width="140px">
<ElSteps :active="currentStep" finish-status="success" simple class="steps-wrap">
<ElStep title="付费抽奖券" />
<ElStep title="免费抽奖券" />
</ElSteps>
<!-- 第一页付费抽奖券 -->
<div v-show="currentStep === 0" class="step-panel">
<ElFormItem label="测试数据档位类型" prop="paid_lottery_config_id">
<ElSelect
v-model="form.paid_lottery_config_id"
placeholder="不选则下方自定义档位概率(默认 type=0"
clearable
filterable
style="width: 100%"
>
<ElOption
v-for="item in paidLotteryOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<template v-if="form.paid_lottery_config_id == null">
<div class="tier-label">自定义档位概率T1T5每档 0-100%五档之和不能超过 100%</div>
<ElRow :gutter="12" class="tier-row">
<ElCol v-for="t in tierKeys" :key="'paid-' + t" :span="8">
<div class="tier-field">
<label class="tier-field-label">档位 {{ t }}%</label>
<input
type="number"
:value="getPaidTier(t)"
min="0"
max="100"
placeholder="0"
class="tier-input"
@input="setPaidTier(t, $event)"
/>
</div>
</ElCol>
</ElRow>
<div v-if="paidTierSum > 100" class="tier-error"
>当前五档之和为 {{ paidTierSum }}%不能超过 100%</div
>
</template>
<ElFormItem label="顺时针次数" prop="paid_s_count" required>
<ElSelect v-model="form.paid_s_count" placeholder="请选择" style="width: 100%">
<ElOption v-for="c in countOptions" :key="c" :label="String(c)" :value="c" />
</ElSelect>
</ElFormItem>
<ElFormItem label="逆时针次数" prop="paid_n_count" required>
<ElSelect v-model="form.paid_n_count" placeholder="请选择" style="width: 100%">
<ElOption v-for="c in countOptions" :key="c" :label="String(c)" :value="c" />
</ElSelect>
</ElFormItem>
</div>
<!-- 第二页免费抽奖券 -->
<div v-show="currentStep === 1" class="step-panel">
<ElFormItem label="测试数据档位类型" prop="free_lottery_config_id">
<ElSelect
v-model="form.free_lottery_config_id"
placeholder="不选则下方自定义档位概率(默认 type=1"
clearable
filterable
style="width: 100%"
>
<ElOption
v-for="item in freeLotteryOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</ElSelect>
</ElFormItem>
<template v-if="form.free_lottery_config_id == null">
<div class="tier-label">自定义档位概率T1T5每档 0-100%五档之和不能超过 100%</div>
<ElRow :gutter="12" class="tier-row">
<ElCol v-for="t in tierKeys" :key="'free-' + t" :span="8">
<div class="tier-field">
<label class="tier-field-label">档位 {{ t }}%</label>
<input
type="number"
:value="getFreeTier(t)"
min="0"
max="100"
placeholder="0"
class="tier-input"
@input="setFreeTier(t, $event)"
/>
</div>
</ElCol>
</ElRow>
<div v-if="freeTierSum > 100" class="tier-error"
>当前五档之和为 {{ freeTierSum }}%不能超过 100%</div
>
</template>
<ElFormItem label="顺时针次数" prop="free_s_count" required>
<ElSelect v-model="form.free_s_count" placeholder="请选择" style="width: 100%">
<ElOption v-for="c in countOptions" :key="c" :label="String(c)" :value="c" />
</ElSelect>
</ElFormItem>
<ElFormItem label="逆时针次数" prop="free_n_count" required>
<ElSelect v-model="form.free_n_count" placeholder="请选择" style="width: 100%">
<ElOption v-for="c in countOptions" :key="c" :label="String(c)" :value="c" />
</ElSelect>
</ElFormItem>
</div>
</ElForm>
<template #footer>
<ElButton v-if="currentStep > 0" :disabled="running" @click="currentStep--">上一步</ElButton>
<ElButton v-if="currentStep < 1" type="primary" :disabled="running" @click="currentStep++"
>下一步</ElButton
>
<ElButton v-if="currentStep === 1" type="primary" :loading="running" @click="handleStart"
>开始测试</ElButton
>
<ElButton :disabled="running" @click="visible = false">取消</ElButton>
</template>
</ElDialog>
</template>
<script setup lang="ts">
import api from '../../../api/reward/index'
import lotteryPoolApi from '../../../api/lottery_pool_config/index'
const countOptions = [0, 100, 500, 1000, 5000]
const tierKeys = ['T1', 'T2', 'T3', 'T4', 'T5'] as const
const visible = defineModel<boolean>({ default: false })
const emit = defineEmits<{ (e: 'success'): void }>()
const formRef = ref()
const currentStep = ref(0)
const form = reactive({
paid_lottery_config_id: undefined as number | undefined,
free_lottery_config_id: undefined as number | undefined,
paid_tier_weights: { T1: 20, T2: 20, T3: 20, T4: 20, T5: 20 } as Record<string, number>,
free_tier_weights: { T1: 20, T2: 20, T3: 20, T4: 20, T5: 20 } as Record<string, number>,
paid_s_count: 100,
paid_n_count: 100,
free_s_count: 100,
free_n_count: 100
})
const lotteryOptions = ref<Array<{ id: number; name: string; type: number }>>([])
/** 将 type 转为数字(接口可能返回字符串 "0"/"1" */
function tierTypeNum(r: { type?: number | string }): number {
const t = r.type ?? 0
return typeof t === 'number' ? t : Number(t) || 0
}
/** 付费抽奖券可选档位type=0 */
const paidLotteryOptions = computed(() =>
lotteryOptions.value.filter((r) => tierTypeNum(r) === 0)
)
/**
* 免费抽奖券可选档位:优先 type=1DiceLotteryPoolConfig.type=1若无则显示全部以便下拉有选项
*/
const freeLotteryOptions = computed(() => {
const type1List = lotteryOptions.value.filter((r) => tierTypeNum(r) === 1)
return type1List.length > 0 ? type1List : lotteryOptions.value
})
const running = ref(false)
function onClose() {
running.value = false
currentStep.value = 0
}
function getPaidTier(t: string): string {
const v = form.paid_tier_weights[t]
return v !== undefined && v !== null ? String(v) : ''
}
function setPaidTier(t: string, val: string | Event) {
const raw =
typeof val === 'string'
? val
: ((val as Event & { target: HTMLInputElement }).target?.value ?? '')
const num = raw === '' ? 0 : Math.max(0, Math.min(100, Number(raw) || 0))
form.paid_tier_weights[t] = num
}
const paidTierSum = computed(() =>
tierKeys.reduce((s, t) => s + (form.paid_tier_weights[t] ?? 0), 0)
)
function getFreeTier(t: string): string {
const v = form.free_tier_weights[t]
return v !== undefined && v !== null ? String(v) : ''
}
function setFreeTier(t: string, val: string | Event) {
const raw =
typeof val === 'string'
? val
: ((val as Event & { target: HTMLInputElement }).target?.value ?? '')
const num = raw === '' ? 0 : Math.max(0, Math.min(100, Number(raw) || 0))
form.free_tier_weights[t] = num
}
const freeTierSum = computed(() =>
tierKeys.reduce((s, t) => s + (form.free_tier_weights[t] ?? 0), 0)
)
async function loadLotteryOptions() {
try {
const list = await lotteryPoolApi.getOptions()
lotteryOptions.value = list.map(
(r: { id: number; name: string; type?: number | string }) => ({
id: r.id,
name: r.name,
type: tierTypeNum(r)
})
)
// 付费抽奖券默认使用 type=0 的档位类型
const type0 = list.find((r: { type?: number | string }) => tierTypeNum(r) === 0)
if (type0) {
form.paid_lottery_config_id = type0.id
}
// 免费抽奖券默认使用 type=1 的档位类型DiceLotteryPoolConfig.type=1若无 type=1 则默认选第一项
const type1 = list.find((r: { type?: number | string }) => tierTypeNum(r) === 1)
if (type1) {
form.free_lottery_config_id = type1.id
} else if (list.length > 0) {
form.free_lottery_config_id = list[0].id
}
} catch (_) {
lotteryOptions.value = []
}
}
function buildPayload() {
const payload: Record<string, unknown> = {
paid_s_count: form.paid_s_count,
paid_n_count: form.paid_n_count,
free_s_count: form.free_s_count,
free_n_count: form.free_n_count
}
if (form.paid_lottery_config_id != null) {
payload.paid_lottery_config_id = form.paid_lottery_config_id
} else {
payload.paid_tier_weights = { ...form.paid_tier_weights }
}
if (form.free_lottery_config_id != null) {
payload.free_lottery_config_id = form.free_lottery_config_id
} else {
payload.free_tier_weights = { ...form.free_tier_weights }
}
return payload
}
function validateForm(): boolean {
if (form.paid_s_count + form.paid_n_count + form.free_s_count + form.free_n_count <= 0) {
ElMessage.warning('付费或免费至少一种方向次数之和大于 0')
return false
}
const needPaidTier = form.paid_lottery_config_id == null
const needFreeTier = form.free_lottery_config_id == null
if (needPaidTier) {
const sum = paidTierSum.value
if (sum <= 0) {
ElMessage.warning('付费未选奖池时T1T5 档位概率之和需大于 0')
return false
}
if (sum > 100) {
ElMessage.warning('付费档位概率 T1T5 之和不能超过 100%')
return false
}
}
if (needFreeTier) {
const sum = freeTierSum.value
if (sum <= 0) {
ElMessage.warning('免费未选奖池时T1T5 档位概率之和需大于 0')
return false
}
if (sum > 100) {
ElMessage.warning('免费档位概率 T1T5 之和不能超过 100%')
return false
}
}
return true
}
async function handleStart() {
if (!validateForm()) return
running.value = true
try {
await api.startWeightTest(buildPayload())
ElMessage.success(
'测试任务已创建,后台将自动执行。请在【玩家抽奖记录(测试数据)】中查看生成的测试数据'
)
visible.value = false
emit('success')
} catch (e: any) {
ElMessage.error(e?.message || '创建测试任务失败')
} finally {
running.value = false
}
}
watch(visible, (v) => {
if (v) {
loadLotteryOptions()
} else {
onClose()
}
})
// 切换到免费步骤时,若当前选中 id 不在免费档位列表中,则重置为第一个 type=1 的选项,避免显示错误
watch(currentStep, (step) => {
if (step === 1) {
const freeOpts = freeLotteryOptions.value
const id = form.free_lottery_config_id
if (freeOpts.length && (id == null || !freeOpts.some((o) => o.id === id))) {
form.free_lottery_config_id = freeOpts[0].id
}
}
})
</script>
<style lang="scss" scoped>
.steps-wrap {
margin-bottom: 16px;
}
.step-panel {
min-height: 200px;
}
.tier-label {
font-size: 13px;
color: var(--el-text-color-secondary);
margin-bottom: 8px;
}
.tier-row {
margin-bottom: 12px;
}
.tier-field {
margin-bottom: 12px;
}
.tier-field-label {
display: block;
font-size: 14px;
color: var(--el-text-color-regular);
margin-bottom: 4px;
line-height: 1.5;
}
.tier-input {
display: block;
width: 100%;
padding: 8px 12px;
font-size: 14px;
line-height: 1.5;
color: var(--el-text-color-regular);
background-color: var(--el-fill-color-blank);
border: 1px solid var(--el-border-color);
border-radius: var(--el-border-radius-base);
box-sizing: border-box;
}
.tier-input:hover {
border-color: var(--el-border-color-hover);
}
.tier-input:focus {
outline: none;
border-color: var(--el-color-primary);
box-shadow: 0 0 0 2px var(--el-color-primary-light-7);
}
.tier-input::placeholder {
color: var(--el-text-color-placeholder);
}
.tier-error {
font-size: 12px;
color: var(--el-color-danger);
margin-top: 4px;
margin-bottom: 8px;
}
</style>