This commit is contained in:
zhx 2026-05-14 19:47:41 +08:00
parent 0a8bddbea0
commit 2f9ae0a10e
2 changed files with 351 additions and 42 deletions

View File

@ -29,6 +29,12 @@ import {
saveVoiceRoomRocketLevelConfigs, saveVoiceRoomRocketLevelConfigs,
saveVoiceRoomRocketRewardConfigs, saveVoiceRoomRocketRewardConfigs,
} from '#/api/legacy/resident-activity'; } from '#/api/legacy/resident-activity';
import { listBadgePictureBySysOrigin } from '#/api/legacy/badge';
import { listGiftBySysOrigin } from '#/api/legacy/gift';
import {
listNotFamilyBySysOriginType,
listSysOriginTypeList,
} from '#/api/legacy/props';
import { getAllowedSysOrigins } from '#/views/system/shared'; import { getAllowedSysOrigins } from '#/views/system/shared';
defineOptions({ name: 'ResidentVoiceRoomRocketPage' }); defineOptions({ name: 'ResidentVoiceRoomRocketPage' });
@ -41,7 +47,6 @@ type LevelRow = {
id?: number | string; id?: number | string;
level: number; level: number;
needEnergy: number; needEnergy: number;
progressBarUrl: string;
rocketAnimationUrl: string; rocketAnimationUrl: string;
rocketIconUrl: string; rocketIconUrl: string;
shakeThresholdPercent: string; shakeThresholdPercent: string;
@ -64,6 +69,16 @@ type RewardRow = {
weight: number; weight: number;
}; };
type ResourceOption = {
amount?: number | string;
cover: string;
id: number | string;
name: string;
remark?: string;
sourceUrl: string;
type: string;
};
const accessStore = useAccessStore(); const accessStore = useAccessStore();
const sysOriginOptions = computed(() => { const sysOriginOptions = computed(() => {
const options = getAllowedSysOrigins(accessStore.accessCodes || []); const options = getAllowedSysOrigins(accessStore.accessCodes || []);
@ -80,6 +95,12 @@ const rewardLoading = ref(false);
const configSaving = ref(false); const configSaving = ref(false);
const levelSaving = ref(false); const levelSaving = ref(false);
const rewardSaving = ref(false); const rewardSaving = ref(false);
const rocketResourceLoading = ref(false);
const rocketResourceRows = ref<ResourceOption[]>([]);
const rewardResourceLoading = reactive<Record<string, boolean>>({});
const rewardResourceRows = reactive<Record<string, ResourceOption[]>>({});
const ROCKET_RESOURCE_TYPE = 'CUSTOMIZE';
const configForm = reactive({ const configForm = reactive({
broadcastDurationSeconds: 7, broadcastDurationSeconds: 7,
@ -113,6 +134,7 @@ const rewardTypeOptions = [
{ label: 'VIP', value: 'VIP_CARD' }, { label: 'VIP', value: 'VIP_CARD' },
{ label: '礼物', value: 'GIFT' }, { label: '礼物', value: 'GIFT' },
{ label: '徽章', value: 'BADGE' }, { label: '徽章', value: 'BADGE' },
{ label: '轮空', value: 'NONE' },
]; ];
const levelOptions = computed(() => const levelOptions = computed(() =>
@ -122,6 +144,12 @@ const levelOptions = computed(() =>
})), })),
); );
function rewardTypeOptionsForRow(row: Record<string, any>) {
return row.rewardScene === 'IN_ROOM'
? rewardTypeOptions
: rewardTypeOptions.filter((item) => item.value !== 'NONE');
}
const statusMeta = computed(() => { const statusMeta = computed(() => {
if (!configForm.configured) { if (!configForm.configured) {
return { color: 'warning', text: '未配置' }; return { color: 'warning', text: '未配置' };
@ -134,10 +162,14 @@ const statusMeta = computed(() => {
const levelColumns = [ const levelColumns = [
{ dataIndex: 'level', key: 'level', title: '等级', width: 90 }, { dataIndex: 'level', key: 'level', title: '等级', width: 90 },
{ dataIndex: 'needEnergy', key: 'needEnergy', title: '所需能量', width: 160 }, { dataIndex: 'needEnergy', key: 'needEnergy', title: '所需能量', width: 160 },
{ dataIndex: 'shakeThresholdPercent', key: 'shakeThresholdPercent', title: '晃动阈值(%)', width: 160 },
{ dataIndex: 'rocketIconUrl', key: 'rocketIconUrl', title: '火箭图标', width: 260 }, { dataIndex: 'rocketIconUrl', key: 'rocketIconUrl', title: '火箭图标', width: 260 },
{ dataIndex: 'rocketAnimationUrl', key: 'rocketAnimationUrl', title: '发射动画', width: 260 }, {
{ dataIndex: 'progressBarUrl', key: 'progressBarUrl', title: '进度条资源', width: 260 }, dataIndex: 'rocketAnimationUrl',
key: 'rocketAnimationUrl',
title: '发射动画',
width: 260,
},
{ dataIndex: 'shakeThresholdPercent', key: 'shakeThresholdPercent', title: '晃动阈值(%)', width: 160 },
{ dataIndex: 'enabled', key: 'enabled', title: '启用', width: 90 }, { dataIndex: 'enabled', key: 'enabled', title: '启用', width: 90 },
{ dataIndex: 'sort', key: 'sort', title: '排序', width: 100 }, { dataIndex: 'sort', key: 'sort', title: '排序', width: 100 },
]; ];
@ -145,9 +177,9 @@ const levelColumns = [
const rewardColumns = [ const rewardColumns = [
{ dataIndex: 'rewardScene', key: 'rewardScene', title: '奖励场景', width: 150 }, { dataIndex: 'rewardScene', key: 'rewardScene', title: '奖励场景', width: 150 },
{ dataIndex: 'rewardType', key: 'rewardType', title: '奖励类型', width: 150 }, { dataIndex: 'rewardType', key: 'rewardType', title: '奖励类型', width: 150 },
{ dataIndex: 'rewardItemId', key: 'rewardItemId', title: '资源ID', width: 170 }, { dataIndex: 'rewardItemId', key: 'rewardItemId', title: '资源', width: 220 },
{ dataIndex: 'rewardName', key: 'rewardName', title: '奖励名称', width: 180 }, { dataIndex: 'rewardName', key: 'rewardName', title: '奖励名称', width: 180 },
{ dataIndex: 'rewardCover', key: 'rewardCover', title: '封面URL', width: 220 }, { dataIndex: 'rewardCover', key: 'rewardCover', title: '封面', width: 220 },
{ dataIndex: 'rewardAmount', key: 'rewardAmount', title: '数量/金币/天数', width: 150 }, { dataIndex: 'rewardAmount', key: 'rewardAmount', title: '数量/金币/天数', width: 150 },
{ dataIndex: 'expireDays', key: 'expireDays', title: '有效天数', width: 130 }, { dataIndex: 'expireDays', key: 'expireDays', title: '有效天数', width: 130 },
{ dataIndex: 'weight', key: 'weight', title: '权重', width: 110 }, { dataIndex: 'weight', key: 'weight', title: '权重', width: 110 },
@ -161,7 +193,6 @@ function createDefaultLevel(level: number): LevelRow {
enabled: true, enabled: true,
level, level,
needEnergy: level * 100, needEnergy: level * 100,
progressBarUrl: '',
rocketAnimationUrl: '', rocketAnimationUrl: '',
rocketIconUrl: '', rocketIconUrl: '',
shakeThresholdPercent: '95.0000', shakeThresholdPercent: '95.0000',
@ -246,7 +277,6 @@ function normalizeLevelRows(rows: Array<Record<string, any>> | undefined) {
enabled: row.enabled === undefined ? true : Boolean(row.enabled), enabled: row.enabled === undefined ? true : Boolean(row.enabled),
id: row.id, id: row.id,
needEnergy: toNumber(row.needEnergy, level * 100), needEnergy: toNumber(row.needEnergy, level * 100),
progressBarUrl: String(row.progressBarUrl || ''),
rocketAnimationUrl: String(row.rocketAnimationUrl || ''), rocketAnimationUrl: String(row.rocketAnimationUrl || ''),
rocketIconUrl: String(row.rocketIconUrl || ''), rocketIconUrl: String(row.rocketIconUrl || ''),
shakeThresholdPercent: String(row.shakeThresholdPercent || '95.0000'), shakeThresholdPercent: String(row.shakeThresholdPercent || '95.0000'),
@ -273,6 +303,183 @@ function normalizeRewardRows(rows: Array<Record<string, any>> | undefined) {
})); }));
} }
function normalizePropsResource(item: Record<string, any>, type: string): ResourceOption {
return {
amount: item.amount,
cover: String(item.cover || item.img || item.icon || ''),
id: item.id,
name: String(item.name || item.title || item.remark || item.id || ''),
remark: item.remark,
sourceUrl: String(item.sourceUrl || item.url || item.cover || ''),
type,
};
}
function normalizeGiftResource(item: Record<string, any>): ResourceOption {
return {
amount: item.giftCandy,
cover: String(item.giftPhoto || ''),
id: item.id,
name: String(item.giftName || item.id || ''),
sourceUrl: String(item.giftSourceUrl || item.giftPhoto || ''),
type: 'GIFT',
};
}
function normalizeBadgeResource(item: Record<string, any>): ResourceOption {
return {
cover: String(item.selectUrl || item.cover || ''),
id: item.badgeConfigId || item.id,
name: String(item.badgeName || item.name || item.id || ''),
sourceUrl: String(item.animationUrl || item.selectUrl || item.cover || ''),
type: 'BADGE',
};
}
function optionLabel(item: ResourceOption) {
return `${item.id} / ${item.name}`.trim();
}
function toResourceSelectOptions(rows: ResourceOption[]) {
return rows
.filter((item) => item.id !== undefined && item.id !== null && item.id !== '')
.map((item) => ({
label: optionLabel(item),
value: String(item.id),
}));
}
function toRocketResourceSelectOptions(
record: Record<string, any>,
key: 'rocketAnimationUrl' | 'rocketIconUrl',
) {
const options = rocketResourceRows.value
.map((item) => {
const value =
key === 'rocketIconUrl'
? String(item.cover || item.sourceUrl || '')
: String(item.sourceUrl || item.cover || '');
return {
label: optionLabel(item),
value,
};
})
.filter((item) => item.value);
const current = String(record[key] || '');
if (current && !options.some((item) => item.value === current)) {
options.unshift({ label: current, value: current });
}
return options;
}
async function loadRocketResources() {
if (!selectedSysOrigin.value || rocketResourceLoading.value) {
return;
}
rocketResourceLoading.value = true;
try {
const result = await listSysOriginTypeList(
selectedSysOrigin.value,
ROCKET_RESOURCE_TYPE,
);
rocketResourceRows.value = (result || []).map((item) =>
normalizePropsResource(item, ROCKET_RESOURCE_TYPE),
);
} finally {
rocketResourceLoading.value = false;
}
}
function rewardResourceType(rewardType: string) {
const value = String(rewardType || '').trim();
const mapping: Record<string, string> = {
AVATAR_FRAME: 'AVATAR_FRAME',
BADGE: 'BADGE',
CHAT_BUBBLE: 'CHAT_BUBBLE',
GIFT: 'GIFT',
MOUNT: 'RIDE',
VIP_CARD: 'NOBLE_VIP',
};
return mapping[value] || '';
}
function isRewardResourceDisabled(row: Record<string, any>) {
return row.rewardType === 'GOLD' || row.rewardType === 'NONE';
}
async function loadRewardResources(rewardType: string) {
const type = String(rewardType || '').trim();
const sourceType = rewardResourceType(type);
if (
!selectedSysOrigin.value ||
!sourceType ||
(rewardResourceRows[type] || []).length > 0
) {
return;
}
rewardResourceLoading[type] = true;
try {
if (type === 'GIFT') {
const result = await listGiftBySysOrigin(selectedSysOrigin.value);
rewardResourceRows[type] = (result || []).map(normalizeGiftResource);
return;
}
if (type === 'BADGE') {
const result = await listBadgePictureBySysOrigin(
selectedSysOrigin.value,
'ACTIVITY',
);
rewardResourceRows[type] = (result || []).map(normalizeBadgeResource);
return;
}
const loader =
type === 'VIP_CARD' ? listNotFamilyBySysOriginType : listSysOriginTypeList;
const result = await loader(selectedSysOrigin.value, sourceType);
rewardResourceRows[type] = (result || []).map((item) =>
normalizePropsResource(item, sourceType),
);
} finally {
rewardResourceLoading[type] = false;
}
}
function rewardResourceOptions(row: Record<string, any>) {
const rows = rewardResourceRows[row.rewardType] || [];
const options = toResourceSelectOptions(rows);
const current = String(row.rewardItemId || '').trim();
if (current && !options.some((item) => item.value === current)) {
options.unshift({
label: `${current} / ${row.rewardName || '已配置资源'}`,
value: current,
});
}
return options;
}
function handleRewardSceneChange(row: Record<string, any>) {
if (row.rewardScene !== 'IN_ROOM' && row.rewardType === 'NONE') {
row.rewardType = 'GOLD';
handleRewardTypeChange(row);
}
}
function handleRewardResourceChange(
row: Record<string, any>,
value?: null | number | string,
) {
const selected =
(rewardResourceRows[row.rewardType] || []).find(
(item) => String(item.id) === String(value),
) || null;
if (!selected) {
row.rewardItemId = value || null;
return;
}
row.rewardItemId = selected.id;
row.rewardName = selected.name;
row.rewardCover = selected.cover || selected.sourceUrl || '';
}
async function loadConfig() { async function loadConfig() {
if (!selectedSysOrigin.value) { if (!selectedSysOrigin.value) {
return; return;
@ -316,7 +523,7 @@ async function loadRewards() {
} }
async function loadAll() { async function loadAll() {
await Promise.all([loadConfig(), loadLevels(), loadRewards()]); await Promise.all([loadConfig(), loadLevels(), loadRewards(), loadRocketResources()]);
} }
async function saveConfig() { async function saveConfig() {
@ -361,7 +568,6 @@ async function saveLevels() {
id: row.id, id: row.id,
level: row.level, level: row.level,
needEnergy: toNumber(row.needEnergy, row.level * 100), needEnergy: toNumber(row.needEnergy, row.level * 100),
progressBarUrl: row.progressBarUrl,
rocketAnimationUrl: row.rocketAnimationUrl, rocketAnimationUrl: row.rocketAnimationUrl,
rocketIconUrl: row.rocketIconUrl, rocketIconUrl: row.rocketIconUrl,
shakeThresholdPercent: row.shakeThresholdPercent, shakeThresholdPercent: row.shakeThresholdPercent,
@ -376,18 +582,19 @@ async function saveLevels() {
} }
} }
function addRewardRow(scene = 'TOP1') { function addRewardRow(scene = 'TOP1', rewardType = 'GOLD') {
const isNone = rewardType === 'NONE';
rewardRows.value.push({ rewardRows.value.push({
enabled: true, enabled: true,
expireDays: null, expireDays: null,
level: rewardLevel.value, level: rewardLevel.value,
localKey: `new-${Date.now()}-${rewardRows.value.length}`, localKey: `new-${Date.now()}-${rewardRows.value.length}`,
rewardAmount: 1, rewardAmount: isNone ? 0 : 1,
rewardCover: '', rewardCover: '',
rewardItemId: null, rewardItemId: null,
rewardName: scene === 'TOP1' ? '金币' : '', rewardName: isNone ? '轮空' : rewardType === 'GOLD' ? '金币' : '',
rewardScene: scene, rewardScene: scene,
rewardType: 'GOLD', rewardType,
sort: rewardRows.value.length + 1, sort: rewardRows.value.length + 1,
weight: 1, weight: 1,
}); });
@ -398,13 +605,32 @@ function removeRewardRow(index: number) {
} }
function handleRewardTypeChange(row: Record<string, any>) { function handleRewardTypeChange(row: Record<string, any>) {
if (row.rewardType === 'NONE') {
if (row.rewardScene !== 'IN_ROOM') {
row.rewardScene = 'IN_ROOM';
}
row.rewardItemId = null;
row.rewardCover = '';
row.expireDays = null;
row.rewardAmount = 0;
row.rewardName = '轮空';
return;
}
if (row.rewardType === 'GOLD') { if (row.rewardType === 'GOLD') {
row.rewardItemId = null; row.rewardItemId = null;
row.rewardCover = '';
row.expireDays = null; row.expireDays = null;
if (!row.rewardName) { if (!row.rewardName) {
row.rewardName = '金币'; row.rewardName = '金币';
} }
return;
} }
row.rewardItemId = null;
row.rewardCover = '';
if (row.rewardName === '金币' || row.rewardName === '轮空') {
row.rewardName = '';
}
void loadRewardResources(row.rewardType);
} }
function validateRewards() { function validateRewards() {
@ -417,12 +643,20 @@ function validateRewards() {
message.warning('请选择奖励类型'); message.warning('请选择奖励类型');
return false; return false;
} }
if (row.rewardType === 'NONE' && row.rewardScene !== 'IN_ROOM') {
message.warning('轮空只能配置在房用户奖励');
return false;
}
if (!String(row.rewardName || '').trim()) { if (!String(row.rewardName || '').trim()) {
message.warning('请输入奖励名称'); message.warning('请输入奖励名称');
return false; return false;
} }
if (row.rewardType !== 'GOLD' && !String(row.rewardItemId || '').trim()) { if (
message.warning('非金币奖励必须填写资源ID'); row.rewardType !== 'GOLD' &&
row.rewardType !== 'NONE' &&
!String(row.rewardItemId || '').trim()
) {
message.warning('非金币奖励必须选择资源');
return false; return false;
} }
} }
@ -439,14 +673,20 @@ async function saveRewards() {
level: rewardLevel.value, level: rewardLevel.value,
rewards: rewardRows.value.map((row) => ({ rewards: rewardRows.value.map((row) => ({
enabled: row.enabled, enabled: row.enabled,
expireDays: toOptionalNumber(row.expireDays), expireDays:
row.rewardType === 'GOLD' || row.rewardType === 'NONE'
? undefined
: toOptionalNumber(row.expireDays),
id: row.id, id: row.id,
level: rewardLevel.value, level: rewardLevel.value,
rewardAmount: toNumber(row.rewardAmount, 1), rewardAmount:
rewardCover: row.rewardCover, row.rewardType === 'NONE' ? 0 : toNumber(row.rewardAmount, 1),
rewardCover: row.rewardType === 'NONE' ? '' : row.rewardCover,
rewardItemId: rewardItemId:
row.rewardType === 'GOLD' ? undefined : toOptionalString(row.rewardItemId), row.rewardType === 'GOLD' || row.rewardType === 'NONE'
rewardName: row.rewardName.trim(), ? undefined
: toOptionalString(row.rewardItemId),
rewardName: row.rewardType === 'NONE' ? '轮空' : row.rewardName.trim(),
rewardScene: row.rewardScene, rewardScene: row.rewardScene,
rewardType: row.rewardType, rewardType: row.rewardType,
sort: toNumber(row.sort, 1), sort: toNumber(row.sort, 1),
@ -474,6 +714,10 @@ watch(
watch( watch(
selectedSysOrigin, selectedSysOrigin,
() => { () => {
rocketResourceRows.value = [];
Object.keys(rewardResourceRows).forEach((key) => {
delete rewardResourceRows[key];
});
void loadAll(); void loadAll();
}, },
{ immediate: true }, { immediate: true },
@ -605,7 +849,7 @@ watch(rewardLevel, () => {
:data-source="levelRows" :data-source="levelRows"
:loading="levelLoading" :loading="levelLoading"
:pagination="false" :pagination="false"
:scroll="{ x: 1420 }" :scroll="{ x: 1240 }"
row-key="level" row-key="level"
> >
<template #bodyCell="{ column, record }"> <template #bodyCell="{ column, record }">
@ -620,24 +864,38 @@ watch(rewardLevel, () => {
class="table-number" class="table-number"
/> />
</template> </template>
<template v-else-if="column.key === 'rocketIconUrl'">
<Select
v-model:value="record.rocketIconUrl"
:loading="rocketResourceLoading"
:options="toRocketResourceSelectOptions(record, 'rocketIconUrl')"
allow-clear
class="table-resource-select"
option-filter-prop="label"
show-search
@focus="loadRocketResources"
/>
</template>
<template v-else-if="column.key === 'rocketAnimationUrl'">
<Select
v-model:value="record.rocketAnimationUrl"
:loading="rocketResourceLoading"
:options="
toRocketResourceSelectOptions(record, 'rocketAnimationUrl')
"
allow-clear
class="table-resource-select"
option-filter-prop="label"
show-search
@focus="loadRocketResources"
/>
</template>
<template v-else-if="column.key === 'shakeThresholdPercent'"> <template v-else-if="column.key === 'shakeThresholdPercent'">
<Input <Input
v-model:value="record.shakeThresholdPercent" v-model:value="record.shakeThresholdPercent"
class="table-input" class="table-input"
/> />
</template> </template>
<template v-else-if="column.key === 'rocketIconUrl'">
<Input v-model:value="record.rocketIconUrl" class="table-input" />
</template>
<template v-else-if="column.key === 'rocketAnimationUrl'">
<Input
v-model:value="record.rocketAnimationUrl"
class="table-input"
/>
</template>
<template v-else-if="column.key === 'progressBarUrl'">
<Input v-model:value="record.progressBarUrl" class="table-input" />
</template>
<template v-else-if="column.key === 'enabled'"> <template v-else-if="column.key === 'enabled'">
<Switch v-model:checked="record.enabled" /> <Switch v-model:checked="record.enabled" />
</template> </template>
@ -676,6 +934,7 @@ watch(rewardLevel, () => {
<Button @click="addRewardRow('TOP1')">添加贡献第一</Button> <Button @click="addRewardRow('TOP1')">添加贡献第一</Button>
<Button @click="addRewardRow('IGNITE')">添加点火人</Button> <Button @click="addRewardRow('IGNITE')">添加点火人</Button>
<Button @click="addRewardRow('IN_ROOM')">添加在房用户</Button> <Button @click="addRewardRow('IN_ROOM')">添加在房用户</Button>
<Button @click="addRewardRow('IN_ROOM', 'NONE')">添加在房轮空</Button>
<Button type="primary" :loading="rewardSaving" @click="saveRewards"> <Button type="primary" :loading="rewardSaving" @click="saveRewards">
保存当前等级奖励 保存当前等级奖励
</Button> </Button>
@ -695,33 +954,53 @@ watch(rewardLevel, () => {
v-model:value="record.rewardScene" v-model:value="record.rewardScene"
:options="sceneOptions" :options="sceneOptions"
class="table-select" class="table-select"
@change="() => handleRewardSceneChange(record)"
/> />
</template> </template>
<template v-else-if="column.key === 'rewardType'"> <template v-else-if="column.key === 'rewardType'">
<Select <Select
v-model:value="record.rewardType" v-model:value="record.rewardType"
:options="rewardTypeOptions" :options="rewardTypeOptionsForRow(record)"
class="table-select" class="table-select"
@change="() => handleRewardTypeChange(record)" @change="() => handleRewardTypeChange(record)"
/> />
</template> </template>
<template v-else-if="column.key === 'rewardItemId'"> <template v-else-if="column.key === 'rewardItemId'">
<Input <Select
v-model:value="record.rewardItemId" v-model:value="record.rewardItemId"
:disabled="record.rewardType === 'GOLD'" :disabled="isRewardResourceDisabled(record)"
class="table-input-small" :loading="rewardResourceLoading[record.rewardType]"
:options="rewardResourceOptions(record)"
allow-clear
class="table-resource-select"
option-filter-prop="label"
show-search
@change="(value) => handleRewardResourceChange(record, value)"
@focus="() => loadRewardResources(record.rewardType)"
/> />
</template> </template>
<template v-else-if="column.key === 'rewardName'"> <template v-else-if="column.key === 'rewardName'">
<Input v-model:value="record.rewardName" class="table-input-small" /> <Input
v-model:value="record.rewardName"
:disabled="record.rewardType === 'NONE'"
class="table-input-small"
/>
</template> </template>
<template v-else-if="column.key === 'rewardCover'"> <template v-else-if="column.key === 'rewardCover'">
<Input v-model:value="record.rewardCover" class="table-input" /> <div class="cover-preview">
<img
v-if="record.rewardCover"
:src="record.rewardCover"
alt=""
/>
<span>{{ record.rewardCover || '-' }}</span>
</div>
</template> </template>
<template v-else-if="column.key === 'rewardAmount'"> <template v-else-if="column.key === 'rewardAmount'">
<InputNumber <InputNumber
v-model:value="record.rewardAmount" v-model:value="record.rewardAmount"
:min="1" :disabled="record.rewardType === 'NONE'"
:min="record.rewardType === 'NONE' ? 0 : 1"
:precision="0" :precision="0"
class="table-number" class="table-number"
/> />
@ -729,7 +1008,9 @@ watch(rewardLevel, () => {
<template v-else-if="column.key === 'expireDays'"> <template v-else-if="column.key === 'expireDays'">
<InputNumber <InputNumber
v-model:value="record.expireDays" v-model:value="record.expireDays"
:disabled="record.rewardType === 'GOLD'" :disabled="
record.rewardType === 'GOLD' || record.rewardType === 'NONE'
"
:min="0" :min="0"
:precision="0" :precision="0"
class="table-number" class="table-number"
@ -840,7 +1121,34 @@ watch(rewardLevel, () => {
width: 140px; width: 140px;
} }
.table-resource-select {
width: 220px;
}
.table-number { .table-number {
width: 110px; width: 110px;
} }
.cover-preview {
display: flex;
align-items: center;
max-width: 220px;
gap: 8px;
color: #6b7280;
font-size: 12px;
}
.cover-preview img {
width: 32px;
height: 32px;
flex: 0 0 auto;
border-radius: 4px;
object-fit: cover;
}
.cover-preview span {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style> </style>

View File

@ -0,0 +1 @@
4934