砸蛋和转盘

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 TASK_CENTER_BASE = '/go/resident-activity/task-center';
const VOICE_ROOM_RED_PACKET_BASE = '/go/resident-activity/voice-room-red-packet'; 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 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) { export async function getResidentInviteConfig(sysOrigin: string) {
return requestClient.get<Record<string, any>>('/resident-activity/invite/config', { 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) { export async function getResidentRechargeRewardConfig(sysOrigin: string) {
return requestClient.get<Record<string, any>>(`${RECHARGE_REWARD_BASE}/config`, { return requestClient.get<Record<string, any>>(`${RECHARGE_REWARD_BASE}/config`, {
params: { sysOrigin }, 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) { function normalizeRoutePath(path?: string | null) {
return String(path || '') return String(path || '')
.trim() .trim()
@ -109,6 +115,7 @@ function buildAuthorizedMenuMatcher(menus: LegacyMenu[]) {
const aliasSet = new Set<string>(); const aliasSet = new Set<string>();
const routerSet = new Set<string>(); const routerSet = new Set<string>();
const titleSet = new Set<string>(); const titleSet = new Set<string>();
let hasResidentActivityAccess = false;
const queue = [...menus]; const queue = [...menus];
while (queue.length > 0) { while (queue.length > 0) {
@ -119,18 +126,34 @@ function buildAuthorizedMenuMatcher(menus: LegacyMenu[]) {
if (item.alias) { if (item.alias) {
aliasSet.add(String(item.alias)); aliasSet.add(String(item.alias));
} }
if (item.router) { const normalizedRouter = normalizeRoutePath(item.router);
routerSet.add(normalizeRoutePath(item.router)); if (normalizedRouter) {
routerSet.add(normalizedRouter);
} }
if (item.menuType === 2 && item.menuName) { if (item.menuType === 2 && item.menuName) {
titleSet.add(String(item.menuName).trim()); 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) { if (Array.isArray(item.childrens) && item.childrens.length > 0) {
queue.push(...item.childrens); queue.push(...item.childrens);
} }
} }
return (route: RouteRecordRaw) => { return (route: RouteRecordRaw) => {
if (
hasResidentActivityAccess &&
LOCAL_RESIDENT_ACTIVITY_FALLBACK_ROUTES.has(String(route.name || ''))
) {
return true;
}
for (const alias of collectRouteAliases(route)) { for (const alias of collectRouteAliases(route)) {
if (aliasSet.has(alias)) { if (aliasSet.has(alias)) {
return true; return true;

View File

@ -23,6 +23,19 @@ const routes: RouteRecordRaw[] = [
component: () => import('#/views/resident-activity/vip-config.vue'), component: () => import('#/views/resident-activity/vip-config.vue'),
meta: { title: 'VIP' }, 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', name: 'ResidentRechargeRewardConfig',
path: 'recharge-reward/config', path: 'recharge-reward/config',

View File

@ -1,14 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, reactive, ref, watch } from 'vue'; 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 { import {
Drawer, Drawer,
Empty, Empty,
@ -18,8 +10,32 @@ import {
Spin, Spin,
} from 'antdv-next'; } 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' }); 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 = { type SourceTypeOption = {
label: string; label: string;
value: string; value: string;
@ -53,22 +69,6 @@ const typeSelectOptions = TYPE_OPTIONS.map((item) => ({
value: item.value as any, 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 loading = ref(false);
const keyword = ref(''); const keyword = ref('');
const query = reactive({ 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( watch(
() => props.open, () => props.open,
(open) => { (open) => {
@ -150,56 +233,53 @@ async function loadSource(type: string) {
store.loading = true; store.loading = true;
loading.value = true; loading.value = true;
try { try {
if (type === 'GIFT') { switch (type) {
const result = await listGiftBySysOrigin(props.sysOrigin); case 'BADGE':
store.list = (result || []).map((item) => ({ case 'ROOM_BADGE': {
amount: item.giftCandy, const badgeType = type === 'BADGE' ? 'ACTIVITY' : 'ROOM_ACHIEVEMENT';
cover: item.giftPhoto, const result = await listBadgePictureBySysOrigin(props.sysOrigin, badgeType);
id: item.id, store.list = (result || []).map((item) =>
name: item.giftName, normalizeSourceItem(item, type, {
sourceUrl: item.giftSourceUrl, coverKeys: ['selectUrl', 'cover', 'coverUrl'],
type, idKeys: ['badgeConfigId', 'id', 'badgeId'],
})); nameKeys: ['badgeName', 'name'],
} else if (type === 'EMOJI') { sourceUrlKeys: ['animationUrl', 'sourceUrl', 'resourceUrl'],
const result = await listEmojiGroupsBySysOrigin(props.sysOrigin); }),
store.list = (result || []).map((item) => ({ );
amount: item.amount, break;
cover: item.cover, }
id: item.id || '', case 'EMOJI': {
name: item.groupName || '', const result = await listEmojiGroupsBySysOrigin(props.sysOrigin);
sourceUrl: '', store.list = (result || []).map((item) =>
type, normalizeSourceItem(item, type, {
})); idKeys: ['id', 'groupId'],
} else if (type === 'BADGE' || type === 'ROOM_BADGE') { nameKeys: ['groupName', 'name'],
const badgeType = type === 'BADGE' ? 'ACTIVITY' : 'ROOM_ACHIEVEMENT'; sourceUrlKeys: [],
const result = await listBadgePictureBySysOrigin(props.sysOrigin, badgeType); }),
store.list = (result || []).map((item) => ({ );
cover: item.selectUrl, break;
id: item.badgeConfigId || '', }
name: item.badgeName || '', case 'GIFT': {
sourceUrl: item.animationUrl, const result = await listGiftBySysOrigin(props.sysOrigin);
type, store.list = (result || []).map((item) =>
})); normalizeSourceItem(item, type, {
} else if (type === 'NOBLE_VIP') { coverKeys: ['giftPhoto', 'cover', 'coverUrl'],
const result = await listNotFamilyBySysOriginType(props.sysOrigin, type); idKeys: ['id', 'giftId'],
store.list = (result || []).map((item) => ({ nameKeys: ['giftName', 'name'],
amount: item.amount, sourceUrlKeys: ['giftSourceUrl', 'sourceUrl', 'resourceUrl'],
cover: item.cover, }),
id: item.id || '', );
name: item.name || '', break;
sourceUrl: item.sourceUrl, }
type, case 'NOBLE_VIP': {
})); const result = await listNotFamilyBySysOriginType(props.sysOrigin, type);
} else { store.list = (result || []).map((item) => normalizeSourceItem(item, type));
const result = await listSysOriginTypeList(props.sysOrigin, type); break;
store.list = (result || []).map((item) => ({ }
amount: item.amount, default: {
cover: item.cover, const result = await listSysOriginTypeList(props.sysOrigin, type);
id: item.id || '', store.list = (result || []).map((item) => normalizeSourceItem(item, type));
name: item.name || '', }
sourceUrl: item.sourceUrl,
type,
}));
} }
store.loaded = true; store.loaded = true;
} finally { } finally {
@ -258,7 +338,7 @@ function handleSelect(item: SourceItem) {
:src="item.cover || 'https://dummyimage.com/88x88/e2e8f0/64748b&text=+'" :src="item.cover || 'https://dummyimage.com/88x88/e2e8f0/64748b&text=+'"
alt="" alt=""
class="source-cover" class="source-cover"
> />
</div> </div>
<div class="source-name">{{ item.name || '-' }}</div> <div class="source-name">{{ item.name || '-' }}</div>
<div class="source-meta">ID {{ item.id }}</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