406 lines
13 KiB
Vue
406 lines
13 KiB
Vue
<template>
|
||
<ElDialog
|
||
v-model="visible"
|
||
:title="$t('page.weightTest.title')"
|
||
width="560px"
|
||
:close-on-click-modal="false"
|
||
destroy-on-close
|
||
@close="onClose"
|
||
>
|
||
<ElAlert type="info" :closable="false" show-icon class="weight-test-tip">
|
||
<template #title>{{ $t('page.weightTest.alertTitle') }}</template>
|
||
{{ $t('page.weightTest.alertBody') }}
|
||
</ElAlert>
|
||
<ElAlert type="warning" :closable="false" show-icon class="weight-test-tip chain-tip">
|
||
{{ $t('page.weightTest.chainModeHint') }}
|
||
</ElAlert>
|
||
<ElAlert type="info" :closable="false" show-icon class="weight-test-tip chain-tip">
|
||
{{ $t('page.weightTest.killModeHint') }}
|
||
</ElAlert>
|
||
<ElForm :model="form" label-width="140px">
|
||
<ElFormItem :label="$t('page.weightTest.labelAnte')" prop="ante" required>
|
||
<ElInputNumber v-model="form.ante" :min="1" :step="1" style="width: 100%" />
|
||
</ElFormItem>
|
||
<ElFormItem :label="$t('page.weightTest.labelKillModeEnabled')" prop="kill_mode_enabled">
|
||
<ElSwitch v-model="form.kill_mode_enabled" />
|
||
</ElFormItem>
|
||
<ElFormItem :label="$t('page.weightTest.labelTestSafetyLine')" prop="test_safety_line">
|
||
<ElInputNumber
|
||
v-model="form.test_safety_line"
|
||
:min="0"
|
||
:step="100"
|
||
:disabled="!form.kill_mode_enabled"
|
||
style="width: 100%"
|
||
/>
|
||
</ElFormItem>
|
||
|
||
<div class="section-title">{{ $t('page.weightTest.sectionPaid') }}</div>
|
||
<ElFormItem
|
||
:label="$t('page.weightTest.labelLotteryTypePaid')"
|
||
prop="paid_lottery_config_id"
|
||
>
|
||
<ElSelect
|
||
v-model="form.paid_lottery_config_id"
|
||
:placeholder="$t('page.weightTest.placeholderPaidPool')"
|
||
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">{{ $t('page.weightTest.tierProbHint') }}</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('page.weightTest.tierFieldLabel', { tier: 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">{{
|
||
$t('page.weightTest.tierSumError', { sum: paidTierSum })
|
||
}}</div>
|
||
</template>
|
||
<ElFormItem :label="$t('page.weightTest.labelCwCount')" prop="paid_s_count" required>
|
||
<ElSelect
|
||
v-model="form.paid_s_count"
|
||
:placeholder="$t('page.weightTest.placeholderSelect')"
|
||
style="width: 100%"
|
||
>
|
||
<ElOption v-for="c in countOptions" :key="c" :label="String(c)" :value="c" />
|
||
</ElSelect>
|
||
</ElFormItem>
|
||
<ElFormItem :label="$t('page.weightTest.labelCcwCount')" prop="paid_n_count" required>
|
||
<ElSelect
|
||
v-model="form.paid_n_count"
|
||
:placeholder="$t('page.weightTest.placeholderSelect')"
|
||
style="width: 100%"
|
||
>
|
||
<ElOption v-for="c in countOptions" :key="c" :label="String(c)" :value="c" />
|
||
</ElSelect>
|
||
</ElFormItem>
|
||
|
||
<div class="section-title">{{ $t('page.weightTest.sectionFreeAfterPlayAgain') }}</div>
|
||
<ElFormItem
|
||
:label="$t('page.weightTest.labelLotteryTypeFree')"
|
||
prop="free_lottery_config_id"
|
||
>
|
||
<ElSelect
|
||
v-model="form.free_lottery_config_id"
|
||
:placeholder="$t('page.weightTest.placeholderFreePool')"
|
||
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">{{ $t('page.weightTest.tierProbHintFreeChain') }}</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('page.weightTest.tierFieldLabel', { tier: 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">{{
|
||
$t('page.weightTest.tierSumError', { sum: freeTierSum })
|
||
}}</div>
|
||
</template>
|
||
</ElForm>
|
||
|
||
<template #footer>
|
||
<ElButton
|
||
v-permission="'dice:reward:index:startWeightTest'"
|
||
type="primary"
|
||
:loading="running"
|
||
@click="handleStart"
|
||
>{{ $t('page.weightTest.btnStart') }}</ElButton
|
||
>
|
||
<ElButton :disabled="running" @click="visible = false">{{
|
||
$t('page.weightTest.btnCancel')
|
||
}}</ElButton>
|
||
</template>
|
||
</ElDialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import api from '../../../api/reward/index'
|
||
import lotteryPoolApi from '../../../api/lottery_pool_config/index'
|
||
import { ElMessage } from 'element-plus'
|
||
import { useI18n } from 'vue-i18n'
|
||
|
||
const { t } = useI18n()
|
||
|
||
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 form = reactive({
|
||
ante: 1,
|
||
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,
|
||
kill_mode_enabled: false,
|
||
test_safety_line: 5000
|
||
})
|
||
const lotteryOptions = ref<Array<{ id: number; name: string }>>([])
|
||
/** 付费抽奖券可选档位:name=default */
|
||
const paidLotteryOptions = computed(() =>
|
||
lotteryOptions.value.filter((r) => r.name === 'default')
|
||
)
|
||
/** 免费抽奖券可选档位:优先 name=killScore,若无则显示全部以便下拉有选项 */
|
||
const freeLotteryOptions = computed(() => {
|
||
const list = lotteryOptions.value.filter((r) => r.name === 'killScore')
|
||
return list.length > 0 ? list : lotteryOptions.value
|
||
})
|
||
const running = ref(false)
|
||
|
||
function onClose() {
|
||
running.value = false
|
||
}
|
||
|
||
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 }) => ({
|
||
id: r.id,
|
||
name: r.name
|
||
}))
|
||
const normal = list.find((r: { name?: string }) => r.name === 'default')
|
||
if (normal) {
|
||
form.paid_lottery_config_id = normal.id
|
||
}
|
||
const kill = list.find((r: { name?: string }) => r.name === 'killScore')
|
||
if (kill) {
|
||
form.free_lottery_config_id = kill.id
|
||
} else if (list.length > 0) {
|
||
form.free_lottery_config_id = list[0].id
|
||
}
|
||
} catch {
|
||
lotteryOptions.value = []
|
||
}
|
||
}
|
||
|
||
function buildPayload() {
|
||
const payload: Record<string, unknown> = {
|
||
ante: form.ante,
|
||
paid_s_count: form.paid_s_count,
|
||
paid_n_count: form.paid_n_count,
|
||
free_s_count: 0,
|
||
free_n_count: 0,
|
||
chain_free_mode: true,
|
||
kill_mode_enabled: form.kill_mode_enabled,
|
||
test_safety_line: form.test_safety_line
|
||
}
|
||
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.ante == null || form.ante <= 0) {
|
||
ElMessage.warning(t('page.weightTest.warnAnte'))
|
||
return false
|
||
}
|
||
if (form.paid_s_count + form.paid_n_count <= 0) {
|
||
ElMessage.warning(t('page.weightTest.warnPaidSpins'))
|
||
return false
|
||
}
|
||
if (form.kill_mode_enabled && (form.test_safety_line == null || form.test_safety_line < 0)) {
|
||
ElMessage.warning(t('page.weightTest.warnTestSafetyLine'))
|
||
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(t('page.weightTest.warnPaidTierSumPositive'))
|
||
return false
|
||
}
|
||
if (sum > 100) {
|
||
ElMessage.warning(t('page.weightTest.warnPaidTierSumMax'))
|
||
return false
|
||
}
|
||
}
|
||
if (needFreeTier) {
|
||
const sum = freeTierSum.value
|
||
if (sum <= 0) {
|
||
ElMessage.warning(t('page.weightTest.warnFreeTierSumPositive'))
|
||
return false
|
||
}
|
||
if (sum > 100) {
|
||
ElMessage.warning(t('page.weightTest.warnFreeTierSumMax'))
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
async function handleStart() {
|
||
if (!validateForm()) return
|
||
running.value = true
|
||
try {
|
||
await api.startWeightTest(buildPayload())
|
||
ElMessage.success(t('page.weightTest.successCreated'))
|
||
visible.value = false
|
||
emit('success')
|
||
} catch (e: any) {
|
||
ElMessage.error(e?.message || t('page.weightTest.failCreate'))
|
||
} finally {
|
||
running.value = false
|
||
}
|
||
}
|
||
|
||
watch(visible, (v) => {
|
||
if (v) {
|
||
loadLotteryOptions()
|
||
} else {
|
||
onClose()
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.weight-test-tip {
|
||
margin-bottom: 16px;
|
||
}
|
||
.chain-tip {
|
||
margin-top: -8px;
|
||
}
|
||
.section-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--el-text-color-primary);
|
||
margin: 8px 0 12px;
|
||
padding-bottom: 6px;
|
||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||
}
|
||
.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>
|