转盘抽奖
This commit is contained in:
parent
6375b45f3a
commit
35d9c1a9d1
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user