update point tier rule
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div v-can="pageAccess" class="point-rule-edit">
|
||||
<t-form class="form" label-align="left" label-width="6rem">
|
||||
<t-form class="form" label-align="left" label-width="7rem">
|
||||
<card-container>
|
||||
<t-form-item v-if="isGlobalManager" label="门店" required-mark :arrow="isCreateMode" @click="openStorePicker">
|
||||
<span class="picker-text" v-text="selectedStoreName" />
|
||||
@@ -22,7 +22,7 @@
|
||||
<t-form-item label="触发类型" arrow @click="openTriggerPicker">
|
||||
<span class="picker-text" v-text="getTriggerText(formModel.triggerType)" />
|
||||
</t-form-item>
|
||||
<t-form-item label="积分值模式" arrow @click="openModePicker">
|
||||
<t-form-item label="积分值模式" :arrow="formModel.triggerType !== 'CUSTOMER_EVENT_VALUE_TIER'" @click="openModePicker">
|
||||
<span class="picker-text" v-text="getModeText(formModel.pointMode)" />
|
||||
</t-form-item>
|
||||
<t-form-item v-if="formModel.pointMode === 'FIXED'" label="固定积分" required-mark>
|
||||
@@ -52,6 +52,15 @@
|
||||
</div>
|
||||
<span class="tips">登记时只有输入在此范围内的正整数,系统才会按本规则发放积分</span>
|
||||
</div>
|
||||
<template v-if="formModel.triggerType === 'CUSTOMER_EVENT_VALUE_TIER'" class="tier-condition">
|
||||
<t-form-item label="来源事件" required-mark arrow @click="openSourceEventPicker">
|
||||
<span class="picker-text" v-text="selectedSourceEventName" />
|
||||
</t-form-item>
|
||||
<t-form-item label="累计窗口(年)" label-width="8rem">
|
||||
<t-input v-model.trim="formModel.windowAmountText" type="number" align="right" borderless />
|
||||
</t-form-item>
|
||||
<point-rule-tier-editor v-model="formModel.tiers" />
|
||||
</template>
|
||||
</card-container>
|
||||
<card-container>
|
||||
<t-form-item label="生效开始" arrow @click="openBeginAtPicker">
|
||||
@@ -117,6 +126,15 @@
|
||||
@confirm="confirmEventPicker"
|
||||
/>
|
||||
</t-popup>
|
||||
<t-popup v-model="sourceEventPickerVisible" placement="bottom">
|
||||
<t-picker
|
||||
v-model="sourceEventPickerDraft"
|
||||
:columns="sourceEventPickerColumns"
|
||||
title="选择来源事件"
|
||||
@cancel="sourceEventPickerVisible = false"
|
||||
@confirm="confirmSourceEventPicker"
|
||||
/>
|
||||
</t-popup>
|
||||
<t-popup v-model="statusPickerVisible" placement="bottom">
|
||||
<t-picker
|
||||
v-model="statusPickerDraft"
|
||||
@@ -173,11 +191,13 @@ import { DialogPlugin, Toast } from "tdesign-mobile-vue";
|
||||
import { Time, useUserStore } from "timi-web";
|
||||
import EventAPI from "@/api/EventAPI";
|
||||
import PointAPI from "@/api/PointAPI";
|
||||
import PointRuleTierEditor from "@/pages/point/component/PointRuleTierEditor.vue";
|
||||
import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
||||
import type { EventItem } from "@/types/EventItem";
|
||||
import type { PointRule, PointRuleMode, PointRuleStatus, PointRuleTriggerType } from "@/types/Point";
|
||||
import type { PointRule, PointRuleMode, PointRuleStatus, PointRuleTier, PointRuleTierForm, PointRuleTriggerType } from "@/types/Point";
|
||||
import { getErrorMessage } from "@/utils/error";
|
||||
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
|
||||
import { getTierMinValue, isLastTier, resolveNonNegativeInteger } from "@/utils/pointRule";
|
||||
|
||||
type PickerValue = string | number;
|
||||
|
||||
@@ -214,6 +234,9 @@ const formModel = reactive({
|
||||
thresholdText: "",
|
||||
minText: "",
|
||||
maxText: "",
|
||||
sourceEventId: "",
|
||||
windowAmountText: "1",
|
||||
tiers: [] as PointRuleTierForm[],
|
||||
beginAtText: "",
|
||||
endAtText: "",
|
||||
priorityText: "0",
|
||||
@@ -240,6 +263,29 @@ const eventPickerColumns = computed(() => [
|
||||
.map(item => ({ label: item.name || "未命名事件", value: item.id || "" }))
|
||||
.filter(item => item.value)
|
||||
]);
|
||||
const sourceEventPickerVisible = ref(false);
|
||||
const sourceEventPickerDraft = ref<PickerValue[]>([]);
|
||||
const sourceEventList = computed(() => eventList.value.filter(item => item.valueType === "NUMBER" && item.id));
|
||||
const sourceEventPickerColumns = computed(() => [
|
||||
sourceEventList.value
|
||||
.map(item => ({ label: item.name || "未命名事件", value: item.id || "" }))
|
||||
.filter(item => item.value)
|
||||
]);
|
||||
const selectedSourceEventName = computed(() => sourceEventList.value.find(item => item.id === formModel.sourceEventId)?.name || (formModel.sourceEventId ? `事件 ${formModel.sourceEventId}` : "请选择来源事件"));
|
||||
|
||||
let tierKey = 0;
|
||||
|
||||
function createTierForm(tier?: PointRuleTier, isLast = false): PointRuleTierForm {
|
||||
return {
|
||||
key: tierKey++,
|
||||
maxText: !isLast && tier?.max !== undefined && tier.max !== null ? String(tier.max) : "",
|
||||
pointValueText: tier ? String(tier.pointValue) : "0"
|
||||
};
|
||||
}
|
||||
|
||||
if (!formModel.tiers.length) {
|
||||
formModel.tiers.push(createTierForm(undefined, true));
|
||||
}
|
||||
|
||||
function openStorePicker(): void {
|
||||
if (!isCreateMode.value || !storePickerColumns.value[0]?.length) {
|
||||
@@ -281,6 +327,26 @@ function confirmEventPicker(value: Array<string | number>): void {
|
||||
eventPickerVisible.value = false;
|
||||
}
|
||||
|
||||
function openSourceEventPicker(): void {
|
||||
if (formModel.triggerType !== "CUSTOMER_EVENT_VALUE_TIER") {
|
||||
return;
|
||||
}
|
||||
if (!sourceEventPickerColumns.value[0]?.length) {
|
||||
Toast.warning("暂无可用的 NUMBER 来源事件");
|
||||
return;
|
||||
}
|
||||
sourceEventPickerDraft.value = [formModel.sourceEventId];
|
||||
sourceEventPickerVisible.value = true;
|
||||
}
|
||||
|
||||
function confirmSourceEventPicker(value: Array<string | number>): void {
|
||||
const sourceEventId = String(value[0] || "");
|
||||
if (sourceEventId) {
|
||||
formModel.sourceEventId = sourceEventId;
|
||||
}
|
||||
sourceEventPickerVisible.value = false;
|
||||
}
|
||||
|
||||
async function prepareStoreScope(): Promise<void> {
|
||||
if (isGlobalManager.value) {
|
||||
await currentStoreStore.loadStoreList();
|
||||
@@ -314,12 +380,18 @@ const triggerOptions: Array<{ label: string; value: PointRuleTriggerType }> = [
|
||||
{ label: "每次登记", value: "EVERY_TIME" },
|
||||
{ label: "首次登记", value: "FIRST_TIME" },
|
||||
{ label: "达到阈值后每次登记", value: "AFTER_COUNT_EVERY_TIME" },
|
||||
{ label: "登记时自定义积分", value: "SOURCE_VALUE" }
|
||||
];
|
||||
const modeOptions: Array<{ label: string; value: PointRuleMode }> = [
|
||||
{ label: "固定积分", value: "FIXED" },
|
||||
{ label: "使用登记时填写的积分", value: "SOURCE_VALUE" }
|
||||
{ label: "登记时自定义积分", value: "SOURCE_VALUE" },
|
||||
{ label: "按来源事件累计分档", value: "CUSTOMER_EVENT_VALUE_TIER" }
|
||||
];
|
||||
const modeOptions = computed<Array<{ label: string; value: PointRuleMode }>>(() => {
|
||||
if (formModel.triggerType === "CUSTOMER_EVENT_VALUE_TIER") {
|
||||
return [{ label: "分档积分", value: "TIER" }];
|
||||
}
|
||||
if (formModel.triggerType === "SOURCE_VALUE") {
|
||||
return [{ label: "使用登记时填写的积分", value: "SOURCE_VALUE" }];
|
||||
}
|
||||
return [{ label: "固定积分", value: "FIXED" }];
|
||||
});
|
||||
const statusPickerVisible = ref(false);
|
||||
const statusPickerDraft = ref<PickerValue[]>([]);
|
||||
const statusPickerColumns = [statusOptions];
|
||||
@@ -328,7 +400,7 @@ const triggerPickerDraft = ref<PickerValue[]>([]);
|
||||
const triggerPickerColumns = [triggerOptions];
|
||||
const modePickerVisible = ref(false);
|
||||
const modePickerDraft = ref<PickerValue[]>([]);
|
||||
const modePickerColumns = [modeOptions];
|
||||
const modePickerColumns = computed(() => [modeOptions.value]);
|
||||
|
||||
function openStatusPicker(): void {
|
||||
statusPickerDraft.value = [formModel.status];
|
||||
@@ -352,21 +424,31 @@ function confirmTriggerPicker(value: Array<string | number>): void {
|
||||
const triggerType = value[0];
|
||||
if (typeof triggerType === "string" && triggerOptions.some(item => item.value === triggerType)) {
|
||||
formModel.triggerType = triggerType as PointRuleTriggerType;
|
||||
formModel.pointMode = triggerType === "SOURCE_VALUE" ? "SOURCE_VALUE" : "FIXED";
|
||||
formModel.pointMode = triggerType === "SOURCE_VALUE" ? "SOURCE_VALUE" : triggerType === "CUSTOMER_EVENT_VALUE_TIER" ? "TIER" : "FIXED";
|
||||
}
|
||||
triggerPickerVisible.value = false;
|
||||
}
|
||||
|
||||
function openModePicker(): void {
|
||||
if (formModel.triggerType === "CUSTOMER_EVENT_VALUE_TIER") {
|
||||
return;
|
||||
}
|
||||
modePickerDraft.value = [formModel.pointMode];
|
||||
modePickerVisible.value = true;
|
||||
}
|
||||
|
||||
function confirmModePicker(value: Array<string | number>): void {
|
||||
const pointMode = value[0];
|
||||
if (pointMode === "FIXED" || pointMode === "SOURCE_VALUE") {
|
||||
if (pointMode === "FIXED" || pointMode === "SOURCE_VALUE" || pointMode === "TIER") {
|
||||
if (!modeOptions.value.some(item => item.value === pointMode)) {
|
||||
return;
|
||||
}
|
||||
formModel.pointMode = pointMode;
|
||||
formModel.triggerType = pointMode === "SOURCE_VALUE" ? "SOURCE_VALUE" : formModel.triggerType === "SOURCE_VALUE" ? "EVERY_TIME" : formModel.triggerType;
|
||||
formModel.triggerType = pointMode === "SOURCE_VALUE"
|
||||
? "SOURCE_VALUE"
|
||||
: pointMode === "TIER"
|
||||
? "CUSTOMER_EVENT_VALUE_TIER"
|
||||
: formModel.triggerType === "SOURCE_VALUE" || formModel.triggerType === "CUSTOMER_EVENT_VALUE_TIER" ? "EVERY_TIME" : formModel.triggerType;
|
||||
}
|
||||
modePickerVisible.value = false;
|
||||
}
|
||||
@@ -380,7 +462,7 @@ function getTriggerText(triggerType: PointRuleTriggerType): string {
|
||||
}
|
||||
|
||||
function getModeText(pointMode: PointRuleMode): string {
|
||||
return modeOptions.find(item => item.value === pointMode)?.label || "固定积分";
|
||||
return modeOptions.value.find(item => item.value === pointMode)?.label || "固定积分";
|
||||
}
|
||||
|
||||
// ---------- 生效时间 ----------
|
||||
@@ -438,6 +520,27 @@ function resolveTimestamp(value: string): number | undefined {
|
||||
}
|
||||
|
||||
function buildCondition(): PointRule["conditionJson"] {
|
||||
if (formModel.triggerType === "CUSTOMER_EVENT_VALUE_TIER") {
|
||||
return {
|
||||
sourceEventId: formModel.sourceEventId,
|
||||
sourceValueField: "numberValue",
|
||||
window: {
|
||||
type: "ROLLING",
|
||||
amount: resolvePositiveInteger(formModel.windowAmountText) || 1,
|
||||
unit: "YEAR"
|
||||
},
|
||||
tiers: formModel.tiers.map((tier, index) => {
|
||||
const result: PointRuleTier = {
|
||||
min: getTierMinValue(formModel.tiers, index) || 0,
|
||||
pointValue: resolveNonNegativeInteger(tier.pointValueText) || 0
|
||||
};
|
||||
if (!isLastTier(formModel.tiers, index)) {
|
||||
result.max = resolveNonNegativeInteger(tier.maxText);
|
||||
}
|
||||
return result;
|
||||
})
|
||||
};
|
||||
}
|
||||
if (formModel.pointMode === "SOURCE_VALUE") {
|
||||
return {
|
||||
min: resolvePositiveInteger(formModel.minText),
|
||||
@@ -470,7 +573,44 @@ function validateForm(): boolean {
|
||||
Toast.warning("请输入规则名称");
|
||||
return false;
|
||||
}
|
||||
if (formModel.pointMode === "FIXED" && !resolvePositiveInteger(formModel.pointValueText)) {
|
||||
if (formModel.triggerType === "CUSTOMER_EVENT_VALUE_TIER") {
|
||||
if (formModel.pointMode !== "TIER") {
|
||||
Toast.warning("分档规则必须使用分档积分模式");
|
||||
return false;
|
||||
}
|
||||
if (!sourceEventList.value.some(item => item.id === formModel.sourceEventId)) {
|
||||
Toast.warning("请选择 NUMBER 类型的来源事件");
|
||||
return false;
|
||||
}
|
||||
if (!resolvePositiveInteger(formModel.windowAmountText)) {
|
||||
Toast.warning("累计窗口必须是正整数");
|
||||
return false;
|
||||
}
|
||||
if (!formModel.tiers.length || !(formModel.tiers.length < 100)) {
|
||||
Toast.warning("分档数量必须是 1-100 档");
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < formModel.tiers.length; i++) {
|
||||
const tier = formModel.tiers[i];
|
||||
const min = getTierMinValue(formModel.tiers, i);
|
||||
const pointValue = resolveNonNegativeInteger(tier.pointValueText);
|
||||
if (min === undefined) {
|
||||
Toast.warning(`第 ${i + 1} 档的上一档最大值无效`);
|
||||
return false;
|
||||
}
|
||||
if (pointValue === undefined) {
|
||||
Toast.warning(`第 ${i + 1} 档积分必须是大于等于 0 的整数`);
|
||||
return false;
|
||||
}
|
||||
if (!isLastTier(formModel.tiers, i)) {
|
||||
const max = resolveNonNegativeInteger(tier.maxText);
|
||||
if (max === undefined || max < min) {
|
||||
Toast.warning(`第 ${i + 1} 档最大值必须是不小于最小值的整数`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (formModel.pointMode === "FIXED" && !resolvePositiveInteger(formModel.pointValueText)) {
|
||||
Toast.warning("固定积分必须是正整数");
|
||||
return false;
|
||||
}
|
||||
@@ -591,6 +731,11 @@ async function fillForm(): Promise<void> {
|
||||
thresholdText: current.conditionJson?.threshold === undefined ? "" : String(current.conditionJson.threshold),
|
||||
minText: current.conditionJson?.min === undefined ? "" : String(current.conditionJson.min),
|
||||
maxText: current.conditionJson?.max === undefined ? "" : String(current.conditionJson.max),
|
||||
sourceEventId: current.conditionJson?.sourceEventId || "",
|
||||
windowAmountText: current.conditionJson?.window?.amount === undefined ? "1" : String(current.conditionJson.window.amount),
|
||||
tiers: current.conditionJson?.tiers?.length
|
||||
? current.conditionJson.tiers.map((tier, index, list) => createTierForm(tier, index === list.length - 1))
|
||||
: [createTierForm(undefined, true)],
|
||||
beginAtText: current.beginAt ? Time.toDateTime(current.beginAt) : "",
|
||||
endAtText: current.endAt ? Time.toDateTime(current.endAt) : "",
|
||||
priorityText: String(current.priority ?? 0),
|
||||
@@ -664,6 +809,28 @@ onMounted(async () => {
|
||||
}
|
||||
}
|
||||
|
||||
.tier-condition {
|
||||
padding: .75rem 1rem 1rem;
|
||||
|
||||
.window-row {
|
||||
gap: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: .5rem 0;
|
||||
|
||||
.t-input {
|
||||
flex: 1;
|
||||
border: 1px solid var(--td-component-border);
|
||||
padding: .25rem .5rem;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.window-label {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: #52607a;
|
||||
font-size: .75rem;
|
||||
@@ -704,6 +871,7 @@ onMounted(async () => {
|
||||
background: rgba(15, 23, 42, .72);
|
||||
border-color: rgba(148, 163, 184, .16);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<template #left-icon>
|
||||
<div class="point">
|
||||
<t-icon name="money" />
|
||||
<span v-text="`${item.pointValue ? item.pointValue : '?'}`" />
|
||||
<span v-text="getPointText(item)" />
|
||||
</div>
|
||||
</template>
|
||||
<template #title>
|
||||
@@ -232,11 +232,19 @@ function getTriggerText(rule: PointRule): string {
|
||||
EVERY_TIME: "每次登记",
|
||||
FIRST_TIME: "首次登记",
|
||||
AFTER_COUNT_EVERY_TIME: `达到 ${rule.conditionJson?.threshold ?? 0} 次后每次登记`,
|
||||
SOURCE_VALUE: "登记时自定义积分"
|
||||
SOURCE_VALUE: "登记时自定义积分",
|
||||
CUSTOMER_EVENT_VALUE_TIER: `按 ${rule.conditionJson?.window?.amount ?? 1} 年累计分档`
|
||||
};
|
||||
return map[rule.triggerType || ""] || "未知触发类型";
|
||||
}
|
||||
|
||||
function getPointText(rule: PointRule): string {
|
||||
if (rule.pointMode === "TIER") {
|
||||
return "分档";
|
||||
}
|
||||
return rule.pointValue === undefined || rule.pointValue === null ? "?" : String(rule.pointValue);
|
||||
}
|
||||
|
||||
function getStatusText(status?: PointRuleStatus): string {
|
||||
return ({
|
||||
DRAFT: "草稿",
|
||||
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user