684 lines
21 KiB
Vue
684 lines
21 KiB
Vue
<script lang="ts" setup>
|
||
import { computed, reactive, ref, watch } from 'vue';
|
||
|
||
import { Page } from '@vben/common-ui';
|
||
import { useAccessStore } from '@vben/stores';
|
||
|
||
import {
|
||
Button,
|
||
Card,
|
||
Input,
|
||
InputNumber,
|
||
message,
|
||
Modal,
|
||
Select,
|
||
Space,
|
||
Spin,
|
||
Switch,
|
||
Table,
|
||
TabPane,
|
||
Tabs,
|
||
Tag,
|
||
} from 'antdv-next';
|
||
|
||
import {
|
||
getResidentFirstRechargeRewardConfig,
|
||
pageResidentFirstRechargeRewardGrantRecords,
|
||
saveResidentFirstRechargeRewardConfig,
|
||
} from '#/api/legacy/resident-activity';
|
||
import ActivityResourceGroupSelectDrawer from '#/views/props/components/activity-resource-group-select-drawer.vue';
|
||
import RewardRow from '#/views/props/components/reward-row.vue';
|
||
import { getAllowedSysOrigins } from '#/views/system/shared';
|
||
|
||
defineOptions({ name: 'ResidentFirstRechargeRewardConfigPage' });
|
||
|
||
let localRowId = 0;
|
||
|
||
function createRowKey(seed?: number | string) {
|
||
localRowId += 1;
|
||
return `first-recharge-reward-${seed || localRowId}-${localRowId}`;
|
||
}
|
||
|
||
function createLevel(level = 1, rechargeAmount = 10) {
|
||
return {
|
||
enabled: true,
|
||
googleProductId: '',
|
||
id: null,
|
||
level,
|
||
rechargeAmount,
|
||
rechargeAmountCents: 0,
|
||
rewardGroupId: null,
|
||
rewardGroupName: '',
|
||
rewardItems: [] as Array<Record<string, any>>,
|
||
rowKey: createRowKey(),
|
||
};
|
||
}
|
||
|
||
function normalizeID(value: unknown) {
|
||
const text = String(value ?? '').trim();
|
||
return text && text !== '0' ? text : '';
|
||
}
|
||
|
||
function hasPositiveID(value: unknown) {
|
||
return /^\d+$/.test(normalizeID(value));
|
||
}
|
||
|
||
function defaultLevels() {
|
||
return [createLevel(1, 10), createLevel(2, 50), createLevel(3, 100)];
|
||
}
|
||
|
||
const accessStore = useAccessStore();
|
||
const sysOriginOptions = computed(() => {
|
||
const options = getAllowedSysOrigins(accessStore.accessCodes || []);
|
||
return options.length > 0 ? options : getAllowedSysOrigins([]);
|
||
});
|
||
|
||
const selectedSysOrigin = ref('');
|
||
const activeTab = ref('config');
|
||
const loading = ref(false);
|
||
const recordLoading = ref(false);
|
||
const saving = ref(false);
|
||
const groupPickerOpen = ref(false);
|
||
const pickerLevelIndex = ref(-1);
|
||
|
||
const form = reactive<Record<string, any>>({
|
||
configured: false,
|
||
enabled: false,
|
||
id: 0,
|
||
levelConfigs: defaultLevels(),
|
||
minAppVersion: '',
|
||
sysOrigin: '',
|
||
updateTime: '',
|
||
});
|
||
|
||
const levelColumns = [
|
||
{ dataIndex: 'enabled', key: 'enabled', title: '启用', width: 90 },
|
||
{ dataIndex: 'level', key: 'level', title: '档位', width: 110 },
|
||
{ dataIndex: 'rechargeAmount', key: 'rechargeAmount', title: '首冲金额', width: 180 },
|
||
{ dataIndex: 'googleProductId', key: 'googleProductId', title: '谷歌商品ID', width: 240 },
|
||
{ dataIndex: 'rewardGroup', key: 'rewardGroup', title: '奖励资源组', width: 420 },
|
||
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 100 },
|
||
];
|
||
|
||
const enabledLevelCount = computed(() =>
|
||
(form.levelConfigs || []).filter((item: Record<string, any>) => item.enabled !== false).length,
|
||
);
|
||
|
||
const statusMeta = computed(() => {
|
||
if (!form.configured) {
|
||
return { color: 'processing', text: '未配置' };
|
||
}
|
||
if (form.enabled) {
|
||
return { color: 'success', text: '已启用' };
|
||
}
|
||
return { color: 'default', text: '已关闭' };
|
||
});
|
||
|
||
const recordQuery = reactive<Record<string, any>>({
|
||
cursor: 1,
|
||
limit: 20,
|
||
status: '',
|
||
userId: '',
|
||
});
|
||
|
||
const recordPage = reactive<Record<string, any>>({
|
||
current: 1,
|
||
records: [],
|
||
size: 20,
|
||
total: 0,
|
||
});
|
||
|
||
const statusOptions = [
|
||
{ label: '全部状态', value: '' },
|
||
{ label: '发放中', value: 'PENDING' },
|
||
{ label: '已发放', value: 'SUCCESS' },
|
||
{ label: '发放失败', value: 'FAILED' },
|
||
];
|
||
|
||
const recordColumns = [
|
||
{ dataIndex: 'user', key: 'user', title: '用户', width: 260 },
|
||
{ dataIndex: 'rechargeAmount', key: 'rechargeAmount', title: '充值金额', width: 120 },
|
||
{ dataIndex: 'rechargeThreshold', key: 'rechargeThreshold', title: '达标金额', width: 120 },
|
||
{ dataIndex: 'level', key: 'level', title: '档位', width: 90 },
|
||
{ dataIndex: 'payPlatform', key: 'payPlatform', title: '充值方式', width: 180 },
|
||
{ dataIndex: 'sourceOrderId', key: 'sourceOrderId', title: '订单', width: 180 },
|
||
{ dataIndex: 'rewardGroup', key: 'rewardGroup', title: '奖励资源组', width: 360 },
|
||
{ dataIndex: 'status', key: 'status', title: '发放状态', width: 120 },
|
||
{ dataIndex: 'sentAt', key: 'sentAt', title: '发放时间', width: 180 },
|
||
{ dataIndex: 'lastError', key: 'lastError', title: '失败原因', width: 220 },
|
||
];
|
||
|
||
function toAmountCents(value: number | string | undefined) {
|
||
const numeric = Number(value || 0);
|
||
if (!Number.isFinite(numeric)) {
|
||
return 0;
|
||
}
|
||
return Math.round(numeric * 100);
|
||
}
|
||
|
||
function formatAmount(value: number | string | undefined) {
|
||
const numeric = Number(value || 0);
|
||
return Number.isFinite(numeric) ? numeric.toFixed(2) : '0.00';
|
||
}
|
||
|
||
function normalizeConfig(result: null | Record<string, any> | undefined) {
|
||
const value = result || {};
|
||
form.configured = Boolean(value.configured);
|
||
form.enabled = Boolean(value.enabled);
|
||
form.id = value.id || 0;
|
||
form.minAppVersion = String(value.minAppVersion || '');
|
||
form.sysOrigin = String(value.sysOrigin || selectedSysOrigin.value || '');
|
||
form.updateTime = String(value.updateTime || '');
|
||
const levels = Array.isArray(value.levelConfigs) && value.levelConfigs.length > 0
|
||
? value.levelConfigs
|
||
: defaultLevels();
|
||
form.levelConfigs = levels
|
||
.map((item: Record<string, any>, index: number) => {
|
||
const rechargeAmount = Number(
|
||
item.rechargeAmount ?? Number(item.rechargeAmountCents || 0) / 100,
|
||
);
|
||
return {
|
||
enabled: item.enabled !== false,
|
||
googleProductId: String(item.googleProductId || ''),
|
||
id: item.id ?? null,
|
||
level: Number(item.level || index + 1),
|
||
rechargeAmount: Number.isFinite(rechargeAmount) ? rechargeAmount : 0,
|
||
rechargeAmountCents: Number(item.rechargeAmountCents || 0),
|
||
rewardGroupId: normalizeID(item.rewardGroupId) || null,
|
||
rewardGroupName: String(item.rewardGroupName || ''),
|
||
rewardItems: Array.isArray(item.rewardItems) ? item.rewardItems : [],
|
||
rowKey: createRowKey(item.id || index),
|
||
};
|
||
})
|
||
.toSorted((left: Record<string, any>, right: Record<string, any>) =>
|
||
toAmountCents(left.rechargeAmount) - toAmountCents(right.rechargeAmount),
|
||
);
|
||
}
|
||
|
||
async function loadConfig() {
|
||
if (!selectedSysOrigin.value) {
|
||
return;
|
||
}
|
||
loading.value = true;
|
||
try {
|
||
const result = await getResidentFirstRechargeRewardConfig(selectedSysOrigin.value);
|
||
normalizeConfig(result);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
function normalizeRecordPage(result: null | Record<string, any> | undefined) {
|
||
const value = result || {};
|
||
recordPage.records = Array.isArray(value.records) ? value.records : [];
|
||
recordPage.total = Number(value.total || 0);
|
||
recordPage.current = Number(value.current || 1);
|
||
recordPage.size = Number(value.size || 20);
|
||
}
|
||
|
||
async function loadGrantRecords() {
|
||
if (!selectedSysOrigin.value) {
|
||
return;
|
||
}
|
||
recordLoading.value = true;
|
||
try {
|
||
const result = await pageResidentFirstRechargeRewardGrantRecords({
|
||
...recordQuery,
|
||
sysOrigin: selectedSysOrigin.value,
|
||
});
|
||
normalizeRecordPage(result);
|
||
} finally {
|
||
recordLoading.value = false;
|
||
}
|
||
}
|
||
|
||
function addLevel() {
|
||
const levels = form.levelConfigs as Array<Record<string, any>>;
|
||
const maxLevel = Math.max(0, ...levels.map((item) => Number(item.level || 0)));
|
||
const maxAmount = Math.max(0, ...levels.map((item) => Number(item.rechargeAmount || 0)));
|
||
levels.push(createLevel(maxLevel + 1, maxAmount + 10));
|
||
}
|
||
|
||
function removeLevel(index: number) {
|
||
(form.levelConfigs as Array<Record<string, any>>).splice(index, 1);
|
||
}
|
||
|
||
function confirmRemoveLevel(index: number) {
|
||
Modal.confirm({
|
||
onOk: () => removeLevel(index),
|
||
title: '确认删除该首冲奖励档位?',
|
||
});
|
||
}
|
||
|
||
function openRewardPicker(index: number) {
|
||
pickerLevelIndex.value = index;
|
||
groupPickerOpen.value = true;
|
||
}
|
||
|
||
function clearRewardGroup(record: Record<string, any>) {
|
||
record.rewardGroupId = null;
|
||
record.rewardGroupName = '';
|
||
record.rewardItems = [];
|
||
}
|
||
|
||
function handleRewardGroupSelect(row: Record<string, any>) {
|
||
const levels = form.levelConfigs as Array<Record<string, any>>;
|
||
const target = levels[pickerLevelIndex.value];
|
||
if (!target) {
|
||
groupPickerOpen.value = false;
|
||
return;
|
||
}
|
||
target.rewardGroupId = normalizeID(row.id) || null;
|
||
target.rewardGroupName = String(row.name || '');
|
||
target.rewardItems = Array.isArray(row.rewardConfigList) ? row.rewardConfigList : [];
|
||
groupPickerOpen.value = false;
|
||
}
|
||
|
||
function validateBeforeSave() {
|
||
const levels = form.levelConfigs as Array<Record<string, any>>;
|
||
if (levels.length === 0) {
|
||
message.warning('请至少配置一个首冲奖励档位');
|
||
return false;
|
||
}
|
||
const seenLevels = new Set<string>();
|
||
const seenAmounts = new Set<string>();
|
||
const seenGoogleProductIds = new Set<string>();
|
||
for (const item of levels) {
|
||
const level = Number(item.level || 0);
|
||
const amountCents = toAmountCents(item.rechargeAmount);
|
||
if (level <= 0) {
|
||
message.warning('档位必须大于 0');
|
||
return false;
|
||
}
|
||
if (amountCents <= 0) {
|
||
message.warning('首冲金额必须大于 0');
|
||
return false;
|
||
}
|
||
if (!hasPositiveID(item.rewardGroupId)) {
|
||
message.warning('每个档位都需要配置奖励资源组');
|
||
return false;
|
||
}
|
||
if (seenLevels.has(String(level))) {
|
||
message.warning('档位不能重复');
|
||
return false;
|
||
}
|
||
if (seenAmounts.has(String(amountCents))) {
|
||
message.warning('首冲金额不能重复');
|
||
return false;
|
||
}
|
||
const googleProductId = String(item.googleProductId || '').trim();
|
||
if (googleProductId) {
|
||
if (seenGoogleProductIds.has(googleProductId)) {
|
||
message.warning('谷歌商品ID不能重复');
|
||
return false;
|
||
}
|
||
seenGoogleProductIds.add(googleProductId);
|
||
}
|
||
seenLevels.add(String(level));
|
||
seenAmounts.add(String(amountCents));
|
||
}
|
||
if (form.enabled && enabledLevelCount.value === 0) {
|
||
message.warning('启用配置时至少要启用一个首冲奖励档位');
|
||
return false;
|
||
}
|
||
if (form.enabled && !String(form.minAppVersion || '').trim()) {
|
||
message.warning('启用配置时需要填写最低App版本');
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
async function submitForm() {
|
||
if (!validateBeforeSave()) {
|
||
return;
|
||
}
|
||
saving.value = true;
|
||
try {
|
||
await saveResidentFirstRechargeRewardConfig({
|
||
enabled: Boolean(form.enabled),
|
||
id: form.id || 0,
|
||
levelConfigs: (form.levelConfigs || []).map((item: Record<string, any>) => ({
|
||
enabled: item.enabled !== false,
|
||
googleProductId: String(item.googleProductId || '').trim(),
|
||
id: item.id || 0,
|
||
level: Number(item.level || 0),
|
||
rechargeAmount: formatAmount(item.rechargeAmount),
|
||
rechargeAmountCents: toAmountCents(item.rechargeAmount),
|
||
rewardGroupId: normalizeID(item.rewardGroupId) || null,
|
||
rewardGroupName: String(item.rewardGroupName || ''),
|
||
})),
|
||
minAppVersion: String(form.minAppVersion || '').trim(),
|
||
sysOrigin: selectedSysOrigin.value,
|
||
});
|
||
message.success('保存成功');
|
||
await loadConfig();
|
||
if (activeTab.value === 'records') {
|
||
await loadGrantRecords();
|
||
}
|
||
} finally {
|
||
saving.value = false;
|
||
}
|
||
}
|
||
|
||
function searchGrantRecords() {
|
||
recordQuery.cursor = 1;
|
||
void loadGrantRecords();
|
||
}
|
||
|
||
function handleGrantTableChange(pagination: Record<string, any>) {
|
||
recordQuery.cursor = Number(pagination.current || 1);
|
||
recordQuery.limit = Number(pagination.pageSize || 20);
|
||
void loadGrantRecords();
|
||
}
|
||
|
||
function avatarBackgroundStyle(url: string) {
|
||
return {
|
||
backgroundImage: `url("${String(url || '').replaceAll('"', '%22')}")`,
|
||
};
|
||
}
|
||
|
||
function grantStatusMeta(status: string) {
|
||
if (status === 'SUCCESS') {
|
||
return { color: 'success', text: '已发放' };
|
||
}
|
||
if (status === 'FAILED') {
|
||
return { color: 'error', text: '发放失败' };
|
||
}
|
||
return { color: 'processing', text: '发放中' };
|
||
}
|
||
|
||
watch(
|
||
sysOriginOptions,
|
||
(options) => {
|
||
if (!selectedSysOrigin.value) {
|
||
selectedSysOrigin.value = String(options[0]?.value || '');
|
||
}
|
||
},
|
||
{ immediate: true },
|
||
);
|
||
|
||
watch(
|
||
selectedSysOrigin,
|
||
(value) => {
|
||
if (value) {
|
||
recordQuery.cursor = 1;
|
||
void loadConfig();
|
||
void loadGrantRecords();
|
||
}
|
||
},
|
||
{ immediate: true },
|
||
);
|
||
|
||
watch(activeTab, (value) => {
|
||
if (value === 'records') {
|
||
void loadGrantRecords();
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<Page auto-content-height>
|
||
<Spin :spinning="loading">
|
||
<Card :bordered="false" title="首冲奖励">
|
||
<div class="toolbar">
|
||
<Select
|
||
v-model:value="selectedSysOrigin"
|
||
:options="sysOriginOptions"
|
||
option-filter-prop="label"
|
||
option-label-prop="label"
|
||
placeholder="请选择系统"
|
||
show-search
|
||
style="width: 180px"
|
||
/>
|
||
<Tag :color="statusMeta.color">{{ statusMeta.text }}</Tag>
|
||
<Tag color="processing">仅 Google / 三方充值</Tag>
|
||
<Button :loading="loading" @click="loadConfig">刷新</Button>
|
||
</div>
|
||
|
||
<Tabs v-model:active-key="activeTab">
|
||
<TabPane key="config" tab="奖励配置">
|
||
<div class="config-row">
|
||
<Space align="center" wrap>
|
||
<span class="field-label">启用</span>
|
||
<Switch v-model:checked="form.enabled" />
|
||
<span class="field-label">最低App版本</span>
|
||
<Input
|
||
v-model:value="form.minAppVersion"
|
||
allow-clear
|
||
placeholder="例如 1.5.0"
|
||
style="width: 160px"
|
||
/>
|
||
<span class="sub-text">更新时间:{{ form.updateTime || '-' }}</span>
|
||
</Space>
|
||
</div>
|
||
|
||
<Table
|
||
:columns="levelColumns"
|
||
:data-source="form.levelConfigs"
|
||
:loading="loading"
|
||
:pagination="false"
|
||
row-key="rowKey"
|
||
:scroll="{ x: 1140 }"
|
||
>
|
||
<template #bodyCell="{ column, index, record }">
|
||
<template v-if="column.key === 'enabled'">
|
||
<Switch v-model:checked="record.enabled" />
|
||
</template>
|
||
<template v-else-if="column.key === 'level'">
|
||
<InputNumber
|
||
v-model:value="record.level"
|
||
:min="1"
|
||
:precision="0"
|
||
style="width: 90px"
|
||
/>
|
||
</template>
|
||
<template v-else-if="column.key === 'rechargeAmount'">
|
||
<InputNumber
|
||
v-model:value="record.rechargeAmount"
|
||
:min="0.01"
|
||
:precision="2"
|
||
style="width: 140px"
|
||
/>
|
||
</template>
|
||
<template v-else-if="column.key === 'googleProductId'">
|
||
<Input
|
||
v-model:value="record.googleProductId"
|
||
allow-clear
|
||
placeholder="Google商品ID"
|
||
style="width: 200px"
|
||
/>
|
||
</template>
|
||
<template v-else-if="column.key === 'rewardGroup'">
|
||
<div class="reward-group-cell">
|
||
<Space wrap>
|
||
<Button size="small" @click="openRewardPicker(index)">
|
||
{{ record.rewardGroupId ? '更换奖励组' : '选择奖励组' }}
|
||
</Button>
|
||
<Button
|
||
v-if="record.rewardGroupId"
|
||
danger
|
||
size="small"
|
||
type="link"
|
||
@click="clearRewardGroup(record)"
|
||
>
|
||
清空
|
||
</Button>
|
||
</Space>
|
||
<div v-if="record.rewardGroupId" class="reward-group-name">
|
||
{{ record.rewardGroupName || '-' }} (ID: {{ record.rewardGroupId }})
|
||
</div>
|
||
<div v-else class="sub-text">未选择奖励组</div>
|
||
<RewardRow v-if="record.rewardItems?.length" :list="record.rewardItems" />
|
||
</div>
|
||
</template>
|
||
<template v-else-if="column.key === 'actions'">
|
||
<Button danger type="link" @click="confirmRemoveLevel(index)">删除</Button>
|
||
</template>
|
||
</template>
|
||
</Table>
|
||
|
||
<div class="actions">
|
||
<Button @click="addLevel">新增档位</Button>
|
||
<Button :loading="saving" type="primary" @click="submitForm">保存配置</Button>
|
||
</div>
|
||
</TabPane>
|
||
|
||
<TabPane key="records" tab="发奖记录">
|
||
<div class="toolbar">
|
||
<Input
|
||
v-model:value="recordQuery.userId"
|
||
allow-clear
|
||
placeholder="用户长ID"
|
||
style="width: 180px"
|
||
/>
|
||
<Select
|
||
v-model:value="recordQuery.status"
|
||
:options="statusOptions"
|
||
style="width: 140px"
|
||
/>
|
||
<Button :loading="recordLoading" type="primary" @click="searchGrantRecords">
|
||
查询
|
||
</Button>
|
||
</div>
|
||
<Table
|
||
:columns="recordColumns"
|
||
:data-source="recordPage.records"
|
||
:loading="recordLoading"
|
||
:pagination="{
|
||
current: recordPage.current,
|
||
pageSize: recordPage.size,
|
||
showSizeChanger: true,
|
||
total: recordPage.total,
|
||
}"
|
||
row-key="id"
|
||
:scroll="{ x: 1810 }"
|
||
@change="handleGrantTableChange"
|
||
>
|
||
<template #bodyCell="{ column, record }">
|
||
<template v-if="column.key === 'user'">
|
||
<div class="user-cell">
|
||
<div
|
||
v-if="record.userAvatar"
|
||
class="user-avatar"
|
||
:style="avatarBackgroundStyle(record.userAvatar)"
|
||
></div>
|
||
<div class="user-main">
|
||
<div class="user-name">{{ record.userNickname || '-' }}</div>
|
||
<div class="sub-text">
|
||
{{ record.account || record.userId || '-' }}
|
||
<span v-if="record.countryCode"> · {{ record.countryCode }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-else-if="column.key === 'payPlatform'">
|
||
<div>{{ record.payPlatform || '-' }}</div>
|
||
<div class="sub-text">{{ record.paymentMethod || '-' }}</div>
|
||
</template>
|
||
<template v-else-if="column.key === 'rewardGroup'">
|
||
<div class="reward-group-name">
|
||
{{ record.rewardGroupName || '-' }} (ID: {{ record.rewardGroupId || '-' }})
|
||
</div>
|
||
<RewardRow v-if="record.rewardItems?.length" :list="record.rewardItems" />
|
||
</template>
|
||
<template v-else-if="column.key === 'status'">
|
||
<Tag :color="grantStatusMeta(record.status).color">
|
||
{{ grantStatusMeta(record.status).text }}
|
||
</Tag>
|
||
</template>
|
||
<template v-else-if="column.key === 'sentAt'">
|
||
{{ record.sentAt || '-' }}
|
||
</template>
|
||
<template v-else-if="column.key === 'lastError'">
|
||
<span class="error-text">{{ record.lastError || '-' }}</span>
|
||
</template>
|
||
</template>
|
||
</Table>
|
||
</TabPane>
|
||
</Tabs>
|
||
</Card>
|
||
|
||
<ActivityResourceGroupSelectDrawer
|
||
:open="groupPickerOpen"
|
||
:sys-origin="selectedSysOrigin"
|
||
@close="groupPickerOpen = false"
|
||
@select="handleRewardGroupSelect"
|
||
/>
|
||
</Spin>
|
||
</Page>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.toolbar {
|
||
align-items: center;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.config-row {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.field-label {
|
||
color: rgb(71 85 105);
|
||
font-size: 13px;
|
||
}
|
||
|
||
.sub-text {
|
||
color: rgb(100 116 139);
|
||
font-size: 12px;
|
||
}
|
||
|
||
.reward-group-cell {
|
||
min-width: 0;
|
||
}
|
||
|
||
.reward-group-name {
|
||
color: rgb(15 23 42);
|
||
font-size: 13px;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.user-cell {
|
||
align-items: center;
|
||
display: flex;
|
||
gap: 10px;
|
||
min-width: 0;
|
||
}
|
||
|
||
.user-avatar {
|
||
background-color: rgb(226 232 240);
|
||
background-position: center;
|
||
background-size: cover;
|
||
border-radius: 50%;
|
||
flex: 0 0 auto;
|
||
height: 36px;
|
||
width: 36px;
|
||
}
|
||
|
||
.user-main {
|
||
min-width: 0;
|
||
}
|
||
|
||
.user-name {
|
||
color: rgb(15 23 42);
|
||
font-size: 13px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.error-text {
|
||
color: rgb(185 28 28);
|
||
}
|
||
|
||
.actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
justify-content: flex-end;
|
||
margin-top: 16px;
|
||
}
|
||
</style>
|