feat(user): 支持后台调整等级并限制极品专区可见性

This commit is contained in:
yml2213
2026-11-05 12:02:48 +08:00
parent 09f244a93d
commit 0d558bdce0
35 changed files with 934 additions and 131 deletions
@@ -5,6 +5,7 @@ import { Search } from '@element-plus/icons-vue'
import { computed, reactive, ref } from 'vue'
import {
adjustAdminUserGrowthLevel,
adjustAdminUserGrowthPoints,
adjustAdminUserWallet,
fetchAdminUsers,
@@ -17,6 +18,7 @@ import {
type AdminUserItem,
type AdminUserQuery,
} from '@/features/admin/api/adminUsers'
import { defaultRenterGrowthConfig } from '@/features/admin/api/renterGrowth'
import { centToYuan, formatCent, yuanToCent } from '@/shared/utils/money'
import { useAdminPaginatedTable } from '@/features/admin/composables/useAdminPaginatedTable'
import { realnameStatusLabel, userStatusLabel } from '@/shared/utils/statusLabels'
@@ -28,6 +30,7 @@ import AdminUserDetailDrawer from '../components/AdminUserDetailDrawer.vue'
const adminSession = useAdminSessionStore()
const canAdjustWallet = computed(() => adminSession.hasPermission('user:wallet_adjust'))
const canAdjustGrowthPoints = computed(() => adminSession.hasPermission('user:growth_points'))
const canAdjustGrowthLevel = computed(() => adminSession.hasPermission('user:growth_level'))
const canSetQualityOwner = computed(() => adminSession.hasPermission('user:quality_owner'))
const canViewUserDetail = computed(
() =>
@@ -44,6 +47,7 @@ const realnameUser = ref<AdminUserItem | null>(null)
const manualRealnameUser = ref<AdminUserItem | null>(null)
const walletUser = ref<AdminUserItem | null>(null)
const growthUser = ref<AdminUserItem | null>(null)
const growthLevelUser = ref<AdminUserItem | null>(null)
const freezeReason = ref('')
const revokeRealnameReason = ref('')
const quotaAmount = ref(0)
@@ -51,6 +55,11 @@ const growthForm = reactive({
targetPoints: 0,
reason: '',
})
const growthLevelForm = reactive({
targetLevel: '',
reason: '',
})
const growthLevelOptions = defaultRenterGrowthConfig.levels
const manualRealnameForm = reactive({
name: '',
idNo: '',
@@ -114,6 +123,12 @@ function openGrowthPoints(row: AdminUserItem) {
growthForm.reason = ''
}
function openGrowthLevel(row: AdminUserItem) {
growthLevelUser.value = row
growthLevelForm.targetLevel = row.renter_growth_level_override || ''
growthLevelForm.reason = ''
}
function openWalletAdjust(row: AdminUserItem) {
walletUser.value = row
walletForm.direction = 'out'
@@ -182,6 +197,49 @@ async function handleAdjustGrowthPoints() {
}
}
async function handleAdjustGrowthLevel() {
if (!growthLevelUser.value) return
const targetLevel = growthLevelForm.targetLevel.trim()
if (targetLevel === (growthLevelUser.value.renter_growth_level_override || '')) {
ElMessage.warning('目标等级与当前手动等级相同')
return
}
if (growthLevelForm.reason.trim().length < 2) {
ElMessage.warning('请填写至少 2 个字的调整原因')
return
}
const targetName = targetLevel
? growthLevelOptions.find(level => level.code === targetLevel)?.name || targetLevel
: '按积分自动计算'
try {
await ElMessageBox.confirm(
`确认将用户 ${growthLevelUser.value.phone || growthLevelUser.value.id} 的成长等级调整为「${targetName}」?\n手动设置后会锁定等级,后续积分变动不会自动覆盖。`,
'确认调整等级',
{
type: 'warning',
confirmButtonText: '确认调整',
cancelButtonText: '取消',
}
)
} catch {
return
}
submitting.value = true
try {
await adjustAdminUserGrowthLevel(growthLevelUser.value.id, {
target_level: targetLevel,
reason: growthLevelForm.reason.trim(),
})
ElMessage.success('成长等级已更新')
growthLevelUser.value = null
await loadUsers()
} catch (error) {
ElMessage.error(readError(error, '调整成长等级失败'))
} finally {
submitting.value = false
}
}
async function handleFreeze() {
if (!activeUser.value) return
submitting.value = true
@@ -435,9 +493,19 @@ function moneyCent(value: number | string | undefined) {
</el-table-column>
<el-table-column label="成长等级" width="100">
<template #default="{ row }">
<el-tag type="success" effect="light">{{
row.renter_growth_level_name || '普通'
}}</el-tag>
<div class="growth-level-cell">
<el-tag type="success" effect="light">{{
row.renter_growth_level_name || '普通'
}}</el-tag>
<el-tag
v-if="row.renter_growth_level_manual"
type="warning"
effect="plain"
size="small"
>
手动
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="成长积分" width="100" align="right">
@@ -475,7 +543,7 @@ function moneyCent(value: number | string | undefined) {
<el-table-column label="注册时间" min-width="160">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</el-table-column>
<el-table-column label="操作" width="470" fixed="right" align="right">
<el-table-column label="操作" width="520" fixed="right" align="right">
<template #default="{ row }">
<div class="table-actions">
<el-button
@@ -505,6 +573,15 @@ function moneyCent(value: number | string | undefined) {
>
积分
</el-button>
<el-button
v-if="canAdjustGrowthLevel"
text
type="primary"
size="small"
@click="openGrowthLevel(row)"
>
等级
</el-button>
<el-button text type="primary" size="small" @click="openDepositQuota(row)">
人工免押
</el-button>
@@ -752,6 +829,60 @@ function moneyCent(value: number | string | undefined) {
</template>
</el-dialog>
<el-dialog
:model-value="!!growthLevelUser"
title="调整成长等级"
width="540px"
@update:model-value="growthLevelUser = null"
>
<div v-if="growthLevelUser" class="dialog-body growth-dialog">
<p>
<strong>{{ growthLevelUser.phone }}</strong> · {{ growthLevelUser.nickname || '-' }} ·
当前
{{ growthLevelUser.renter_growth_level_name }}
{{ growthLevelUser.renter_growth_level_manual ? '(手动锁定)' : '(自动计算)' }}
</p>
<div class="growth-current">
<span>当前成长积分</span>
<strong>{{ growthLevelUser.renter_growth_points }}</strong>
</div>
<el-form label-position="top" @submit.prevent>
<el-form-item label="目标等级">
<el-select v-model="growthLevelForm.targetLevel" class="full-control">
<el-option label="恢复按积分自动计算" value="" />
<el-option
v-for="level in growthLevelOptions"
:key="level.code"
:label="level.name"
:value="level.code"
/>
</el-select>
</el-form-item>
<el-alert
type="warning"
:closable="false"
title="手动等级会锁定,后续积分变动不会自动覆盖;选择“恢复按积分自动计算”可解除锁定。操作会写入审计日志。"
/>
<el-form-item label="调整原因(必填)" class="level-reason-item">
<el-input
v-model="growthLevelForm.reason"
type="textarea"
:rows="3"
maxlength="200"
show-word-limit
placeholder="例如:特殊用户授权、客服补偿或修正异常等级"
/>
</el-form-item>
</el-form>
</div>
<template #footer>
<el-button :disabled="submitting" @click="growthLevelUser = null">取消</el-button>
<el-button type="primary" :loading="submitting" @click="handleAdjustGrowthLevel">
确认调整
</el-button>
</template>
</el-dialog>
<el-dialog
:model-value="!!walletUser"
title="余额管理"