转盘抽奖
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 { Page } from '@vben/common-ui';
|
||||||
import { useAccessStore } from '@vben/stores';
|
import { useAccessStore } from '@vben/stores';
|
||||||
|
|
||||||
|
import type { Dayjs } from 'dayjs';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
Avatar,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
|
DatePicker,
|
||||||
Input,
|
Input,
|
||||||
InputNumber,
|
InputNumber,
|
||||||
message,
|
message,
|
||||||
@ -31,6 +36,8 @@ import { getAllowedSysOrigins } from '#/views/system/shared';
|
|||||||
|
|
||||||
defineOptions({ name: 'ResidentWheelConfigPage' });
|
defineOptions({ name: 'ResidentWheelConfigPage' });
|
||||||
|
|
||||||
|
const { RangePicker } = DatePicker;
|
||||||
|
|
||||||
const PROBABILITY_TOTAL = 1_000_000;
|
const PROBABILITY_TOTAL = 1_000_000;
|
||||||
const WHEEL_CATEGORIES = [
|
const WHEEL_CATEGORIES = [
|
||||||
{
|
{
|
||||||
@ -175,12 +182,24 @@ const recordQuery = reactive<Record<string, any>>({
|
|||||||
status: '',
|
status: '',
|
||||||
userId: '',
|
userId: '',
|
||||||
});
|
});
|
||||||
|
const recordRangeDate = ref<[Dayjs, Dayjs] | null>([
|
||||||
|
dayjs().startOf('day'),
|
||||||
|
dayjs().endOf('day'),
|
||||||
|
]);
|
||||||
const recordPage = reactive<Record<string, any>>({
|
const recordPage = reactive<Record<string, any>>({
|
||||||
current: 1,
|
current: 1,
|
||||||
records: [],
|
records: [],
|
||||||
size: 20,
|
size: 20,
|
||||||
total: 0,
|
total: 0,
|
||||||
});
|
});
|
||||||
|
const recordSummary = reactive<Record<string, any>>({
|
||||||
|
endTime: '',
|
||||||
|
participantCount: 0,
|
||||||
|
startTime: '',
|
||||||
|
totalDrawTimes: 0,
|
||||||
|
totalPaidGold: 0,
|
||||||
|
totalRewardGold: 0,
|
||||||
|
});
|
||||||
|
|
||||||
const rewardColumns = [
|
const rewardColumns = [
|
||||||
{ dataIndex: 'enabled', key: 'enabled', title: '启用', width: 80 },
|
{ dataIndex: 'enabled', key: 'enabled', title: '启用', width: 80 },
|
||||||
@ -195,7 +214,8 @@ const rewardColumns = [
|
|||||||
const recordColumns = [
|
const recordColumns = [
|
||||||
{ dataIndex: 'drawNo', key: 'drawNo', title: '抽奖单号', width: 190 },
|
{ dataIndex: 'drawNo', key: 'drawNo', title: '抽奖单号', width: 190 },
|
||||||
{ dataIndex: 'category', key: 'category', title: '转盘', width: 120 },
|
{ 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: 'drawTimes', key: 'drawTimes', title: '次数', width: 90 },
|
||||||
{ dataIndex: 'paidGold', key: 'paidGold', title: '消耗金币', width: 120 },
|
{ dataIndex: 'paidGold', key: 'paidGold', title: '消耗金币', width: 120 },
|
||||||
{ dataIndex: 'reward', key: 'reward', title: '中奖奖励', width: 360 },
|
{ dataIndex: 'reward', key: 'reward', title: '中奖奖励', width: 360 },
|
||||||
@ -245,6 +265,10 @@ function probabilityPercent(value: any) {
|
|||||||
return ((Number(value || 0) * 100) / PROBABILITY_TOTAL).toFixed(4);
|
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) {
|
function normalizeReward(item: Record<string, any> | undefined, index: number) {
|
||||||
const value = item || {};
|
const value = item || {};
|
||||||
const rewardType = normalizeRewardTypeValue(value.rewardType);
|
const rewardType = normalizeRewardTypeValue(value.rewardType);
|
||||||
@ -390,6 +414,25 @@ function normalizeRecordPage(result: null | Record<string, any> | undefined) {
|
|||||||
recordPage.total = Number(value.total || 0);
|
recordPage.total = Number(value.total || 0);
|
||||||
recordPage.current = Number(value.current || 1);
|
recordPage.current = Number(value.current || 1);
|
||||||
recordPage.size = Number(value.size || 20);
|
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) {
|
async function loadRecords(reset = false) {
|
||||||
@ -403,6 +446,7 @@ async function loadRecords(reset = false) {
|
|||||||
try {
|
try {
|
||||||
const result = await pageResidentWheelDrawRecords({
|
const result = await pageResidentWheelDrawRecords({
|
||||||
...recordQuery,
|
...recordQuery,
|
||||||
|
...recordTimeParams(),
|
||||||
sysOrigin: selectedSysOrigin.value,
|
sysOrigin: selectedSysOrigin.value,
|
||||||
});
|
});
|
||||||
normalizeRecordPage(result);
|
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) {
|
function coverStyle(url: string) {
|
||||||
return {
|
return {
|
||||||
backgroundImage: `url("${String(url || '').replaceAll('"', '%22')}")`,
|
backgroundImage: `url("${String(url || '').replaceAll('"', '%22')}")`,
|
||||||
@ -1103,12 +1156,36 @@ watch(activePageTab, (value) => {
|
|||||||
placeholder="状态"
|
placeholder="状态"
|
||||||
style="width: 130px"
|
style="width: 130px"
|
||||||
/>
|
/>
|
||||||
|
<RangePicker
|
||||||
|
v-model:value="recordRangeDate"
|
||||||
|
show-time
|
||||||
|
style="width: 380px"
|
||||||
|
/>
|
||||||
<Button :loading="recordLoading" type="primary" @click="handleRecordSearch">
|
<Button :loading="recordLoading" type="primary" @click="handleRecordSearch">
|
||||||
查询
|
查询
|
||||||
</Button>
|
</Button>
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</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
|
<Table
|
||||||
:columns="recordColumns"
|
:columns="recordColumns"
|
||||||
:data-source="recordPage.records"
|
:data-source="recordPage.records"
|
||||||
@ -1120,13 +1197,28 @@ watch(activePageTab, (value) => {
|
|||||||
total: recordPage.total,
|
total: recordPage.total,
|
||||||
}"
|
}"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
:scroll="{ x: 1790 }"
|
:scroll="{ x: 2060 }"
|
||||||
@change="handleRecordTableChange"
|
@change="handleRecordTableChange"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'category'">
|
<template v-if="column.key === 'category'">
|
||||||
<Tag>{{ categoryLabel(record.category) }}</Tag>
|
<Tag>{{ categoryLabel(record.category) }}</Tag>
|
||||||
</template>
|
</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'">
|
<template v-else-if="column.key === 'reward'">
|
||||||
<div class="record-reward">
|
<div class="record-reward">
|
||||||
<div
|
<div
|
||||||
@ -1216,12 +1308,50 @@ watch(activePageTab, (value) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.record-reward,
|
.record-reward,
|
||||||
|
.record-user,
|
||||||
.resource-summary {
|
.resource-summary {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
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 {
|
.resource-summary {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user