砸蛋和转盘

This commit is contained in:
zhx 2026-05-15 09:58:36 +08:00
parent 2f9ae0a10e
commit 20c00c14d5
6 changed files with 2950 additions and 77 deletions

View File

@ -12,6 +12,8 @@ const RECHARGE_REWARD_BASE = '/go/resident-activity/recharge-reward';
const TASK_CENTER_BASE = '/go/resident-activity/task-center';
const VOICE_ROOM_RED_PACKET_BASE = '/go/resident-activity/voice-room-red-packet';
const VOICE_ROOM_ROCKET_BASE = '/go/resident-activity/voice-room-rocket';
const WHEEL_BASE = '/go/resident-activity/wheel';
const SMASH_GOLDEN_EGG_BASE = '/go/resident-activity/smash-golden-egg';
export async function getResidentInviteConfig(sysOrigin: string) {
return requestClient.get<Record<string, any>>('/resident-activity/invite/config', {
@ -100,6 +102,51 @@ export async function pageResidentVipUserStates(params: Record<string, any>) {
});
}
export async function getResidentWheelConfig(sysOrigin: string) {
return requestClient.get<Record<string, any>>(`${WHEEL_BASE}/config`, {
params: { sysOrigin },
});
}
export async function saveResidentWheelConfig(data: Record<string, any>) {
return requestClient.post<Record<string, any>>(`${WHEEL_BASE}/config/save`, data);
}
export async function saveResidentWheelCategoryConfig(data: Record<string, any>) {
return requestClient.post<Record<string, any>>(
`${WHEEL_BASE}/config/category/save`,
data,
);
}
export async function pageResidentWheelDrawRecords(params: Record<string, any>) {
return requestClient.get<Record<string, any>>(`${WHEEL_BASE}/record/page`, {
params,
});
}
export async function getResidentSmashGoldenEggConfig(sysOrigin: string) {
return requestClient.get<Record<string, any>>(`${SMASH_GOLDEN_EGG_BASE}/config`, {
params: { sysOrigin },
});
}
export async function saveResidentSmashGoldenEggConfig(data: Record<string, any>) {
return requestClient.post<Record<string, any>>(
`${SMASH_GOLDEN_EGG_BASE}/config/save`,
data,
);
}
export async function pageResidentSmashGoldenEggDrawRecords(
params: Record<string, any>,
) {
return requestClient.get<Record<string, any>>(
`${SMASH_GOLDEN_EGG_BASE}/draw-record/page`,
{ params },
);
}
export async function getResidentRechargeRewardConfig(sysOrigin: string) {
return requestClient.get<Record<string, any>>(`${RECHARGE_REWARD_BASE}/config`, {
params: { sysOrigin },

View File

@ -62,6 +62,12 @@ const ROUTE_MENU_OVERRIDES: Record<
},
};
const LOCAL_RESIDENT_ACTIVITY_FALLBACK_ROUTES = new Set([
'ResidentWheelConfig',
'ResidentSmashGoldenEggConfig',
'ResidentVoiceRoomRocket',
]);
function normalizeRoutePath(path?: string | null) {
return String(path || '')
.trim()
@ -109,6 +115,7 @@ function buildAuthorizedMenuMatcher(menus: LegacyMenu[]) {
const aliasSet = new Set<string>();
const routerSet = new Set<string>();
const titleSet = new Set<string>();
let hasResidentActivityAccess = false;
const queue = [...menus];
while (queue.length > 0) {
@ -119,18 +126,34 @@ function buildAuthorizedMenuMatcher(menus: LegacyMenu[]) {
if (item.alias) {
aliasSet.add(String(item.alias));
}
if (item.router) {
routerSet.add(normalizeRoutePath(item.router));
const normalizedRouter = normalizeRoutePath(item.router);
if (normalizedRouter) {
routerSet.add(normalizedRouter);
}
if (item.menuType === 2 && item.menuName) {
titleSet.add(String(item.menuName).trim());
}
const menuName = String(item.menuName || '').trim();
if (
String(item.alias || '') === 'ResidentActivityManager' ||
menuName === '常驻活动' ||
normalizedRouter === 'resident-activity/manager' ||
normalizedRouter.startsWith('resident-activity/manager/')
) {
hasResidentActivityAccess = true;
}
if (Array.isArray(item.childrens) && item.childrens.length > 0) {
queue.push(...item.childrens);
}
}
return (route: RouteRecordRaw) => {
if (
hasResidentActivityAccess &&
LOCAL_RESIDENT_ACTIVITY_FALLBACK_ROUTES.has(String(route.name || ''))
) {
return true;
}
for (const alias of collectRouteAliases(route)) {
if (aliasSet.has(alias)) {
return true;

View File

@ -23,6 +23,19 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/resident-activity/vip-config.vue'),
meta: { title: 'VIP' },
},
{
name: 'ResidentWheelConfig',
path: 'wheel/config',
component: () => import('#/views/resident-activity/wheel-config.vue'),
meta: { title: '转盘' },
},
{
name: 'ResidentSmashGoldenEggConfig',
path: 'smash-golden-egg/config',
component: () =>
import('#/views/resident-activity/smash-golden-egg.vue'),
meta: { title: '砸蛋' },
},
{
name: 'ResidentRechargeRewardConfig',
path: 'recharge-reward/config',

View File

@ -1,14 +1,6 @@
<script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue';
import { listEmojiGroupsBySysOrigin } from '#/api/legacy/app-system';
import { listBadgePictureBySysOrigin } from '#/api/legacy/badge';
import { listGiftBySysOrigin } from '#/api/legacy/gift';
import {
listNotFamilyBySysOriginType,
listSysOriginTypeList,
} from '#/api/legacy/props';
import {
Drawer,
Empty,
@ -18,8 +10,32 @@ import {
Spin,
} from 'antdv-next';
import { listEmojiGroupsBySysOrigin } from '#/api/legacy/app-system';
import { listBadgePictureBySysOrigin } from '#/api/legacy/badge';
import { listGiftBySysOrigin } from '#/api/legacy/gift';
import {
listNotFamilyBySysOriginType,
listSysOriginTypeList,
} from '#/api/legacy/props';
defineOptions({ name: 'OperatePropsSourceSelectDrawer' });
const props = withDefaults(
defineProps<{
defaultType?: string;
open: boolean;
sysOrigin: string;
}>(),
{
defaultType: 'AVATAR_FRAME',
},
);
const emit = defineEmits<{
close: [];
select: [item: SourceItem];
}>();
type SourceTypeOption = {
label: string;
value: string;
@ -53,22 +69,6 @@ const typeSelectOptions = TYPE_OPTIONS.map((item) => ({
value: item.value as any,
}));
const props = withDefaults(
defineProps<{
defaultType?: string;
open: boolean;
sysOrigin: string;
}>(),
{
defaultType: 'AVATAR_FRAME',
},
);
const emit = defineEmits<{
close: [];
select: [item: SourceItem];
}>();
const loading = ref(false);
const keyword = ref('');
const query = reactive({
@ -103,6 +103,89 @@ const currentList = computed(() => {
);
});
function firstPresent(record: Record<string, any>, keys: string[]) {
for (const key of keys) {
const value = record?.[key];
if (value !== undefined && value !== null && String(value).trim() !== '') {
return value;
}
}
return '';
}
function normalizeSourceItem(
item: Record<string, any>,
type: string,
options: {
coverKeys?: string[];
idKeys?: string[];
nameKeys?: string[];
sourceUrlKeys?: string[];
} = {},
): SourceItem {
const id = firstPresent(
item,
options.idKeys || [
'id',
'resourceId',
'sourceId',
'propsId',
'propsSourceId',
'badgeConfigId',
'giftId',
'groupId',
],
);
return {
amount: firstPresent(item, ['amount', 'giftCandy', 'price', 'goldAmount']),
cover: String(
firstPresent(
item,
options.coverKeys || [
'cover',
'coverUrl',
'resourceCover',
'resourceCoverUrl',
'giftPhoto',
'selectUrl',
'icon',
'smallIcon',
],
),
),
id: id as number | string,
name: String(
firstPresent(
item,
options.nameKeys || [
'name',
'resourceName',
'sourceName',
'giftName',
'badgeName',
'groupName',
'title',
'remark',
],
) || (id ? `资源 ${id}` : ''),
),
sourceUrl: String(
firstPresent(
item,
options.sourceUrlKeys || [
'sourceUrl',
'resourceUrl',
'url',
'giftSourceUrl',
'animationUrl',
'playUrl',
],
),
),
type,
};
}
watch(
() => props.open,
(open) => {
@ -150,56 +233,53 @@ async function loadSource(type: string) {
store.loading = true;
loading.value = true;
try {
if (type === 'GIFT') {
const result = await listGiftBySysOrigin(props.sysOrigin);
store.list = (result || []).map((item) => ({
amount: item.giftCandy,
cover: item.giftPhoto,
id: item.id,
name: item.giftName,
sourceUrl: item.giftSourceUrl,
type,
}));
} else if (type === 'EMOJI') {
const result = await listEmojiGroupsBySysOrigin(props.sysOrigin);
store.list = (result || []).map((item) => ({
amount: item.amount,
cover: item.cover,
id: item.id || '',
name: item.groupName || '',
sourceUrl: '',
type,
}));
} else if (type === 'BADGE' || type === 'ROOM_BADGE') {
const badgeType = type === 'BADGE' ? 'ACTIVITY' : 'ROOM_ACHIEVEMENT';
const result = await listBadgePictureBySysOrigin(props.sysOrigin, badgeType);
store.list = (result || []).map((item) => ({
cover: item.selectUrl,
id: item.badgeConfigId || '',
name: item.badgeName || '',
sourceUrl: item.animationUrl,
type,
}));
} else if (type === 'NOBLE_VIP') {
const result = await listNotFamilyBySysOriginType(props.sysOrigin, type);
store.list = (result || []).map((item) => ({
amount: item.amount,
cover: item.cover,
id: item.id || '',
name: item.name || '',
sourceUrl: item.sourceUrl,
type,
}));
} else {
const result = await listSysOriginTypeList(props.sysOrigin, type);
store.list = (result || []).map((item) => ({
amount: item.amount,
cover: item.cover,
id: item.id || '',
name: item.name || '',
sourceUrl: item.sourceUrl,
type,
}));
switch (type) {
case 'BADGE':
case 'ROOM_BADGE': {
const badgeType = type === 'BADGE' ? 'ACTIVITY' : 'ROOM_ACHIEVEMENT';
const result = await listBadgePictureBySysOrigin(props.sysOrigin, badgeType);
store.list = (result || []).map((item) =>
normalizeSourceItem(item, type, {
coverKeys: ['selectUrl', 'cover', 'coverUrl'],
idKeys: ['badgeConfigId', 'id', 'badgeId'],
nameKeys: ['badgeName', 'name'],
sourceUrlKeys: ['animationUrl', 'sourceUrl', 'resourceUrl'],
}),
);
break;
}
case 'EMOJI': {
const result = await listEmojiGroupsBySysOrigin(props.sysOrigin);
store.list = (result || []).map((item) =>
normalizeSourceItem(item, type, {
idKeys: ['id', 'groupId'],
nameKeys: ['groupName', 'name'],
sourceUrlKeys: [],
}),
);
break;
}
case 'GIFT': {
const result = await listGiftBySysOrigin(props.sysOrigin);
store.list = (result || []).map((item) =>
normalizeSourceItem(item, type, {
coverKeys: ['giftPhoto', 'cover', 'coverUrl'],
idKeys: ['id', 'giftId'],
nameKeys: ['giftName', 'name'],
sourceUrlKeys: ['giftSourceUrl', 'sourceUrl', 'resourceUrl'],
}),
);
break;
}
case 'NOBLE_VIP': {
const result = await listNotFamilyBySysOriginType(props.sysOrigin, type);
store.list = (result || []).map((item) => normalizeSourceItem(item, type));
break;
}
default: {
const result = await listSysOriginTypeList(props.sysOrigin, type);
store.list = (result || []).map((item) => normalizeSourceItem(item, type));
}
}
store.loaded = true;
} finally {
@ -258,7 +338,7 @@ function handleSelect(item: SourceItem) {
:src="item.cover || 'https://dummyimage.com/88x88/e2e8f0/64748b&text=+'"
alt=""
class="source-cover"
>
/>
</div>
<div class="source-name">{{ item.name || '-' }}</div>
<div class="source-meta">ID {{ item.id }}</div>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff