转盘抽奖

This commit is contained in:
zhx 2026-05-20 13:53:14 +08:00
parent 6375b45f3a
commit 35d9c1a9d1

View File

@ -4,9 +4,14 @@ import { computed, nextTick, reactive, ref, watch } from 'vue';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import type { Dayjs } from 'dayjs';
import dayjs from 'dayjs';
import {
Avatar,
Button,
Card,
DatePicker,
Input,
InputNumber,
message,
@ -31,6 +36,8 @@ import { getAllowedSysOrigins } from '#/views/system/shared';
defineOptions({ name: 'ResidentWheelConfigPage' });
const { RangePicker } = DatePicker;
const PROBABILITY_TOTAL = 1_000_000;
const WHEEL_CATEGORIES = [
{
@ -175,12 +182,24 @@ const recordQuery = reactive<Record<string, any>>({
status: '',
userId: '',
});
const recordRangeDate = ref<[Dayjs, Dayjs] | null>([
dayjs().startOf('day'),
dayjs().endOf('day'),
]);
const recordPage = reactive<Record<string, any>>({
current: 1,
records: [],
size: 20,
total: 0,
});
const recordSummary = reactive<Record<string, any>>({
endTime: '',
participantCount: 0,
startTime: '',
totalDrawTimes: 0,
totalPaidGold: 0,
totalRewardGold: 0,
});
const rewardColumns = [
{ dataIndex: 'enabled', key: 'enabled', title: '启用', width: 80 },
@ -195,7 +214,8 @@ const rewardColumns = [
const recordColumns = [
{ dataIndex: 'drawNo', key: 'drawNo', title: '抽奖单号', width: 190 },
{ dataIndex: 'category', key: 'category', title: '转盘', width: 120 },
{ dataIndex: 'userId', key: 'userId', title: '用户ID', width: 140 },
{ dataIndex: 'user', key: 'user', title: '用户信息', width: 280 },
{ dataIndex: 'userTotalDrawTimes', key: 'userTotalDrawTimes', title: '用户总次数', width: 130 },
{ dataIndex: 'drawTimes', key: 'drawTimes', title: '次数', width: 90 },
{ dataIndex: 'paidGold', key: 'paidGold', title: '消耗金币', width: 120 },
{ dataIndex: 'reward', key: 'reward', title: '中奖奖励', width: 360 },
@ -245,6 +265,10 @@ function probabilityPercent(value: any) {
return ((Number(value || 0) * 100) / PROBABILITY_TOTAL).toFixed(4);
}
function numberText(value: any) {
return Number(value || 0).toLocaleString();
}
function normalizeReward(item: Record<string, any> | undefined, index: number) {
const value = item || {};
const rewardType = normalizeRewardTypeValue(value.rewardType);
@ -390,6 +414,25 @@ function normalizeRecordPage(result: null | Record<string, any> | undefined) {
recordPage.total = Number(value.total || 0);
recordPage.current = Number(value.current || 1);
recordPage.size = Number(value.size || 20);
Object.assign(recordSummary, {
endTime: String(value.endTime || ''),
participantCount: Number(value.participantCount || 0),
startTime: String(value.startTime || ''),
totalDrawTimes: Number(value.totalDrawTimes || 0),
totalPaidGold: Number(value.totalPaidGold || 0),
totalRewardGold: Number(value.totalRewardGold || 0),
});
}
function recordTimeParams() {
const range = recordRangeDate.value;
if (!range?.length) {
return { endTime: '', startTime: '' };
}
return {
endTime: range[1]?.format('YYYY-MM-DD HH:mm:ss') || '',
startTime: range[0]?.format('YYYY-MM-DD HH:mm:ss') || '',
};
}
async function loadRecords(reset = false) {
@ -403,6 +446,7 @@ async function loadRecords(reset = false) {
try {
const result = await pageResidentWheelDrawRecords({
...recordQuery,
...recordTimeParams(),
sysOrigin: selectedSysOrigin.value,
});
normalizeRecordPage(result);
@ -803,6 +847,15 @@ function statusColor(status: string) {
}
}
function userAvatarFallback(record: Record<string, any>) {
const nickname = String(record.userNickname || '').trim();
if (nickname) {
return nickname.slice(0, 1).toUpperCase();
}
const shortId = String(record.userShortId || '').trim();
return shortId ? shortId.slice(0, 1).toUpperCase() : 'U';
}
function coverStyle(url: string) {
return {
backgroundImage: `url("${String(url || '').replaceAll('"', '%22')}")`,
@ -1103,12 +1156,36 @@ watch(activePageTab, (value) => {
placeholder="状态"
style="width: 130px"
/>
<RangePicker
v-model:value="recordRangeDate"
show-time
style="width: 380px"
/>
<Button :loading="recordLoading" type="primary" @click="handleRecordSearch">
查询
</Button>
</Space>
</div>
<div class="record-summary">
<div class="record-summary-item">
<span>用户总抽奖消耗金币</span>
<strong>{{ numberText(recordSummary.totalPaidGold) }}</strong>
</div>
<div class="record-summary-item">
<span>礼物发放金币总价值</span>
<strong>{{ numberText(recordSummary.totalRewardGold) }}</strong>
</div>
<div class="record-summary-item">
<span>用户总抽奖次数</span>
<strong>{{ numberText(recordSummary.totalDrawTimes) }}</strong>
</div>
<div class="record-summary-item">
<span>用户参与量</span>
<strong>{{ numberText(recordSummary.participantCount) }}</strong>
</div>
</div>
<Table
:columns="recordColumns"
:data-source="recordPage.records"
@ -1120,13 +1197,28 @@ watch(activePageTab, (value) => {
total: recordPage.total,
}"
row-key="id"
:scroll="{ x: 1790 }"
:scroll="{ x: 2060 }"
@change="handleRecordTableChange"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'category'">
<Tag>{{ categoryLabel(record.category) }}</Tag>
</template>
<template v-else-if="column.key === 'user'">
<div class="record-user">
<Avatar :size="36" :src="record.userAvatar || undefined">
{{ userAvatarFallback(record) }}
</Avatar>
<div class="record-user-info">
<div class="record-user-name">{{ record.userNickname || '-' }}</div>
<div class="sub-text">长ID{{ record.userId || '-' }}</div>
<div class="sub-text">短ID{{ record.userShortId || '-' }}</div>
</div>
</div>
</template>
<template v-else-if="column.key === 'userTotalDrawTimes'">
{{ numberText(record.userTotalDrawTimes) }}
</template>
<template v-else-if="column.key === 'reward'">
<div class="record-reward">
<div
@ -1216,12 +1308,50 @@ watch(activePageTab, (value) => {
}
.record-reward,
.record-user,
.resource-summary {
align-items: center;
display: flex;
gap: 10px;
}
.record-summary {
display: grid;
gap: 10px;
grid-template-columns: repeat(4, minmax(160px, 1fr));
margin-bottom: 16px;
}
.record-summary-item {
background: rgb(248 250 252);
border: 1px solid rgb(226 232 240);
border-radius: 6px;
padding: 12px 14px;
}
.record-summary-item span {
color: rgb(100 116 139);
display: block;
font-size: 12px;
margin-bottom: 4px;
}
.record-summary-item strong {
color: rgb(15 23 42);
font-size: 20px;
font-weight: 700;
}
.record-user-info {
min-width: 0;
}
.record-user-name {
color: rgb(15 23 42);
font-weight: 600;
line-height: 20px;
}
.resource-summary {
margin-top: 8px;
}