222 lines
6.5 KiB
Vue
222 lines
6.5 KiB
Vue
<script setup lang="ts">
|
|
import { ElMessage } from 'element-plus'
|
|
import { ref, watch } from 'vue'
|
|
|
|
import { updateSystemConfig, type SystemConfig } from '@/features/admin/api/systemConfigs'
|
|
import {
|
|
cloneRenterGrowthConfig,
|
|
defaultRenterGrowthConfig,
|
|
parseRenterGrowthConfig,
|
|
type RenterGrowthConfig,
|
|
type RenterGrowthLevelRule,
|
|
} from '@/features/admin/api/renterGrowth'
|
|
import { readError } from '@/shared/utils/error'
|
|
|
|
const props = defineProps<{
|
|
modelValue: boolean
|
|
config: SystemConfig
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(event: 'update:modelValue', value: boolean): void
|
|
(event: 'saved'): void
|
|
}>()
|
|
|
|
const submitting = ref(false)
|
|
const draft = ref<RenterGrowthConfig>(cloneRenterGrowthConfig(defaultRenterGrowthConfig))
|
|
|
|
watch(
|
|
() => [props.modelValue, props.config.value] as const,
|
|
([visible]) => {
|
|
if (visible) draft.value = parseRenterGrowthConfig(props.config.value)
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
function discountPercent(level: RenterGrowthLevelRule) {
|
|
return level.discount_bps / 100
|
|
}
|
|
|
|
function updateDiscountPercent(level: RenterGrowthLevelRule, value: number | undefined) {
|
|
level.discount_bps = Math.round(Number(value || 0) * 100)
|
|
}
|
|
|
|
function quotaYuan(level: RenterGrowthLevelRule) {
|
|
return level.deposit_free_quota_cent / 100
|
|
}
|
|
|
|
function updateQuotaYuan(level: RenterGrowthLevelRule, value: number | undefined) {
|
|
level.deposit_free_quota_cent = Math.round(Number(value || 0) * 100)
|
|
}
|
|
|
|
function resetDefaults() {
|
|
draft.value = cloneRenterGrowthConfig(defaultRenterGrowthConfig)
|
|
}
|
|
|
|
function validateDraft() {
|
|
if (!Number.isInteger(draft.value.points_per_yuan) || draft.value.points_per_yuan <= 0) {
|
|
return '每元积分必须是大于 0 的整数'
|
|
}
|
|
for (const [index, level] of draft.value.levels.entries()) {
|
|
const previous = draft.value.levels[index - 1]
|
|
if (!Number.isInteger(level.min_points) || level.min_points < 0) {
|
|
return `${level.name}积分门槛不正确`
|
|
}
|
|
if (index === 0 && level.min_points !== 0) return '普通等级积分门槛必须为 0'
|
|
if (previous && level.min_points <= previous.min_points) return '等级积分门槛必须严格递增'
|
|
if (level.discount_bps <= 0 || level.discount_bps > 10000) {
|
|
return `${level.name}支付比例必须在 0% 到 100% 之间`
|
|
}
|
|
if (previous && level.discount_bps > previous.discount_bps)
|
|
return '高等级支付比例不能高于低等级'
|
|
if (!Number.isInteger(level.deposit_free_quota_cent) || level.deposit_free_quota_cent < 0) {
|
|
return `${level.name}免押额度不正确`
|
|
}
|
|
if (previous && level.deposit_free_quota_cent < previous.deposit_free_quota_cent) {
|
|
return '高等级免押额度不能低于低等级'
|
|
}
|
|
}
|
|
return ''
|
|
}
|
|
|
|
async function handleSave() {
|
|
const validationMessage = validateDraft()
|
|
if (validationMessage) {
|
|
ElMessage.warning(validationMessage)
|
|
return
|
|
}
|
|
submitting.value = true
|
|
try {
|
|
await updateSystemConfig(props.config.key, {
|
|
value: JSON.stringify(draft.value),
|
|
description: '租客成长等级规则(积分门槛、租金折扣、等级免押额度)',
|
|
})
|
|
ElMessage.success('成长等级配置已更新')
|
|
emit('saved')
|
|
emit('update:modelValue', false)
|
|
} catch (error) {
|
|
ElMessage.error(readError(error, '成长等级配置保存失败'))
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<el-dialog
|
|
:model-value="modelValue"
|
|
title="成长等级配置"
|
|
width="860px"
|
|
destroy-on-close
|
|
@update:model-value="emit('update:modelValue', $event)"
|
|
>
|
|
<div class="growth-config-form">
|
|
<div class="growth-switch-row">
|
|
<span>启用成长等级</span>
|
|
<el-switch v-model="draft.enabled" />
|
|
</div>
|
|
<el-form label-position="top" @submit.prevent>
|
|
<el-form-item label="每完成 1 元实付租金获得积分">
|
|
<el-input-number
|
|
v-model="draft.points_per_yuan"
|
|
:min="1"
|
|
:max="1000"
|
|
:step="1"
|
|
step-strictly
|
|
/>
|
|
</el-form-item>
|
|
</el-form>
|
|
|
|
<el-table :data="draft.levels" border>
|
|
<el-table-column prop="name" label="等级" width="100" />
|
|
<el-table-column label="积分门槛" min-width="170">
|
|
<template #default="{ row, $index }">
|
|
<el-input-number
|
|
v-model="row.min_points"
|
|
:disabled="$index === 0"
|
|
:min="0"
|
|
:max="1000000000"
|
|
:step="100"
|
|
step-strictly
|
|
/>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="租金支付比例" min-width="190">
|
|
<template #default="{ row, $index }">
|
|
<el-input-number
|
|
:model-value="discountPercent(row)"
|
|
:disabled="$index === 0"
|
|
:min="0.01"
|
|
:max="100"
|
|
:precision="2"
|
|
:step="0.5"
|
|
@update:model-value="updateDiscountPercent(row, $event)"
|
|
/>
|
|
<span class="unit">%</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column label="等级免押额度" min-width="190">
|
|
<template #default="{ row, $index }">
|
|
<el-input-number
|
|
:model-value="quotaYuan(row)"
|
|
:disabled="$index === 0"
|
|
:min="0"
|
|
:max="1000000"
|
|
:precision="0"
|
|
:step="100"
|
|
@update:model-value="updateQuotaYuan(row, $event)"
|
|
/>
|
|
<span class="unit">元</span>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</div>
|
|
|
|
<template #footer>
|
|
<el-button :disabled="submitting" @click="resetDefaults">恢复默认</el-button>
|
|
<el-button :disabled="submitting" @click="emit('update:modelValue', false)">取消</el-button>
|
|
<el-button type="primary" :loading="submitting" @click="handleSave">保存配置</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.growth-config-form {
|
|
display: grid;
|
|
gap: 18px;
|
|
}
|
|
|
|
.growth-switch-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 40px;
|
|
padding-bottom: 14px;
|
|
border-bottom: 1px solid #e5e7eb;
|
|
color: #111827;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.growth-config-form :deep(.el-form-item) {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.growth-config-form :deep(.el-input-number) {
|
|
width: calc(100% - 32px);
|
|
}
|
|
|
|
.unit {
|
|
display: inline-block;
|
|
width: 28px;
|
|
margin-left: 4px;
|
|
color: #64748b;
|
|
font-size: 13px;
|
|
}
|
|
|
|
@media (max-width: 760px) {
|
|
.growth-config-form :deep(.el-table) {
|
|
min-width: 760px;
|
|
}
|
|
}
|
|
</style>
|