yumi-admin/apps/src/views/operate/game-lottery-config.vue
2026-03-31 17:40:10 +08:00

301 lines
7.3 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.

<script lang="ts" setup>
import {
computed,
reactive,
ref,
watch } from 'vue';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import {
pageGameLotteryGroup,
saveOrUpdateGameLotteryGroup,
} from '#/api/legacy/game';
import RewardRow from '#/views/props/components/reward-row.vue';
import { formatDate,
getAllowedSysOrigins } from '#/views/system/shared';
import {
Button,
Card,
Input,
Modal,
Pagination,
Select,
Space,
Table,
TextArea,
message
} from 'antdv-next';
import { LOTTERY_GAME_TYPE_OPTIONS } from './constants';
defineOptions({ name: 'OperateGameLotteryConfig' });
const accessStore = useAccessStore();
const sysOriginOptions = computed(() => {
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
return options.length > 0 ? options : getAllowedSysOrigins([]);
});
const loading = ref(false);
const total = ref(0);
const list = ref<Array<Record<string, any>>>([]);
const editorOpen = ref(false);
const editorTitle = ref('添加');
const editorJson = ref('{}');
const editorSaving = ref(false);
const query = reactive({
cursor: 1,
gameType: '',
id: '',
limit: 20,
sysOrigin: '',
});
const columns = [
{ dataIndex: 'id', key: 'id', title: 'ID', width: 120 },
{ dataIndex: 'gameType', key: 'gameType', title: '类型', width: 120 },
{ dataIndex: 'rewardConfigList', key: 'rewardConfigList', title: '道具', width: 360 },
{ dataIndex: 'time', key: 'time', title: '时间', width: 220 },
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 100 },
];
watch(
sysOriginOptions,
(options) => {
if (!query.sysOrigin && options.length > 0) {
query.sysOrigin = String(options[0]?.value || '');
void loadData(true);
}
},
{ immediate: true },
);
async function loadData(reset = false) {
if (!query.sysOrigin) {
return;
}
if (reset) {
query.cursor = 1;
}
loading.value = true;
try {
const result = await pageGameLotteryGroup({ ...query });
list.value = result.records || [];
total.value = result.total || 0;
} finally {
loading.value = false;
}
}
function handlePageChange(page: number, pageSize: number) {
query.cursor = page;
query.limit = pageSize;
void loadData();
}
function openCreate() {
editorTitle.value = '添加';
editorJson.value = JSON.stringify(
{
gameType: 'EGG',
rewardConfigList: [],
sysOrigin: query.sysOrigin,
},
null,
2,
);
editorOpen.value = true;
}
function openEdit(row: Record<string, any>) {
editorTitle.value = '编辑';
editorJson.value = JSON.stringify(row || {}, null, 2);
editorOpen.value = true;
}
async function handleSave() {
let payload: Record<string, any> = {};
try {
payload = JSON.parse(editorJson.value || '{}');
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
message.warning('配置必须是对象');
return;
}
} catch {
message.warning('JSON 格式不正确');
return;
}
if (!payload.gameType) {
message.warning('gameType 不能为空');
return;
}
payload.sysOrigin = payload.sysOrigin || query.sysOrigin;
payload.rewardConfigList = Array.isArray(payload.rewardConfigList)
? payload.rewardConfigList.map((item: Record<string, any>, index: number) => ({
...item,
sort: item.sort || index + 1,
}))
: [];
const probabilitySum = payload.rewardConfigList.reduce(
(sum: number, item: Record<string, any>) =>
sum + Number(item.probability || 0),
0,
);
if (payload.rewardConfigList.length > 0 && probabilitySum.toFixed(3) !== '1.000') {
message.warning('rewardConfigList 概率总和必须等于 1');
return;
}
editorSaving.value = true;
try {
await saveOrUpdateGameLotteryGroup(payload);
message.success('保存成功');
editorOpen.value = false;
await loadData(true);
} finally {
editorSaving.value = false;
}
}
</script>
<template>
<Page title="抽奖概率配置">
<Card>
<Space class="toolbar" wrap>
<SysOriginSelect
v-model:value="query.sysOrigin"
style="width: 160px"
@change="loadData(true)"
:options="sysOriginOptions"
></SysOriginSelect>
<Input
v-model:value="query.id"
allow-clear
placeholder="ID"
style="width: 180px"
/>
<Select option-label-prop="label"
v-model:value="query.gameType"
allow-clear
placeholder="游戏类型"
style="width: 160px"
@change="loadData(true)"
:options="LOTTERY_GAME_TYPE_OPTIONS.map((item) => ({ label: `${item.name}`, value: item.value as any }))"
/>
<Button :loading="loading" type="primary" @click="loadData(true)">
搜索
</Button>
<Button type="primary" @click="openCreate">
添加
</Button>
</Space>
<Table
:columns="columns"
:data-source="list"
:loading="loading"
:pagination="false"
row-key="id"
:scroll="{ x: 920 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'gameType'">
{{
LOTTERY_GAME_TYPE_OPTIONS.find((item) => item.value === record.gameType)?.name
|| record.gameType
|| '-'
}}
</template>
<template v-else-if="column.key === 'rewardConfigList'">
<RewardRow :list="record.rewardConfigList" />
</template>
<template v-else-if="column.key === 'time'">
<div>创建{{ formatDate(record.createTime) }}</div>
<div>修改{{ formatDate(record.updateTime) }}</div>
</template>
<template v-else-if="column.key === 'actions'">
<Button size="small" type="link" @click="openEdit(record)">
编辑
</Button>
</template>
</template>
</Table>
<div class="pager">
<Pagination
:current="query.cursor"
:page-size="query.limit"
:total="total"
show-size-changer
@change="handlePageChange"
@showSizeChange="handlePageChange"
/>
</div>
</Card>
<Modal
:confirm-loading="editorSaving"
:open="editorOpen"
destroy-on-close
:title="editorTitle"
width="980"
@cancel="editorOpen = false"
@ok="handleSave"
>
<div class="editor-tips">
<div>这里保持原项目保存的完整数据结构</div>
<div>请直接编辑 `gameType / sysOrigin / rewardConfigList` 等字段</div>
</div>
<TextArea
v-model:value="editorJson"
:auto-size="{ minRows: 24, maxRows: 36 }"
class="json-editor"
spellcheck="false"
/>
</Modal>
</Page>
</template>
<style scoped>
.toolbar {
margin-bottom: 16px;
}
.pager {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
.editor-tips {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
color: #475569;
line-height: 1.7;
margin-bottom: 12px;
padding: 12px 14px;
}
.json-editor {
font-family:
'SFMono-Regular',
'Menlo',
'Monaco',
'Consolas',
'Liberation Mono',
'Courier New',
monospace;
}
</style>