feat: extend VIP resource config
This commit is contained in:
parent
17e81a9759
commit
b8bbaec2ac
@ -102,7 +102,7 @@ const nobleVipNameOptions = NOBLE_VIP_OPTIONS.map((item) => ({
|
||||
value: item.value as any,
|
||||
}));
|
||||
const badgeTypeOptions = BADGE_TYPE_OPTIONS.filter((item) =>
|
||||
['ACHIEVEMENT', 'ACTIVITY'].includes(String(item.value)),
|
||||
['VIP', 'ACHIEVEMENT', 'ACTIVITY'].includes(String(item.value)),
|
||||
).map((item) => ({
|
||||
label: item.name,
|
||||
value: item.value as any,
|
||||
|
||||
@ -21,6 +21,7 @@ export const PROPS_TYPES: OptionItem[] = [
|
||||
|
||||
export const PROPS_RESOURCE_CONFIG_TYPES: OptionItem[] = [
|
||||
...PROPS_TYPES,
|
||||
{ value: 'VIP_EFFECT_IMAGE', name: 'VIP动效图' },
|
||||
{ value: 'BADGE', name: '徽章' },
|
||||
];
|
||||
|
||||
@ -64,6 +65,7 @@ export const NOBLE_VIP_OPTIONS: OptionItem[] = [
|
||||
];
|
||||
|
||||
export const BADGE_TYPE_OPTIONS: OptionItem[] = [
|
||||
{ value: 'VIP', name: '用户-VIP徽章' },
|
||||
{ value: 'ACHIEVEMENT', name: '用户-成就徽章' },
|
||||
{ value: 'ADMINISTRATOR', name: '用户-管理员' },
|
||||
{ value: 'ACTIVITY', name: '用户-活动徽章' },
|
||||
@ -128,6 +130,7 @@ export const PROPS_SOURCE_GROUP_TYPE_MAP: Record<
|
||||
CUSTOMIZE: { value: 'CUSTOMIZE', name: '自定义(只读)' },
|
||||
FRAGMENTS: { value: 'FRAGMENTS', name: '碎片' },
|
||||
DATA_CARD: { value: 'PROPS', name: '资料卡' },
|
||||
VIP_EFFECT_IMAGE: { value: 'PROPS', name: 'VIP动效图' },
|
||||
PROP_COUPON: { value: 'PROP_COUPON', name: '道具券' },
|
||||
HONOR_ACTIVITY: { value: 'BADGE', name: '荣誉-活动' },
|
||||
};
|
||||
@ -290,6 +293,7 @@ export function isPropsSourceType(type?: null | string) {
|
||||
'NOBLE_VIP',
|
||||
'RIDE',
|
||||
'THEME',
|
||||
'VIP_EFFECT_IMAGE',
|
||||
].includes(String(type || ''));
|
||||
}
|
||||
|
||||
|
||||
@ -32,11 +32,14 @@ import {
|
||||
defineOptions({ name: 'ResidentVipConfigPage' });
|
||||
|
||||
const resourceFields = [
|
||||
{ key: 'badge', title: '徽章', type: 'BADGE' },
|
||||
{ key: 'longBadge', title: '长徽章素材', type: 'BADGE', badgeTypes: ['VIP'] },
|
||||
{ key: 'shortBadge', title: '短徽章素材', type: 'BADGE', badgeTypes: ['ACTIVITY', 'ACHIEVEMENT'] },
|
||||
{ key: 'avatarFrame', title: '头像框', type: 'AVATAR_FRAME' },
|
||||
{ key: 'entryEffect', title: '进场特效', type: 'RIDE' },
|
||||
{ key: 'chatBubble', title: '聊天气泡', type: 'CHAT_BUBBLE' },
|
||||
{ key: 'floatPicture', title: '飘窗', type: 'FLOAT_PICTURE' },
|
||||
{ key: 'backgroundCard', title: '背景卡素材', type: 'DATA_CARD' },
|
||||
{ key: 'effectImage', title: '动效图', type: 'VIP_EFFECT_IMAGE' },
|
||||
] as const;
|
||||
|
||||
type ResourceField = (typeof resourceFields)[number];
|
||||
@ -163,16 +166,19 @@ function createResource() {
|
||||
function createLevel(level: number) {
|
||||
return {
|
||||
avatarFrame: createResource(),
|
||||
badge: createResource(),
|
||||
backgroundCard: createResource(),
|
||||
chatBubble: createResource(),
|
||||
displayName: `VIP ${level}`,
|
||||
durationDays: 30,
|
||||
enabled: false,
|
||||
entryEffect: createResource(),
|
||||
effectImage: createResource(),
|
||||
floatPicture: createResource(),
|
||||
level,
|
||||
levelCode: `VIP${level}`,
|
||||
longBadge: createResource(),
|
||||
priceGold: 0,
|
||||
shortBadge: createResource(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -282,12 +288,42 @@ async function loadResourceOptions(field: ResourceField) {
|
||||
}
|
||||
resourceLoading[field.key] = true;
|
||||
try {
|
||||
resourceRows[field.key] = await listSysOriginTypeList(form.sysOrigin, field.type);
|
||||
resourceRows[field.key] = filterResourceRowsByField(
|
||||
await listSysOriginTypeList(form.sysOrigin, field.type),
|
||||
field,
|
||||
);
|
||||
} finally {
|
||||
resourceLoading[field.key] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function filterResourceRowsByField(rows: Array<Record<string, any>>, field: ResourceField) {
|
||||
if (!('badgeTypes' in field) || !field.badgeTypes?.length) {
|
||||
return rows;
|
||||
}
|
||||
const allowed = new Set(field.badgeTypes.map((item) => String(item).toUpperCase()));
|
||||
return rows.filter((item) => allowed.has(resourceBadgeType(item)));
|
||||
}
|
||||
|
||||
function resourceBadgeType(value: Record<string, any>) {
|
||||
const expand = parseJsonObject(value.expand);
|
||||
return String(expand.badgeType || 'ACTIVITY').toUpperCase();
|
||||
}
|
||||
|
||||
function parseJsonObject(value: unknown) {
|
||||
if (typeof value !== 'string' || !value.trim()) {
|
||||
return {} as Record<string, any>;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||
? (parsed as Record<string, any>)
|
||||
: ({} as Record<string, any>);
|
||||
} catch {
|
||||
return {} as Record<string, any>;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadResourceOptionsByKey(key: string) {
|
||||
const field = findResourceField(key);
|
||||
if (field) {
|
||||
@ -329,17 +365,20 @@ function normalizeLevels(levels: Array<Record<string, any>> | undefined) {
|
||||
}
|
||||
return Array.from({ length: 5 }, (_, index) => {
|
||||
const level = index + 1;
|
||||
const item = levelMap.get(level) || createLevel(level);
|
||||
const item = (levelMap.get(level) || createLevel(level)) as Record<string, any>;
|
||||
return {
|
||||
...createLevel(level),
|
||||
displayName: String(item.displayName || `VIP ${level}`),
|
||||
enabled: Boolean(item.enabled),
|
||||
priceGold: Number(item.priceGold || 0),
|
||||
badge: normalizeResource(item.badge),
|
||||
longBadge: normalizeResource(item.longBadge || item['badge']),
|
||||
shortBadge: normalizeResource(item.shortBadge),
|
||||
avatarFrame: normalizeResource(item.avatarFrame),
|
||||
entryEffect: normalizeResource(item.entryEffect),
|
||||
chatBubble: normalizeResource(item.chatBubble),
|
||||
floatPicture: normalizeResource(item.floatPicture),
|
||||
backgroundCard: normalizeResource(item.backgroundCard),
|
||||
effectImage: normalizeResource(item.effectImage),
|
||||
};
|
||||
});
|
||||
}
|
||||
@ -357,16 +396,20 @@ function buildSavePayload() {
|
||||
sysOrigin: form.sysOrigin,
|
||||
levels: (form.levels || []).map((item: Record<string, any>) => ({
|
||||
avatarFrame: buildResourceSavePayload(item.avatarFrame),
|
||||
badge: buildResourceSavePayload(item.badge),
|
||||
badge: buildResourceSavePayload(item.longBadge),
|
||||
backgroundCard: buildResourceSavePayload(item.backgroundCard),
|
||||
chatBubble: buildResourceSavePayload(item.chatBubble),
|
||||
displayName: String(item.displayName || '').trim(),
|
||||
durationDays: 30,
|
||||
enabled: Boolean(item.enabled),
|
||||
entryEffect: buildResourceSavePayload(item.entryEffect),
|
||||
effectImage: buildResourceSavePayload(item.effectImage),
|
||||
floatPicture: buildResourceSavePayload(item.floatPicture),
|
||||
level: Number(item.level || 0),
|
||||
levelCode: `VIP${Number(item.level || 0)}`,
|
||||
longBadge: buildResourceSavePayload(item.longBadge),
|
||||
priceGold: Number(item.priceGold || 0),
|
||||
shortBadge: buildResourceSavePayload(item.shortBadge),
|
||||
})),
|
||||
};
|
||||
}
|
||||
@ -551,7 +594,7 @@ watch(
|
||||
:data-source="form.levels"
|
||||
:pagination="false"
|
||||
row-key="level"
|
||||
:scroll="{ x: 1700 }"
|
||||
:scroll="{ x: 2600 }"
|
||||
>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'level'">
|
||||
@ -600,7 +643,7 @@ watch(
|
||||
<div>固定 5 个等级,每次购买 30 天;同等级再次购买会续 30 天。</div>
|
||||
<div>资源从道具资源配置选择,保存时会记录当前资源快照。</div>
|
||||
<div>用户只能升级,升级时支付目标等级和当前等级的差价,到期后资源自动失效。</div>
|
||||
<div>升级成功后会把徽章、头像框、进场特效、聊天气泡和飘窗替换成新等级资源。</div>
|
||||
<div>升级成功后会把长徽章、短徽章、头像框、进场特效、聊天气泡、飘窗和背景卡替换成新等级资源;动效图只用于 VIP 页面展示。</div>
|
||||
</div>
|
||||
|
||||
<Space class="actions" wrap>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user