update point tier rule

This commit is contained in:
Timi
2026-08-14 18:26:11 +08:00
parent 3b5ff75772
commit a43694daa3
16 changed files with 940 additions and 32 deletions
@@ -0,0 +1,293 @@
<template>
<div class="tier-editor">
<div class="title">
<div>累计数值分档</div>
<div class="tips gray">首档从 0 开始,末档不设上限;积分为 0 不产生流水</div>
</div>
<div class="list">
<div
v-for="(tier, index) in tiers"
:key="tier.key"
class="card"
@click="openTierEditor(index)"
>
<div class="summary">
<span class="index" v-text="`第 ${index + 1} 档`" />
<strong v-text="getTierRangeText(index)" />
</div>
<div class="points text-bold" v-text="getTierPointText(tier)" />
<t-button
v-if="1 < tiers.length"
theme="danger"
variant="text"
shape="square"
@click.stop="removeTier(index)"
>
<t-icon name="close" />
</t-button>
</div>
</div>
<t-button class="add-tier" variant="outline" size="small" content="增加分档" @click="addTier" />
</div>
<bottom-popup
v-model="tierEditorVisible"
placement="bottom"
:title="tierEditorTitle"
@left-button-click="closeTierEditor"
@right-button-click="confirmTierEditor"
>
<div class="tier-editor-popup">
<t-form v-if="tierEditorIndex >= 0" class="form" label-align="left" label-width="10rem">
<t-form-item label="最小值" content-align="right">
<span class="gray" v-text="getTierMinText(tiers, tierEditorIndex)" />
</t-form-item>
<t-form-item v-if="!isLastTier(tiers, tierEditorIndex) || tierEditorAdding" :label="tierEditorAdding ? '当前档最大值' : '最大值'" required-mark>
<t-input
v-model.trim="tierEditorDraft.maxText"
type="number"
align="right"
borderless
placeholder="请输入最大值"
/>
</t-form-item>
<t-form-item label="积分" required-mark>
<t-input
v-model.trim="tierEditorDraft.pointValueText"
type="number"
align="right"
borderless
placeholder="请输入积分,可填写 0"
/>
</t-form-item>
<div class="tips">积分为 0 时规则仍会命中,但系统不会创建积分流水。</div>
</t-form>
</div>
</bottom-popup>
</template>
<script setup lang="ts">
import type { PointRuleTierForm } from "@/types/Point";
import { getTierMinText, getTierMinValue, isLastTier, resolveNonNegativeInteger } from "@/utils/pointRule";
import { Toast } from "tdesign-mobile-vue";
defineOptions({
name: "PointRuleTierEditor"
});
const tiers = defineModel<PointRuleTierForm[]>({ required: true });
const tierEditorVisible = ref(false);
const tierEditorIndex = ref(-1);
const tierEditorAdding = ref(false);
const tierEditorDraft = reactive({
maxText: "",
pointValueText: ""
});
const tierEditorTitle = computed(() => {
if (tierEditorIndex.value < 0) {
return "编辑分档";
}
return tierEditorAdding.value ? `编辑第 ${tierEditorIndex.value + 1} 档并新增下一档` : `编辑第 ${tierEditorIndex.value + 1} 档`;
});
function getTierRangeText(index: number): string {
const tier = tiers.value[index];
if (!tier) {
return "待填写";
}
const minText = getTierMinText(tiers.value, index);
if (isLastTier(tiers.value, index)) {
return `${minText} 及以上`;
}
const max = resolveNonNegativeInteger(tier.maxText);
return max === undefined ? `${minText} - 待填写` : `${minText}-${max}`;
}
function getTierPointText(tier: PointRuleTierForm): string {
const pointValue = resolveNonNegativeInteger(tier.pointValueText);
if (pointValue === undefined) {
return "待填写";
}
return `+${pointValue} 积分`;
}
function openTierEditor(index: number, adding = false): void {
const tier = tiers.value[index];
if (!tier) {
return;
}
tierEditorIndex.value = index;
tierEditorAdding.value = adding;
tierEditorDraft.maxText = adding ? "" : tier.maxText;
tierEditorDraft.pointValueText = tier.pointValueText;
tierEditorVisible.value = true;
}
function closeTierEditor(): void {
tierEditorAdding.value = false;
tierEditorVisible.value = false;
}
function confirmTierEditor(): void {
const index = tierEditorIndex.value;
const tier = tiers.value[index];
if (!tier) {
return;
}
const min = getTierMinValue(tiers.value, index);
const pointValue = resolveNonNegativeInteger(tierEditorDraft.pointValueText);
if (min === undefined) {
Toast.warning("请先修正上一档的最大值");
return;
}
if (pointValue === undefined) {
Toast.warning("积分必须是大于等于 0 的整数,允许填写 0");
return;
}
const shouldAddTier = tierEditorAdding.value && isLastTier(tiers.value, index);
if (!isLastTier(tiers.value, index) || shouldAddTier) {
const max = resolveNonNegativeInteger(tierEditorDraft.maxText);
if (max === undefined || max < min) {
Toast.warning("当前档最大值必须是不小于最小值的整数");
return;
}
tier.maxText = String(max);
} else {
tier.maxText = "";
}
tier.pointValueText = String(pointValue);
if (shouldAddTier) {
tiers.value.push(createTierForm());
}
closeTierEditor();
}
function addTier(): void {
if (!(tiers.value.length < 100)) {
Toast.warning("最多配置 100 档");
return;
}
const lastTierIndex = tiers.value.length - 1;
if (!(lastTierIndex < 0)) {
openTierEditor(lastTierIndex, true);
return;
}
tiers.value.push(createTierForm());
}
function removeTier(index: number): void {
if (!(1 < tiers.value.length)) {
return;
}
tiers.value.splice(index, 1);
}
function createTierForm(): PointRuleTierForm {
const key = tiers.value.reduce((max, tier) => Math.max(max, tier.key), -1) + 1;
return {
key,
maxText: "",
pointValueText: "0"
};
}
</script>
<style scoped lang="less">
.tier-editor {
padding: 1rem;
.tips {
font-size: .75rem;
margin-top: .25rem;
}
.list {
gap: .5rem;
display: grid;
margin-top: .5rem;
}
.card {
padding: .5rem .75rem;
display: flex;
align-items: center;
background: var(--td-bg-color-container);
border: 1px solid var(--td-component-border);
border-radius: .75rem;
.summary {
gap: .25rem;
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
strong {
font-size: .9375rem;
}
.index {
color: var(--td-brand-color);
font-size: .75rem;
}
}
.points {
gap: .25rem;
color: var(--td-brand-color);
display: flex;
font-size: 1.5rem;
align-items: baseline;
white-space: nowrap;
}
}
.add-tier {
width: 100%;
margin-top: .75rem;
}
}
.tier-editor-popup {
.form {
padding: .5rem 0 1rem 0;
.tips {
color: #52607a;
padding: 0 1rem;
font-size: .75rem;
line-height: 1.5;
}
}
}
:global(html.theme-dark) {
.tier-editor {
.tips,
.points span {
color: #94a3b8;
}
.card {
background: rgba(15, 23, 42, .72);
border-color: rgba(148, 163, 184, .16);
}
.tier-editor-popup {
background: #0f172a;
:deep(.t-input) {
background: rgba(15, 23, 42, .72);
border-color: rgba(148, 163, 184, .16);
}
.form {
.picker-text,
.tips {
color: #94a3b8;
}
}
}
}
}
</style>