diff --git a/src/pages/point/PointLedgerDetail.vue b/src/pages/point/PointLedgerDetail.vue
index be66170..6b5919e 100644
--- a/src/pages/point/PointLedgerDetail.vue
+++ b/src/pages/point/PointLedgerDetail.vue
@@ -296,7 +296,15 @@ function getRuleConditionText(rule: PointRule): string {
textList.push(`来源事件:${sourceEvent.value?.name || sourceEvent.value?.id || condition.sourceEventId}`);
}
if (condition.tiers?.length) {
- textList.push(condition.tiers.map(tier => `${tier.min}~${tier.max ?? "以上"}:${0 < tier.pointValue ? "发放" : "消耗"} ${Math.abs(tier.pointValue)} 分`).join(","));
+ const tierSourceEvent = sourceEvent.value || (condition.sourceEventId === triggerEvent.value?.id ? triggerEvent.value : undefined);
+ textList.push(condition.tiers.map(tier => {
+ const minText = formatEventNumberValue(tierSourceEvent, tier.min);
+ const maxText = tier.max === undefined || tier.max === null ? "以上" : formatEventNumberValue(tierSourceEvent, tier.max);
+ const pointText = tier.pointValue === 0
+ ? "不产生积分流水"
+ : `${0 < tier.pointValue ? "发放" : "消耗"} ${Math.abs(tier.pointValue)} 分`;
+ return `${minText}~${maxText}:${pointText}`;
+ }).join(","));
}
return textList.join(";") || "无额外条件";
}
diff --git a/src/pages/point/PointRuleEdit.vue b/src/pages/point/PointRuleEdit.vue
index e1c3618..21fd415 100644
--- a/src/pages/point/PointRuleEdit.vue
+++ b/src/pages/point/PointRuleEdit.vue
@@ -65,7 +65,7 @@
-
+
@@ -91,9 +91,8 @@
-
@@ -208,11 +198,13 @@ import EventAPI from "@/api/EventAPI";
import PointAPI from "@/api/PointAPI";
import PointRuleTierEditor from "@/pages/point/component/PointRuleTierEditor.vue";
import { useCurrentStoreStore } from "@/store/currentStoreStore";
+import { useNavBarStore } from "@/store/navBarStore";
import type { EventItem } from "@/types/EventItem";
import type { PointAction, PointRule, PointRuleMode, PointRuleStatus, PointRuleTier, PointRuleTierForm, PointRuleTriggerType } from "@/types/Point";
+import { getEventValueUnitText } from "@/utils/eventValue";
import { getErrorMessage } from "@/utils/error";
import { gaoRoleCodes, permissionCodes } from "@/utils/permission";
-import { getTierMinValue, isLastTier, resolveNonNegativeInteger } from "@/utils/pointRule";
+import { formatTierBoundaryValue, getTierMinValue, isLastTier, resolveNonNegativeInteger, resolveTierBoundaryValue } from "@/utils/pointRule";
type PickerValue = string | number;
@@ -222,6 +214,7 @@ defineOptions({
const route = useRoute();
const router = useRouter();
+const navBarStore = useNavBarStore();
const userStore = useUserStore();
const currentStoreStore = useCurrentStoreStore();
const routeId = computed(() => String(route.params.id || ""));
@@ -282,20 +275,24 @@ const eventPickerColumns = computed(() => [
const sourceEventPickerVisible = ref(false);
const sourceEventPickerDraft = ref([]);
const sourceEventList = computed(() => eventList.value.filter(item => item.valueType === "NUMBER" && item.id));
+const selectedSourceEvent = computed(() => sourceEventList.value.find(item => item.id === formModel.sourceEventId));
+const selectedSourceValueUnit = computed(() => selectedSourceEvent.value?.valueUnit || null);
const sourceEventPickerColumns = computed(() => [
sourceEventList.value
- .map(item => ({ label: item.name || "未命名事件", value: item.id || "" }))
+ .map(item => ({ label: `${item.name || "未命名事件"}(${getEventValueUnitText(item.valueUnit)})`, value: item.id || "" }))
.filter(item => item.value)
]);
-const selectedSourceEventName = computed(() => sourceEventList.value.find(item => item.id === formModel.sourceEventId)?.name || (formModel.sourceEventId ? `事件 ${formModel.sourceEventId}` : "请选择来源事件"));
+const selectedSourceEventName = computed(() => selectedSourceEvent.value
+ ? `${selectedSourceEvent.value.name || "未命名事件"}(${getEventValueUnitText(selectedSourceEvent.value.valueUnit)})`
+ : formModel.sourceEventId ? `事件 ${formModel.sourceEventId}` : "请选择来源事件");
let tierKey = 0;
-function createTierForm(tier?: PointRuleTier, isLast = false): PointRuleTierForm {
+function createTierForm(tier?: PointRuleTier, isLast = false, valueUnit?: EventItem["valueUnit"]): PointRuleTierForm {
const pointValue = tier?.pointValue;
return {
key: tierKey++,
- maxText: !isLast && tier?.max !== undefined && tier.max !== null ? String(tier.max) : "",
+ maxText: !isLast && tier?.max !== undefined && tier.max !== null ? formatTierBoundaryValue(tier.max, valueUnit) : "",
pointAction: pointValue !== undefined && pointValue < 0 ? "CONSUME" : "EARN",
pointValueText: pointValue === undefined ? "0" : String(Math.abs(pointValue))
};
@@ -360,6 +357,12 @@ function openSourceEventPicker(): void {
function confirmSourceEventPicker(value: Array): void {
const sourceEventId = String(value[0] || "");
if (sourceEventId) {
+ const previousSourceEvent = sourceEventList.value.find(item => item.id === formModel.sourceEventId);
+ const nextSourceEvent = sourceEventList.value.find(item => item.id === sourceEventId);
+ if (formModel.sourceEventId && previousSourceEvent?.valueUnit !== nextSourceEvent?.valueUnit) {
+ formModel.tiers = [createTierForm(undefined, true, nextSourceEvent?.valueUnit)];
+ Toast.warning("来源事件单位已变化,原分档已清空,请重新配置");
+ }
formModel.sourceEventId = sourceEventId;
}
sourceEventPickerVisible.value = false;
@@ -382,8 +385,11 @@ async function loadEventList(): Promise {
const options = isGlobalManager.value ? { storeId: formModel.storeId } : undefined;
const result = await EventAPI.list({ index: 0, size: 1000 }, options);
eventList.value = result.list || [];
- if (formModel.eventId && !eventList.value.some(item => item.id === formModel.eventId)) {
- eventList.value.push(await EventAPI.detail(formModel.eventId));
+ const requiredEventIdList = [formModel.eventId, formModel.sourceEventId].filter((id, index, list) => id && list.indexOf(id) === index);
+ for (const eventId of requiredEventIdList) {
+ if (!eventList.value.some(item => item.id === eventId)) {
+ eventList.value.push(await EventAPI.detail(eventId));
+ }
}
}
@@ -571,6 +577,7 @@ function resolveTimestamp(value: string): number | undefined {
function buildCondition(): PointRule["conditionJson"] {
if (formModel.triggerType === "CUSTOMER_EVENT_VALUE_TIER") {
+ const valueUnit = selectedSourceValueUnit.value;
return {
sourceEventId: formModel.sourceEventId,
sourceValueField: "numberValue",
@@ -580,12 +587,13 @@ function buildCondition(): PointRule["conditionJson"] {
unit: "YEAR"
},
tiers: formModel.tiers.map((tier, index) => {
+ const min = getTierMinValue(formModel.tiers, index, valueUnit);
const result: PointRuleTier = {
- min: getTierMinValue(formModel.tiers, index) || 0,
+ min: min ?? 0,
pointValue: (tier.pointAction === "CONSUME" ? -1 : 1) * (resolveNonNegativeInteger(tier.pointValueText) || 0)
};
if (!isLastTier(formModel.tiers, index)) {
- result.max = resolveNonNegativeInteger(tier.maxText);
+ result.max = resolveTierBoundaryValue(tier.maxText, valueUnit);
}
return result;
})
@@ -628,10 +636,14 @@ function validateForm(): boolean {
Toast.warning("分档规则必须使用分档积分模式");
return false;
}
- if (!sourceEventList.value.some(item => item.id === formModel.sourceEventId)) {
+ if (!selectedSourceEvent.value) {
Toast.warning("请选择 NUMBER 类型的来源事件");
return false;
}
+ if (selectedSourceValueUnit.value !== "CENT" && selectedSourceValueUnit.value !== "COUNT") {
+ Toast.warning("来源事件必须配置数值单位");
+ return false;
+ }
if (!resolvePositiveInteger(formModel.windowAmountText)) {
Toast.warning("累计窗口必须是正整数");
return false;
@@ -642,7 +654,7 @@ function validateForm(): boolean {
}
for (let i = 0; i < formModel.tiers.length; i++) {
const tier = formModel.tiers[i];
- const min = getTierMinValue(formModel.tiers, i);
+ const min = getTierMinValue(formModel.tiers, i, selectedSourceValueUnit.value);
const pointValue = resolveNonNegativeInteger(tier.pointValueText);
if (min === undefined) {
Toast.warning(`第 ${i + 1} 档的上一档最大值无效`);
@@ -653,9 +665,11 @@ function validateForm(): boolean {
return false;
}
if (!isLastTier(formModel.tiers, i)) {
- const max = resolveNonNegativeInteger(tier.maxText);
+ const max = resolveTierBoundaryValue(tier.maxText, selectedSourceValueUnit.value);
if (max === undefined || max < min) {
- Toast.warning(`第 ${i + 1} 档最大值必须是不小于最小值的整数`);
+ Toast.warning(selectedSourceValueUnit.value === "CENT"
+ ? `第 ${i + 1} 档最大值必须是不小于最小值且最多两位小数`
+ : `第 ${i + 1} 档最大值必须是不小于最小值的整数`);
return false;
}
}
@@ -786,31 +800,43 @@ async function fillForm(): Promise {
maxText: current.conditionJson?.max === undefined ? "" : String(Math.abs(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)],
+ tiers: [],
beginAtText: current.beginAt ? Time.toDateTime(current.beginAt) : "",
endAtText: current.endAt ? Time.toDateTime(current.endAt) : "",
priorityText: String(current.priority ?? 0),
exclusive: current.exclusive ?? false,
remark: current.remark || ""
});
+ await loadEventList();
+ const valueUnit = selectedSourceValueUnit.value;
+ formModel.tiers = current.conditionJson?.tiers?.length
+ ? current.conditionJson.tiers.map((tier, index, list) => createTierForm(tier, index === list.length - 1, valueUnit))
+ : [createTierForm(undefined, true, valueUnit)];
+ return;
}
await loadEventList();
+ if (!formModel.tiers.length) {
+ formModel.tiers.push(createTierForm(undefined, true, selectedSourceValueUnit.value));
+ }
}
onMounted(async () => {
+ if (userStore.hasPermissions(savePermissionCode.value)) {
+ navBarStore.setRightText("保存", "var(--td-brand-color)", saveRule);
+ }
try {
await fillForm();
} catch (error) {
Toast.error(getErrorMessage(error, "积分规则加载失败"));
}
});
+
+onUnmounted(() => navBarStore.clearRight());