优化奖励配置/按规则一键生成表

This commit is contained in:
2026-03-27 10:26:58 +08:00
parent e32f3890f1
commit cfc6537f97
5 changed files with 82 additions and 30 deletions

View File

@@ -14,6 +14,7 @@
"colDisplayText": "Display Text",
"colDisplayTextEn": "Display Text (EN)",
"colRealEv": "Real Settlement",
"colRealReward": "Player Real Reward",
"colTier": "Tier",
"colRemark": "Remark",
"placeholderTierSelect": "Tier",
@@ -53,7 +54,7 @@
"infoNoBigwin": "No BIGWIN rows. Set tier to BIGWIN in the Reward Index tab first.",
"btnRuleGenerate": "Generate by rules",
"ruleGenerateTitle": "Generate reward index by rules",
"ruleGenerateRules": "[Generation logic (same as Create Reward Reference)]\n• 26 cells ordered by id ascending are positions 025; each rows grid_number is 530 and unique.\n• Roll D (530): start at the cell whose grid_number equals D (start_index); clockwise landing = (start position + D) mod 26; counter-clockwise = start D (if negative, +26).\n• Each reference rows “dice points” column is the roll D; tier / real_ev / display text come from the config at the landing id.\n\n[Leopard rolls]\nFor rolls 5, 10, 15, 20, 25, 30, clockwise and counter-clockwise landing tiers must NOT be T4 or T5 (avoid leopard roll + penalty / try again).\n\n[real_ev vs tier]\nreal < 100 → T4; 100 < real < 0 → T3; 0 < real < 100 → T2; 100 < real < 500 → T1; T5 “try again” real_ev=0. Set per-tier real_ev standards below; values are written into the config and can be edited later.\n\n[Inputs]\nT1/T4/T5: fixed weighted counts. T2: minimum weighted countclockwise and counter-clockwise must satisfy the corresponding constraints (one value per tier, applied to both directions). real_ev standards: one value per tier. Generated T1T4 use ui_text / ui_text_en = 100 + real_ev (same EN); T5 uses “try again” / Once again. Remarks still distinguish break-even vs small win where applicable.",
"ruleGenerateRules": "[Generation logic (same as Create Reward Reference)]\n• 26 cells ordered by id ascending are positions 025; each rows grid_number is 530 and unique.\n• Roll D (530): start at the cell whose grid_number equals D (start_index); clockwise landing = (start position + D) mod 26; counter-clockwise = start D (if negative, +26).\n• Each reference rows “dice points” column is the roll D; tier / real_ev / display text come from the config at the landing id.\n\n[Leopard rolls]\nFor rolls 5, 10, 15, 20, 25, 30, clockwise and counter-clockwise landing tiers must NOT be T4 or T5 (avoid leopard roll + penalty / once again).\n\n[Settlement amount vs tier]\nSettlement < 0 → T4; 0 < Settlement < 100 → T3; 100 < Settlement < 200 → T2; Settlement > 200 → T1; T5 “once again” settlement = 0. You can set a unified settlement standard for each tier below; generated rows write those values into the config, and details can be edited later in the table.\n\n[Inputs in this dialog]\nCount: T1/T4/T5 are fixed; T2 is minimum. Clockwise and counter-clockwise weighted counts (each roll result counts once) must each satisfy the entered values; T1, T4, and T5 are entered separately.\nSettlement standard: all cells in the same tier use the same value. On generation, T1T4 use ui_text / ui_text_en = settlement real_ev; T5 is fixed to \"再来一次\" / \"Once again\". Remarks still distinguish break-even / small win, etc.",
"ruleGenT1Row": "T1 (big prize)",
"ruleGenT2Row": "T2 (small win / break-even)",
"ruleGenT3RealEvOnly": "T3 (rake)",
@@ -63,10 +64,10 @@
"ruleGenFixedCount": "Fixed count (CW & CCW)",
"ruleGenRealEvStd": "real_ev standard",
"ruleGenRealEvEditHint": "After saving, you can still edit display text, EN, real_ev and remarks per row in the table above.",
"ruleGenInvalidT1RealEv": "T1 real_ev must satisfy 100 < value < 500",
"ruleGenInvalidT2RealEv": "T2 real_ev must satisfy 0 < value < 100",
"ruleGenInvalidT3RealEv": "T3 real_ev must satisfy -100 < value < 0",
"ruleGenInvalidT4RealEv": "T4 real_ev must satisfy value < -100",
"ruleGenInvalidT1RealEv": "T1 settlement amount must satisfy: value > 200",
"ruleGenInvalidT2RealEv": "T2 settlement amount must satisfy: 100 < value < 200",
"ruleGenInvalidT3RealEv": "T3 settlement amount must satisfy: 0 < value < 100",
"ruleGenInvalidT4RealEv": "T4 settlement amount must satisfy: value < 0",
"ruleGenInvalidT5RealEv": "T5 “try again” real_ev must be 0",
"ruleGenT1Min": "T1 fixed count (CW & CCW)",
"ruleGenT2Min": "T2 min (CW & CCW)",

View File

@@ -13,7 +13,8 @@
"colDicePoints": "色子点数",
"colDisplayText": "显示文本",
"colDisplayTextEn": "显示文本(英文)",
"colRealEv": "真实结算",
"colRealEv": "结算金额",
"colRealReward": "玩家实际中奖",
"colTier": "所属档位",
"colRemark": "备注",
"placeholderTierSelect": "档位",
@@ -53,7 +54,7 @@
"infoNoBigwin": "暂无 BIGWIN 档位配置,请先在「奖励索引」中设置 tier 为 BIGWIN",
"btnRuleGenerate": "按规则生成",
"ruleGenerateTitle": "按规则生成奖励索引",
"ruleGenerateRules": "【生成逻辑(与创建奖励对照一致)】\n• 盘面 26 格按 id 升序为位置 025每条配置的 grid_number 为 530 且不重复。\n• 摇取点数 D530起点为「grid_number=D」所在格位的 id即 start_index顺时针落点位置 = (起点位置 + D) mod 26逆时针落点 = 起点位置 D若小于 0 则 +26。\n• 对照表每条记录的「色子点数」列为摇取点数 D档位、真实结算、显示文案取自落点格位对应 id 的配置。\n\n【豹子摇取点数】\n摇取点数为 5、10、15、20、25、30 时,其顺/逆时针落点档位不能为 T4、T5避免对照表上出现豹子点数 + 惩罚/再来一次)。\n\n【real_ev 与 tier】\nreal < 100 → T4100 < real < 0 → T30 < real < 100 → T2100 < real < 500 → T1T5「再来一次」real_ev=0。下方可为各档位填写统一的 real_ev 标准,生成时写入配置;细则可稍后在表格中再改。\n\n【本弹窗输入】\n条数T1/T4/T5「固定」T2「不少于」——顺时针与逆时针的加权条数每条摇取结果计一次须分别满足所填数值T1、T4 与 T5 分开填写。\nreal_ev 标准:同档位各格使用同一数值。生成时 T1T4 的 ui_text / ui_text_en 均为「100+真实结算」T5 固定为「再来一次」/「Once again」。备注仍区分完美回本/小赚等。",
"ruleGenerateRules": "【生成逻辑(与创建奖励对照一致)】\n• 盘面 26 格按 id 升序为位置 025每条配置的 grid_number 为 530 且不重复。\n• 摇取点数 D530起点为「grid_number=D」所在格位的 id即 start_index顺时针落点位置 = (起点位置 + D) mod 26逆时针落点 = 起点位置 D若小于 0 则 +26。\n• 对照表每条记录的「色子点数」列为摇取点数 D档位、真实结算、显示文案取自落点格位对应 id 的配置。\n\n【豹子摇取点数】\n摇取点数为 5、10、15、20、25、30 时,其顺/逆时针落点档位不能为 T4、T5避免对照表上出现豹子点数 + 惩罚/再来一次)。\n\n【结算金额 与 档位】\n结算金额 < 0 → T40 < 结算金额 < 100 → T3100 < 结算金额 < 200 → T2200 < 结算金额 → T1T5「再来一次」结算金额=0。下方可为各档位填写统一的 结算金额 标准,生成时写入配置;细则可稍后在表格中再改。\n\n【本弹窗输入】\n条数T1/T4/T5「固定」T2「不少于」——顺时针与逆时针的加权条数每条摇取结果计一次须分别满足所填数值T1、T4 与 T5 分开填写。\n结算金额 标准:同档位各格使用同一数值。生成时 T1T4 的 显示文本 / 显示文本(英文) = 结算金额T5 固定为「再来一次」/「Once again」。备注仍区分完美回本/小赚等。",
"ruleGenT1Row": "T1 大奖",
"ruleGenT2Row": "T2 小赚/回本",
"ruleGenT3RealEvOnly": "T3 抽水",
@@ -61,13 +62,13 @@
"ruleGenT5Row": "T5 再来一次",
"ruleGenMinCount": "最少条数",
"ruleGenFixedCount": "固定条数(顺/逆)",
"ruleGenRealEvStd": "real_ev 标准",
"ruleGenRealEvStd": "结算金额",
"ruleGenRealEvEditHint": "生成并保存后,仍可在本页表格中逐条修改显示文案、英文、真实结算与备注。",
"ruleGenInvalidT1RealEv": "T1 的 real_ev 须满足:100 < 值 < 500",
"ruleGenInvalidT2RealEv": "T2 的 real_ev 须满足0 < 值 < 100",
"ruleGenInvalidT3RealEv": "T3 的 real_ev 须满足:-100 < 值 < 0",
"ruleGenInvalidT4RealEv": "T4 的 real_ev 须满足:值 < -100",
"ruleGenInvalidT5RealEv": "T5「再来一次」的 real_ev 须为 0",
"ruleGenInvalidT1RealEv": "T1 的 结算金额 满足:200 < 值",
"ruleGenInvalidT2RealEv": "T2 的 结算金额 满足:100 < 值 < 200",
"ruleGenInvalidT3RealEv": "T3 的 结算金额 满足:0 < 值 < 100",
"ruleGenInvalidT4RealEv": "T4 的 结算金额 满足:值 < 0",
"ruleGenInvalidT5RealEv": "T5「再来一次」的 结算金额 须为 0",
"ruleGenT1Min": "T1 固定条数(顺/逆)",
"ruleGenT2Min": "T2 最少条数(顺/逆)",
"ruleGenT4Max": "T4 固定条数(顺/逆)",

View File

@@ -78,12 +78,18 @@
<template #default="{ row }">
<ElInputNumber
v-model="row.real_ev"
@change="handleRealEvChange(row)"
controls-position="right"
size="small"
class="full-width"
/>
</template>
</ElTableColumn>
<ElTableColumn :label="$t('page.configPage.colRealReward')" min-width="130" align="center">
<template #default="{ row }">
<span>{{ calcRealReward(row.real_ev) }}</span>
</template>
</ElTableColumn>
<ElTableColumn :label="$t('page.configPage.colTier')" width="100" align="center">
<template #default="{ row }">
<ElSelect
@@ -162,6 +168,7 @@
<template #default="{ row }">
<ElInputNumber
v-model="row.real_ev"
@change="handleRealEvChange(row)"
controls-position="right"
size="small"
class="full-width"
@@ -481,6 +488,24 @@
}
}
function calcRealReward(realEv: unknown): number {
const n = typeof realEv === 'number' && !Number.isNaN(realEv) ? realEv : Number(realEv)
if (Number.isNaN(n)) {
return -100
}
return n - 100
}
function handleRealEvChange(row: IndexRow) {
const n =
typeof row.real_ev === 'number' && !Number.isNaN(row.real_ev)
? row.real_ev
: Number(row.real_ev)
const text = Number.isNaN(n) ? '' : String(n)
row.ui_text = text
row.ui_text_en = text
}
async function handleCreateRewardReference() {
try {
await ElMessageBox.confirm(

View File

@@ -22,7 +22,11 @@
<el-input v-model="formData.ui_text_en" :placeholder="$t('page.form.placeholderUiTextEn')" />
</el-form-item>
<el-form-item :label="$t('page.form.labelRealEv')" prop="real_ev">
<el-input-number v-model="formData.real_ev" :placeholder="$t('page.form.placeholderRealEv')" />
<el-input-number
v-model="formData.real_ev"
:placeholder="$t('page.form.placeholderRealEv')"
@change="handleRealEvChange"
/>
</el-form-item>
<el-form-item :label="$t('page.form.labelTier')" prop="tier">
<el-select
@@ -237,6 +241,16 @@
console.log('表单验证失败:', error)
}
}
const handleRealEvChange = () => {
const n =
typeof formData.real_ev === 'number' && !Number.isNaN(formData.real_ev)
? formData.real_ev
: Number(formData.real_ev)
const text = Number.isNaN(n) ? '' : String(n)
formData.ui_text = text
formData.ui_text_en = text
}
</script>
<style lang="scss" scoped>

View File

@@ -53,12 +53,12 @@ export interface TierRealEvStandards {
T5: number
}
/** 默认标准(与常见业务约定一致100&lt;T1&lt;500 */
/** 默认标准(与规则弹窗说明一致 */
export const DEFAULT_TIER_REAL_EV_STANDARDS: TierRealEvStandards = {
T1: 400,
T2: 50,
T3: -80,
T4: -140,
T1: 300,
T2: 150,
T3: 50,
T4: -40,
T5: 0
}
@@ -66,16 +66,16 @@ export const DEFAULT_TIER_REAL_EV_STANDARDS: TierRealEvStandards = {
* 校验档位与 real_ev 区间是否一致;通过返回 null否则返回 i18n 键名(不含 page.configPage. 前缀)
*/
export function validateTierRealEvStandards(s: TierRealEvStandards): string | null {
if (!Number.isFinite(s.T1) || !(s.T1 > 100 && s.T1 < 500)) {
if (!Number.isFinite(s.T1) || !(s.T1 > 200)) {
return 'ruleGenInvalidT1RealEv'
}
if (!Number.isFinite(s.T2) || !(s.T2 > 0 && s.T2 < 100)) {
if (!Number.isFinite(s.T2) || !(s.T2 > 100 && s.T2 < 200)) {
return 'ruleGenInvalidT2RealEv'
}
if (!Number.isFinite(s.T3) || !(-100 < s.T3 && s.T3 < 0)) {
if (!Number.isFinite(s.T3) || !(s.T3 > 0 && s.T3 < 100)) {
return 'ruleGenInvalidT3RealEv'
}
if (!Number.isFinite(s.T4) || !(s.T4 < -100)) {
if (!Number.isFinite(s.T4) || !(s.T4 < 0)) {
return 'ruleGenInvalidT4RealEv'
}
if (!Number.isFinite(s.T5) || s.T5 !== 0) {
@@ -271,10 +271,21 @@ export function generateTiers(input: GenerateTierInput): GenerateTierResult {
return { ok: false, message: '在当前盘面与约束下未找到可行解,请调整 T1/T4/T5 固定条数或放宽 T2 下限后重试' }
}
function uiTextByTierWhenStandards(
tier: IndexTier,
realEv: number
): { ui_text: string; ui_text_en: string } {
if (tier === 'T5') {
return { ui_text: '再来一次', ui_text_en: 'Once again' }
}
const value = String(realEv)
return { ui_text: value, ui_text_en: value }
}
/** 展示文案:直接使用真实结算值(中英文相同) */
function uiTextFromRealEv(realEv: number): { ui_text: string; ui_text_en: string } {
const s = String(realEv)
return { ui_text: s, ui_text_en: s }
const value = String(realEv)
return { ui_text: value, ui_text_en: value }
}
/**
@@ -315,31 +326,31 @@ export function buildRowsFromTiers(
if (standards !== undefined) {
if (tier === 'T1') {
real_ev = standards.T1
const f = uiTextFromRealEv(real_ev)
const f = uiTextByTierWhenStandards(tier, real_ev)
ui_text = f.ui_text
ui_text_en = f.ui_text_en
remark = '大奖格'
} else if (tier === 'T2') {
real_ev = standards.T2
const f = uiTextFromRealEv(real_ev)
const f = uiTextByTierWhenStandards(tier, real_ev)
ui_text = f.ui_text
ui_text_en = f.ui_text_en
remark = standards.T2 <= 1 ? '完美回本' : '小赚'
} else if (tier === 'T3') {
real_ev = standards.T3
const f = uiTextFromRealEv(real_ev)
const f = uiTextByTierWhenStandards(tier, real_ev)
ui_text = f.ui_text
ui_text_en = f.ui_text_en
remark = '抽水'
} else if (tier === 'T4') {
real_ev = standards.T4
const f = uiTextFromRealEv(real_ev)
const f = uiTextByTierWhenStandards(tier, real_ev)
ui_text = f.ui_text
ui_text_en = f.ui_text_en
remark = '惩罚'
} else {
real_ev = standards.T5
const f = uiTextFromRealEv(real_ev)
const f = uiTextByTierWhenStandards(tier, real_ev)
ui_text = f.ui_text
ui_text_en = f.ui_text_en
remark = '前端需要在播放一次动画(特殊)'