[色子游戏]玩家抽奖记录-新增字段保存中奖详情记录信息

This commit is contained in:
2026-03-04 10:21:05 +08:00
parent 5b39efc7a3
commit 894a562eb4
7 changed files with 269 additions and 10 deletions

View File

@@ -62,6 +62,18 @@
{{ row.is_win === 0 ? '无' : row.is_win === 1 ? '中奖' : '-' }}
</ElTag>
</template>
<!-- 方向 tag -->
<template #direction="{ row }">
<ElTag size="small" :type="row.direction === 0 ? 'primary' : 'warning'">
{{ row.direction === 0 ? '顺时针' : row.direction === 1 ? '逆时针' : '-' }}
</ElTag>
</template>
<!-- 摇取点数 tag -->
<template #roll_array="{ row }">
<ElTag size="small">
{{ formatRollArray(row.roll_array) }}
</ElTag>
</template>
<!-- 操作列 -->
<template #operation="{ row }">
<div class="flex gap-2">
@@ -106,7 +118,8 @@
win_coin_min: undefined,
win_coin_max: undefined,
reward_ui_text: undefined,
reward_tier: undefined
reward_tier: undefined,
direction: undefined
})
// 搜索处理
@@ -122,6 +135,21 @@
const rewardTierFormatter = (row: Record<string, any>) =>
row?.diceRewardConfig?.tier ?? row?.reward_config_id ?? '-'
/** 摇取点数格式化为 1,3,4,5,6,6 */
function formatRollArray(val: unknown): string {
if (val == null || val === '') return '-'
if (Array.isArray(val)) return val.join(',')
if (typeof val === 'string') {
try {
const arr = JSON.parse(val)
return Array.isArray(arr) ? arr.join(',') : val
} catch {
return val
}
}
return String(val)
}
// 表格配置
const {
columns,
@@ -156,6 +184,10 @@
{ prop: 'lottery_type', label: '抽奖类型', width: 100, useSlot: true },
{ prop: 'is_win', label: '中奖', width: 80, useSlot: true },
{ prop: 'win_coin', label: '赢取平台币' },
{ prop: 'direction', label: '方向', width: 90, useSlot: true },
{ prop: 'start_index', label: '起始索引', width: 90 },
{ prop: 'target_index', label: '终点索引', width: 90 },
{ prop: 'roll_array', label: '摇取点数', width: 140, useSlot: true },
{
prop: 'reward_config_id',
label: '奖励配置',

View File

@@ -75,6 +75,53 @@
:disabled="dialogType === 'edit'"
/>
</el-form-item>
<el-form-item label="方向" prop="direction">
<el-select
v-model="formData.direction"
placeholder="请选择方向"
clearable
style="width: 100%"
:disabled="dialogType === 'edit'"
>
<el-option label="顺时针" :value="0" />
<el-option label="逆时针" :value="1" />
</el-select>
</el-form-item>
<el-form-item label="起始索引" prop="start_index">
<el-input-number
v-model="formData.start_index"
placeholder="起始索引"
:min="0"
style="width: 100%"
:disabled="dialogType === 'edit'"
/>
</el-form-item>
<el-form-item label="终点索引" prop="target_index">
<el-input-number
v-model="formData.target_index"
placeholder="终点索引"
:min="0"
style="width: 100%"
:disabled="dialogType === 'edit'"
/>
</el-form-item>
<el-form-item label="摇取点数" prop="rollArrayItems">
<div class="roll-array-wrap">
<el-input-number
v-for="(_, i) in 6"
:key="i"
v-model="formData.rollArrayItems[i]"
:min="1"
:max="6"
:precision="0"
controls-position="right"
placeholder=""
class="roll-array-input"
:disabled="dialogType === 'edit'"
/>
</div>
<div class="roll-array-hint">固定 6 个数每个 16</div>
</el-form-item>
<el-form-item label="奖励配置" prop="reward_config_id">
<el-select
v-model="formData.reward_config_id"
@@ -141,6 +188,23 @@
lottery_type: [{ required: true, message: '请选择抽奖类型', trigger: 'change' }],
is_win: [{ required: true, message: '请选择中奖', trigger: 'change' }],
win_coin: [{ required: true, message: '赢取平台币必填', trigger: 'blur' }],
rollArrayItems: [
{
validator: (_rule: any, value: (number | null)[], callback: (e?: Error) => void) => {
if (!value || value.length !== 6) {
callback(new Error('摇取点数必须为 6 个数'))
return
}
const ok = value.every((n) => n != null && n >= 1 && n <= 6)
if (!ok) {
callback(new Error('摇取点数必须填写 6 个数,每个 16'))
return
}
callback()
},
trigger: 'change'
}
],
reward_config_id: [{ required: true, message: '请选择奖励配置', trigger: 'change' }]
})
@@ -155,10 +219,20 @@
lottery_type: null as number | null,
is_win: null as number | null,
win_coin: null as number | null,
direction: null as number | null,
start_index: null as number | null,
target_index: null as number | null,
roll_array: null as string | number[] | null,
reward_config_id: null as number | null
}
const formData = reactive({ ...initialFormData })
/** 摇取点数固定 6 位 [n0..n5],每项 16 */
const rollArrayItemsDefault = (): (number | null)[] => [null, null, null, null, null, null]
const formData = reactive({
...initialFormData,
rollArrayItems: rollArrayItemsDefault() as (number | null)[]
})
watch(
() => props.modelValue,
@@ -188,7 +262,7 @@
)
const initPage = async () => {
Object.assign(formData, { ...initialFormData })
Object.assign(formData, { ...initialFormData, rollArrayItems: rollArrayItemsDefault() })
if (props.data) {
await nextTick()
initForm()
@@ -204,16 +278,47 @@
'lottery_type',
'is_win',
'win_coin',
'direction',
'start_index',
'target_index',
'roll_array',
'reward_config_id'
]
keys.forEach((key) => {
const val = props.data![key]
if (val != null && val !== undefined) {
if (key === 'roll_array') {
formData.roll_array = val
formData.rollArrayItems = parseRollArrayToItems(val)
} else {
;(formData as Record<string, unknown>)[key] = val
}
}
})
}
/** 将接口的 roll_array 转为固定 6 项数组,不足补 null */
function parseRollArrayToItems(val: unknown): (number | null)[] {
let arr: number[] = []
if (Array.isArray(val)) {
arr = val.map((n) => (typeof n === 'number' && !Number.isNaN(n) ? n : 0)).slice(0, 6)
} else if (typeof val === 'string') {
try {
const parsed = JSON.parse(val)
arr = Array.isArray(parsed) ? parsed.slice(0, 6).map((n: any) => Number(n) || 0) : []
} catch {
arr = val
.split(',')
.map((n) => parseInt(n, 10))
.filter((n) => !Number.isNaN(n))
.slice(0, 6)
}
}
const items: (number | null)[] = [...arr]
while (items.length < 6) items.push(null)
return items.slice(0, 6)
}
const handleClose = () => {
visible.value = false
formRef.value?.resetFields()
@@ -223,19 +328,56 @@
if (!formRef.value) return
try {
await formRef.value.validate()
const payload = { ...formData } as Record<string, unknown>
// 将 6 个输入值拼成 [1,2,3,4,5,6] 格式,确保每项为 16 的整数
const items = formData.rollArrayItems
payload.roll_array = items.map((n) => {
const v = n != null ? Number(n) : 1
return Math.min(6, Math.max(1, Number.isNaN(v) ? 1 : Math.floor(v)))
})
delete payload.rollArrayItems
if (props.dialogType === 'add') {
const rest = { ...formData } as Record<string, unknown>
delete rest.id
await api.save(rest)
delete payload.id
await api.save(payload)
ElMessage.success('新增成功')
} else {
await api.update(formData)
await api.update(payload)
ElMessage.success('修改成功')
}
emit('success')
handleClose()
} catch (error) {
console.log('表单验证失败:', error)
} catch (error: any) {
let msg = '表单验证失败,请检查必填项与格式'
if (error?.message) {
msg = error.message
} else if (typeof error === 'string') {
msg = error
} else if (error && typeof error === 'object') {
const first = Object.values(error).find((v: any) => v?.[0]?.message)
if (first && Array.isArray(first)) {
msg = (first[0] as any).message || msg
}
}
ElMessage.warning(msg)
}
}
</script>
<style lang="scss" scoped>
.roll-array-wrap {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.roll-array-input {
width: 72px;
}
.roll-array-hint {
margin-top: 6px;
font-size: 12px;
color: var(--el-text-color-secondary);
}
</style>

View File

@@ -34,6 +34,14 @@
</el-select>
</el-form-item>
</el-col>
<el-col v-bind="setSpan(6)">
<el-form-item label="方向" prop="direction">
<el-select v-model="formData.direction" placeholder="全部" clearable style="width: 100%">
<el-option label="顺时针" :value="0" />
<el-option label="逆时针" :value="1" />
</el-select>
</el-form-item>
</el-col>
<el-col v-bind="setSpan(6)">
<el-form-item label="赢取平台币" prop="win_coin_min">
<div class="range-wrap">

View File

@@ -48,6 +48,7 @@ class DicePlayRecordController extends BaseController
['win_coin_max', ''],
['reward_ui_text', ''],
['reward_tier', ''],
['direction', ''],
]);
$query = $this->logic->search($where);
$query->with([

View File

@@ -24,4 +24,36 @@ class DicePlayRecordLogic extends BaseLogic
$this->model = new DicePlayRecord();
}
/**
* 添加前roll_array 转为 JSON 字符串(数据库为 string 类型)
*/
public function add(array $data): mixed
{
$data = $this->normalizeRollArray($data);
return parent::add($data);
}
/**
* 修改前roll_array 转为 JSON 字符串(数据库为 string 类型)
*/
public function edit($id, array $data): mixed
{
$data = $this->normalizeRollArray($data);
return parent::edit($id, $data);
}
/**
* 将 roll_array 从数组转为 JSON 字符串
*/
private function normalizeRollArray(array $data): array
{
if (!array_key_exists('roll_array', $data)) {
return $data;
}
$val = $data['roll_array'];
if (is_array($val)) {
$data['roll_array'] = json_encode($val, JSON_UNESCAPED_UNICODE);
}
return $data;
}
}

View File

@@ -23,8 +23,12 @@ use think\model\relation\BelongsTo;
* @property $lottery_type 抽奖类型
* @property $is_win 中奖
* @property $win_coin 赢取平台币
* @property $direction 方向:0=顺时针,1=逆时针
* @property $reward_config_id 奖励配置id
* @property $lottery_id 奖池
* @property $start_index 起始索引
* @property $target_index 结束索引
* @property $roll_array 摇取点数,格式:[1,2,3,4,5,6]
* @property $lottery_name 奖池名
* @property $create_time 创建时间
* @property $update_time 修改时间
@@ -157,4 +161,12 @@ class DicePlayRecord extends BaseModel
$query->whereRaw('1=0');
}
}
/** 方向 0=顺时针 1=逆时针 */
public function searchDirectionAttr($query, $value)
{
if ($value !== '' && $value !== null) {
$query->where('direction', '=', $value);
}
}
}

View File

@@ -23,6 +23,7 @@ class DicePlayRecordValidate extends BaseValidate
'is_win' => 'require',
'win_coin' => 'require',
'reward_config_id' => 'require',
'roll_array' => 'require|checkRollArray',
];
/**
@@ -35,6 +36,7 @@ class DicePlayRecordValidate extends BaseValidate
'is_win' => '中奖必须填写',
'win_coin' => '赢取平台币必须填写',
'reward_config_id' => '奖励配置必须填写',
'roll_array.require' => '摇取点数必须填写',
];
/**
@@ -48,6 +50,7 @@ class DicePlayRecordValidate extends BaseValidate
'is_win',
'win_coin',
'reward_config_id',
'roll_array',
],
'update' => [
'player_id',
@@ -56,7 +59,36 @@ class DicePlayRecordValidate extends BaseValidate
'is_win',
'win_coin',
'reward_config_id',
'roll_array',
],
];
/**
* 验证 roll_array必须为 6 个元素,每个值在 16 之间
* @param mixed $value
* @param mixed $rule
* @param array $data
* @param string $field
* @return bool|string
*/
protected function checkRollArray($value, $rule = '', array $data = [], string $field = '')
{
if (is_string($value)) {
$decoded = json_decode($value, true);
$value = is_array($decoded) ? $decoded : [];
}
if (!is_array($value)) {
return '摇取点数必须为数组';
}
if (count($value) !== 6) {
return '摇取点数必须为 6 个数';
}
foreach ($value as $i => $n) {
$v = is_numeric($n) ? (int) $n : null;
if ($v === null || $v < 1 || $v > 6) {
return '摇取点数第' . ($i + 1) . '个值必须在 16 之间';
}
}
return true;
}
}