update point rule groovy script
This commit is contained in:
+8
-1
@@ -6,7 +6,8 @@ import type {
|
|||||||
PointAdjustRequest,
|
PointAdjustRequest,
|
||||||
PointLedger,
|
PointLedger,
|
||||||
PointLedgerPage,
|
PointLedgerPage,
|
||||||
PointRule
|
PointRule,
|
||||||
|
GroovyScript
|
||||||
} from "@/types/Point";
|
} from "@/types/Point";
|
||||||
import { getCurrentStoreId, withCurrentStoreId, withCurrentStorePage } from "@/utils/store";
|
import { getCurrentStoreId, withCurrentStoreId, withCurrentStorePage } from "@/utils/store";
|
||||||
|
|
||||||
@@ -94,6 +95,11 @@ async function ruleDetail(id: string): Promise<PointRule> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 查询积分规则可选的 Groovy 脚本。 */
|
||||||
|
async function ruleScriptList(): Promise<GroovyScript[]> {
|
||||||
|
return await axios.post(`${BASE_URI}/rule/script/list`);
|
||||||
|
}
|
||||||
|
|
||||||
async function ruleCreate(rule: PointRule): Promise<string> {
|
async function ruleCreate(rule: PointRule): Promise<string> {
|
||||||
return await axios.post(`${BASE_URI}/rule/create`, withCurrentStoreId(rule));
|
return await axios.post(`${BASE_URI}/rule/create`, withCurrentStoreId(rule));
|
||||||
}
|
}
|
||||||
@@ -120,6 +126,7 @@ export default {
|
|||||||
adjust,
|
adjust,
|
||||||
ruleList,
|
ruleList,
|
||||||
ruleDetail,
|
ruleDetail,
|
||||||
|
ruleScriptList,
|
||||||
ruleCreate,
|
ruleCreate,
|
||||||
ruleUpdate,
|
ruleUpdate,
|
||||||
ruleSort,
|
ruleSort,
|
||||||
|
|||||||
@@ -22,9 +22,6 @@
|
|||||||
borderless
|
borderless
|
||||||
/>
|
/>
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
<t-form-item v-if="isGlobalManager" label="门店" :arrow="isCreateMode" @click="openStorePicker">
|
|
||||||
<div class="picker-text" v-text="selectedStoreName" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="业务类型" :arrow="false">
|
<t-form-item label="业务类型" :arrow="false">
|
||||||
<div class="picker-text" v-text="getBizTypeText(formModel.bizType)" />
|
<div class="picker-text" v-text="getBizTypeText(formModel.bizType)" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
@@ -171,15 +168,6 @@
|
|||||||
@confirm="confirmLimitPicker"
|
@confirm="confirmLimitPicker"
|
||||||
/>
|
/>
|
||||||
</t-popup>
|
</t-popup>
|
||||||
<t-popup v-model="storePickerVisible" placement="bottom">
|
|
||||||
<t-picker
|
|
||||||
v-model="storePickerDraft"
|
|
||||||
:columns="storePickerColumns"
|
|
||||||
title="选择门店"
|
|
||||||
@cancel="storePickerVisible = false"
|
|
||||||
@confirm="confirmStorePicker"
|
|
||||||
/>
|
|
||||||
</t-popup>
|
|
||||||
<t-popup v-model="statusPickerVisible" placement="bottom">
|
<t-popup v-model="statusPickerVisible" placement="bottom">
|
||||||
<t-picker
|
<t-picker
|
||||||
v-model="statusPickerDraft"
|
v-model="statusPickerDraft"
|
||||||
@@ -311,46 +299,6 @@ function getBizTypeText(value: EventBizType): string {
|
|||||||
return getEventBizTypeText(value);
|
return getEventBizTypeText(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 门店选择 ----------
|
|
||||||
|
|
||||||
const storePickerVisible = ref(false);
|
|
||||||
const storePickerDraft = ref<PickerValue[]>([]);
|
|
||||||
const storePickerColumns = computed(() => [
|
|
||||||
currentStoreStore.storeList
|
|
||||||
.map(item => ({
|
|
||||||
label: item.name || item.code || "未命名门店",
|
|
||||||
value: item.id || ""
|
|
||||||
}))
|
|
||||||
.filter(item => item.value)
|
|
||||||
]);
|
|
||||||
const selectedStoreName = computed(() => {
|
|
||||||
const store = currentStoreStore.storeList.find(item => item.id === formModel.storeId);
|
|
||||||
return store?.name || store?.code || "请选择门店";
|
|
||||||
});
|
|
||||||
|
|
||||||
function openStorePicker(): void {
|
|
||||||
if (!isCreateMode.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!currentStoreStore.storeList.length) {
|
|
||||||
Message.warning("暂无可选门店");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
storePickerDraft.value = [formModel.storeId];
|
|
||||||
storePickerVisible.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function confirmStorePicker(): Promise<void> {
|
|
||||||
const storeId = String(storePickerDraft.value[0] || "");
|
|
||||||
if (!storeId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
formModel.storeId = storeId;
|
|
||||||
storePickerVisible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------- 颜色 ----------
|
// ---------- 颜色 ----------
|
||||||
|
|
||||||
const colorPickerVisible = ref(false);
|
const colorPickerVisible = ref(false);
|
||||||
@@ -600,7 +548,7 @@ function resolveTimestamp(valueText: string): number | undefined {
|
|||||||
|
|
||||||
function validateForm(): boolean {
|
function validateForm(): boolean {
|
||||||
if (!formModel.storeId) {
|
if (!formModel.storeId) {
|
||||||
Message.warning("请选择门店");
|
Message.warning("当前门店不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!formModel.name.trim()) {
|
if (!formModel.name.trim()) {
|
||||||
@@ -729,15 +677,10 @@ async function saveEventItem(): Promise<void> {
|
|||||||
async function prepareStoreScope(): Promise<void> {
|
async function prepareStoreScope(): Promise<void> {
|
||||||
if (isGlobalManager.value) {
|
if (isGlobalManager.value) {
|
||||||
await currentStoreStore.loadStoreList();
|
await currentStoreStore.loadStoreList();
|
||||||
const queryStoreId = String(route.query.storeId || "");
|
} else {
|
||||||
formModel.storeId = currentStoreStore.storeList.some(item => item.id === queryStoreId)
|
await currentStoreStore.loadLoginStore();
|
||||||
? queryStoreId
|
|
||||||
: currentStoreStore.currentStoreId;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
formModel.storeId = currentStoreStore.currentStoreId;
|
||||||
const store = await currentStoreStore.loadLoginStore();
|
|
||||||
formModel.storeId = store?.id || "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fillForm(): Promise<void> {
|
async function fillForm(): Promise<void> {
|
||||||
|
|||||||
@@ -328,10 +328,7 @@ async function loadData(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function toCreate(): Promise<void> {
|
async function toCreate(): Promise<void> {
|
||||||
await router.push({
|
await router.push("/management/registration-events/create");
|
||||||
path: "/management/registration-events/create",
|
|
||||||
query: currentStoreStore.currentStoreId ? { storeId: currentStoreStore.currentStoreId } : undefined
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toSort(): Promise<void> {
|
async function toSort(): Promise<void> {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
<t-cell title="规则名称" :note="hitRule.name || '未命名规则'" />
|
<t-cell title="规则名称" :note="hitRule.name || '未命名规则'" />
|
||||||
<t-cell title="规则模式" :note="hitRule.ruleMode === 'GROOVY' ? '脚本规则' : '简单规则'" />
|
<t-cell title="规则模式" :note="hitRule.ruleMode === 'GROOVY' ? '脚本规则' : '简单规则'" />
|
||||||
<t-cell title="规则积分" :note="getRulePointText(hitRule)" />
|
<t-cell title="规则积分" :note="getRulePointText(hitRule)" />
|
||||||
|
<t-cell v-if="hitRule.ruleMode === 'GROOVY'" title="执行脚本" :note="getRuleScriptText(hitRule)" />
|
||||||
<t-cell title="规则状态" :note="getRuleStatusText(hitRule.status)" />
|
<t-cell title="规则状态" :note="getRuleStatusText(hitRule.status)" />
|
||||||
<t-cell title="规则优先级" :note="getPointText(hitRule.priority)" />
|
<t-cell title="规则优先级" :note="getPointText(hitRule.priority)" />
|
||||||
<t-cell title="命中后停止匹配" :note="hitRule.exclusive ? '是' : '否'" />
|
<t-cell title="命中后停止匹配" :note="hitRule.exclusive ? '是' : '否'" />
|
||||||
@@ -254,6 +255,15 @@ function getRulePointText(rule: PointRule): string {
|
|||||||
return `${0 < rule.pointValue ? "发放" : "消耗"} ${Math.abs(rule.pointValue)} 分`;
|
return `${0 < rule.pointValue ? "发放" : "消耗"} ${Math.abs(rule.pointValue)} 分`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRuleScriptText(rule: PointRule): string {
|
||||||
|
const script = rule.groovyScript;
|
||||||
|
if (!script) {
|
||||||
|
return rule.groovyScriptId || "未绑定脚本";
|
||||||
|
}
|
||||||
|
const version = script.latest?.version;
|
||||||
|
return `${script.name || script.code || "未命名脚本"}${version === undefined ? "" : `(v${version})`}`;
|
||||||
|
}
|
||||||
|
|
||||||
function getRuleStatusText(status?: PointRuleStatus): string {
|
function getRuleStatusText(status?: PointRuleStatus): string {
|
||||||
return ({
|
return ({
|
||||||
DRAFT: "草稿",
|
DRAFT: "草稿",
|
||||||
|
|||||||
@@ -34,7 +34,27 @@
|
|||||||
<t-cell-group v-if="rule" title="积分配置" theme="card" v-entrance-transition="140">
|
<t-cell-group v-if="rule" title="积分配置" theme="card" v-entrance-transition="140">
|
||||||
<t-cell title="规则模式" :note="rule.ruleMode === 'GROOVY' ? '脚本规则' : '简单规则'" />
|
<t-cell title="规则模式" :note="rule.ruleMode === 'GROOVY' ? '脚本规则' : '简单规则'" />
|
||||||
<t-cell v-if="rule.ruleMode !== 'GROOVY'" title="固定积分" :note="formatPointValue(rule.pointValue)" />
|
<t-cell v-if="rule.ruleMode !== 'GROOVY'" title="固定积分" :note="formatPointValue(rule.pointValue)" />
|
||||||
<t-cell v-else title="执行脚本" :note="rule.groovyScriptId || '未绑定脚本'" />
|
<template v-if="rule?.ruleMode === 'GROOVY'">
|
||||||
|
<t-cell title="执行脚本" :note="scriptName" />
|
||||||
|
<t-cell class="empty-cell-title-margin" title="脚本说明">
|
||||||
|
<template #description>
|
||||||
|
<markdown-view :content="rule.groovyScript?.description || '暂无脚本说明'" />
|
||||||
|
</template>
|
||||||
|
</t-cell>
|
||||||
|
<t-cell class="empty-cell-title-margin" title="脚本代码">
|
||||||
|
<template #description>
|
||||||
|
<code-editor
|
||||||
|
v-if="scriptContent"
|
||||||
|
:value="scriptContent"
|
||||||
|
language="groovy"
|
||||||
|
readonly
|
||||||
|
line-numbers
|
||||||
|
class="script-editor"
|
||||||
|
/>
|
||||||
|
<div v-else class="empty-script" v-text="'暂无脚本代码'" />
|
||||||
|
</template>
|
||||||
|
</t-cell>
|
||||||
|
</template>
|
||||||
</t-cell-group>
|
</t-cell-group>
|
||||||
<t-cell-group v-if="rule" title="其他配置" theme="card" v-entrance-transition="280">
|
<t-cell-group v-if="rule" title="其他配置" theme="card" v-entrance-transition="280">
|
||||||
<t-cell title="规则优先级" :note="formatNumber(rule.priority)" />
|
<t-cell title="规则优先级" :note="formatNumber(rule.priority)" />
|
||||||
@@ -60,7 +80,8 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DialogPlugin } from "tdesign-mobile-vue";
|
import { DialogPlugin } from "tdesign-mobile-vue";
|
||||||
import { Text, Time } from "timi-web";
|
import { CodeEditor } from "timi-tdesign-mobile";
|
||||||
|
import { MarkdownView, Text, Time } from "timi-web";
|
||||||
import { useUserStore } from "timi-web-api";
|
import { useUserStore } from "timi-web-api";
|
||||||
import EventAPI from "@/api/EventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import PointAPI from "@/api/PointAPI";
|
import PointAPI from "@/api/PointAPI";
|
||||||
@@ -112,6 +133,22 @@ const rulePointText = computed(() => {
|
|||||||
}
|
}
|
||||||
return formatPointValue(rule.value?.pointValue);
|
return formatPointValue(rule.value?.pointValue);
|
||||||
});
|
});
|
||||||
|
const scriptName = computed(() => rule.value?.groovyScript?.name || rule.value?.groovyScriptId || "未绑定脚本");
|
||||||
|
const scriptStatusText = computed(() => {
|
||||||
|
const script = rule.value?.groovyScript;
|
||||||
|
if (!script) {
|
||||||
|
return "脚本不存在";
|
||||||
|
}
|
||||||
|
if (script.enabled && script.publishedVersionId) {
|
||||||
|
return "已启用并已发布";
|
||||||
|
}
|
||||||
|
return script.enabled ? "已启用,未发布" : "已停用";
|
||||||
|
});
|
||||||
|
const scriptVersionText = computed(() => {
|
||||||
|
const version = rule.value?.groovyScript?.latest?.version;
|
||||||
|
return version === undefined || version === null ? "未创建版本" : String(version);
|
||||||
|
});
|
||||||
|
const scriptContent = computed(() => rule.value?.groovyScript?.latest?.content || "");
|
||||||
|
|
||||||
async function loadStoreScope(): Promise<void> {
|
async function loadStoreScope(): Promise<void> {
|
||||||
if (isGlobalManager.value) {
|
if (isGlobalManager.value) {
|
||||||
@@ -349,5 +386,22 @@ onUnmounted(() => {
|
|||||||
color: var(--td-text-color-secondary);
|
color: var(--td-text-color-secondary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.script-section {
|
||||||
|
|
||||||
|
.script-editor {
|
||||||
|
height: 24rem;
|
||||||
|
min-height: 16rem;
|
||||||
|
background: var(--td-bg-color-page);
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-script {
|
||||||
|
padding: 1rem;
|
||||||
|
color: var(--td-text-color-placeholder);
|
||||||
|
background: var(--td-bg-color-page);
|
||||||
|
border-radius: .5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -2,9 +2,6 @@
|
|||||||
<div v-can="pageAccess" class="point-rule-edit">
|
<div v-can="pageAccess" class="point-rule-edit">
|
||||||
<t-form class="form" label-align="left" label-width="7rem">
|
<t-form class="form" label-align="left" label-width="7rem">
|
||||||
<card-container v-entrance-transition>
|
<card-container v-entrance-transition>
|
||||||
<t-form-item v-if="isGlobalManager" label="门店" required-mark :arrow="isCreateMode" @click="openStorePicker">
|
|
||||||
<span class="picker-text" v-text="selectedStoreName" />
|
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="触发来源" required-mark arrow @click="openTriggerSourcePicker">
|
<t-form-item label="触发来源" required-mark arrow @click="openTriggerSourcePicker">
|
||||||
<span class="picker-text" v-text="getTriggerSourceText(formModel.triggerSourceType)" />
|
<span class="picker-text" v-text="getTriggerSourceText(formModel.triggerSourceType)" />
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
@@ -23,18 +20,28 @@
|
|||||||
</t-form-item>
|
</t-form-item>
|
||||||
</card-container>
|
</card-container>
|
||||||
<card-container v-entrance-transition="70">
|
<card-container v-entrance-transition="70">
|
||||||
<t-form-item label="积分动作" arrow @click="openPointActionPicker">
|
<t-form-item label="规则模式" arrow @click="openRuleModePicker">
|
||||||
<span class="picker-text" v-text="getPointActionText(formModel.pointAction)" />
|
<span class="picker-text" v-text="getRuleModeText(formModel.ruleMode)" />
|
||||||
</t-form-item>
|
|
||||||
<t-form-item label="积分数量" required-mark>
|
|
||||||
<t-input
|
|
||||||
v-model.trim="formModel.pointValueText"
|
|
||||||
type="number"
|
|
||||||
align="right"
|
|
||||||
borderless
|
|
||||||
placeholder="请输入数量"
|
|
||||||
/>
|
|
||||||
</t-form-item>
|
</t-form-item>
|
||||||
|
<template v-if="formModel.ruleMode === 'GROOVY'">
|
||||||
|
<t-form-item label="执行脚本" required-mark arrow @click="openScriptPicker">
|
||||||
|
<span class="picker-text" v-text="selectedScriptText" />
|
||||||
|
</t-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<t-form-item label="积分动作" arrow @click="openPointActionPicker">
|
||||||
|
<span class="picker-text" v-text="getPointActionText(formModel.pointAction)" />
|
||||||
|
</t-form-item>
|
||||||
|
<t-form-item label="积分数量" required-mark>
|
||||||
|
<t-input
|
||||||
|
v-model.trim="formModel.pointValueText"
|
||||||
|
type="number"
|
||||||
|
align="right"
|
||||||
|
borderless
|
||||||
|
placeholder="请输入数量"
|
||||||
|
/>
|
||||||
|
</t-form-item>
|
||||||
|
</template>
|
||||||
</card-container>
|
</card-container>
|
||||||
<card-container v-entrance-transition="140">
|
<card-container v-entrance-transition="140">
|
||||||
<t-form-item label="生效开始" arrow @click="openBeginAtPicker">
|
<t-form-item label="生效开始" arrow @click="openBeginAtPicker">
|
||||||
@@ -57,15 +64,6 @@
|
|||||||
</card-container>
|
</card-container>
|
||||||
</t-form>
|
</t-form>
|
||||||
</div>
|
</div>
|
||||||
<t-popup v-model="storePickerVisible" placement="bottom">
|
|
||||||
<t-picker
|
|
||||||
v-model="storePickerDraft"
|
|
||||||
:columns="storePickerColumns"
|
|
||||||
title="选择门店"
|
|
||||||
@cancel="storePickerVisible = false"
|
|
||||||
@confirm="confirmStorePicker"
|
|
||||||
/>
|
|
||||||
</t-popup>
|
|
||||||
<t-popup v-model="eventPickerVisible" placement="bottom">
|
<t-popup v-model="eventPickerVisible" placement="bottom">
|
||||||
<t-picker
|
<t-picker
|
||||||
v-model="eventPickerDraft"
|
v-model="eventPickerDraft"
|
||||||
@@ -102,6 +100,51 @@
|
|||||||
@confirm="confirmPointActionPicker"
|
@confirm="confirmPointActionPicker"
|
||||||
/>
|
/>
|
||||||
</t-popup>
|
</t-popup>
|
||||||
|
<t-popup v-model="ruleModePickerVisible" placement="bottom">
|
||||||
|
<t-picker
|
||||||
|
v-model="ruleModePickerDraft"
|
||||||
|
:columns="ruleModePickerColumns"
|
||||||
|
title="规则模式"
|
||||||
|
@cancel="ruleModePickerVisible = false"
|
||||||
|
@confirm="confirmRuleModePicker"
|
||||||
|
/>
|
||||||
|
</t-popup>
|
||||||
|
<bottom-popup
|
||||||
|
v-model="scriptPickerVisible"
|
||||||
|
title="选择执行脚本"
|
||||||
|
height="min(80vh, 48rem)"
|
||||||
|
@left-button-click="cancelScriptPicker"
|
||||||
|
@right-button-click="confirmScriptPicker"
|
||||||
|
>
|
||||||
|
<t-collapse
|
||||||
|
v-if="availableScriptList.length"
|
||||||
|
v-model="expandedScriptIdList"
|
||||||
|
class="script-collapse"
|
||||||
|
:expand-mutex="true"
|
||||||
|
>
|
||||||
|
<t-collapse-panel
|
||||||
|
v-for="script in availableScriptList"
|
||||||
|
:key="getScriptId(script)"
|
||||||
|
:value="getScriptId(script)"
|
||||||
|
class="script-panel"
|
||||||
|
placement="bottom"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<t-checkbox
|
||||||
|
:model-value="scriptPickerDraftId === getScriptId(script)"
|
||||||
|
:label="getScriptName(script)"
|
||||||
|
borderless
|
||||||
|
@click.stop
|
||||||
|
@change="selectScript(getScriptId(script))"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<div class="script-description">
|
||||||
|
<markdown-view :content="script.description || '暂无脚本说明'" />
|
||||||
|
</div>
|
||||||
|
</t-collapse-panel>
|
||||||
|
</t-collapse>
|
||||||
|
<t-empty v-else description="暂无可选积分脚本" />
|
||||||
|
</bottom-popup>
|
||||||
<t-popup v-model="beginAtPickerVisible" placement="bottom">
|
<t-popup v-model="beginAtPickerVisible" placement="bottom">
|
||||||
<t-date-time-picker
|
<t-date-time-picker
|
||||||
v-model="beginAtDraftText"
|
v-model="beginAtDraftText"
|
||||||
@@ -127,7 +170,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Time } from "timi-web";
|
import { BottomPopup } from "timi-tdesign-mobile";
|
||||||
|
import { MarkdownView, Time } from "timi-web";
|
||||||
import { useUserStore } from "timi-web-api";
|
import { useUserStore } from "timi-web-api";
|
||||||
import EventAPI from "@/api/EventAPI";
|
import EventAPI from "@/api/EventAPI";
|
||||||
import PointAPI from "@/api/PointAPI";
|
import PointAPI from "@/api/PointAPI";
|
||||||
@@ -135,8 +179,10 @@ import { useCurrentStoreStore } from "@/store/currentStoreStore";
|
|||||||
import { useNavBarStore } from "@/store/navBarStore";
|
import { useNavBarStore } from "@/store/navBarStore";
|
||||||
import type { EventItem } from "@/types/EventItem";
|
import type { EventItem } from "@/types/EventItem";
|
||||||
import type {
|
import type {
|
||||||
|
GroovyScript,
|
||||||
PointAction,
|
PointAction,
|
||||||
PointRule,
|
PointRule,
|
||||||
|
PointRuleMode,
|
||||||
PointRuleStatus,
|
PointRuleStatus,
|
||||||
PointRuleTriggerSourceType
|
PointRuleTriggerSourceType
|
||||||
} from "@/types/Point";
|
} from "@/types/Point";
|
||||||
@@ -170,30 +216,24 @@ const isSaving = ref(false);
|
|||||||
const formModel = reactive({
|
const formModel = reactive({
|
||||||
id: "",
|
id: "",
|
||||||
storeId: "",
|
storeId: "",
|
||||||
|
ruleMode: "SIMPLE" as PointRuleMode,
|
||||||
triggerSourceType: "EVENT_RECORD" as PointRuleTriggerSourceType,
|
triggerSourceType: "EVENT_RECORD" as PointRuleTriggerSourceType,
|
||||||
triggerRefId: "",
|
triggerRefId: "",
|
||||||
name: "",
|
name: "",
|
||||||
status: "DRAFT" as PointRuleStatus,
|
status: "DRAFT" as PointRuleStatus,
|
||||||
pointAction: "EARN" as PointAction,
|
pointAction: "EARN" as PointAction,
|
||||||
pointValueText: "",
|
pointValueText: "",
|
||||||
|
groovyScriptId: "",
|
||||||
beginAtText: "",
|
beginAtText: "",
|
||||||
endAtText: "",
|
endAtText: "",
|
||||||
exclusive: false,
|
exclusive: false,
|
||||||
remark: ""
|
remark: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
// ---------- 门店与事件 ----------
|
// ---------- 事件与脚本 ----------
|
||||||
|
|
||||||
const eventList = ref<EventItem[]>([]);
|
const eventList = ref<EventItem[]>([]);
|
||||||
const selectedStoreName = computed(() => currentStoreStore.storeList.find(item => item.id === formModel.storeId)?.name || "请选择门店");
|
|
||||||
const selectedEventName = computed(() => eventList.value.find(item => item.id === formModel.triggerRefId)?.name || "请选择触发事件");
|
const selectedEventName = computed(() => eventList.value.find(item => item.id === formModel.triggerRefId)?.name || "请选择触发事件");
|
||||||
const storePickerVisible = ref(false);
|
|
||||||
const storePickerDraft = ref<PickerValue[]>([]);
|
|
||||||
const storePickerColumns = computed(() => [
|
|
||||||
currentStoreStore.storeList
|
|
||||||
.map(item => ({ label: item.name || item.code || "未命名门店", value: item.id || "" }))
|
|
||||||
.filter(item => item.value)
|
|
||||||
]);
|
|
||||||
const eventPickerVisible = ref(false);
|
const eventPickerVisible = ref(false);
|
||||||
const eventPickerDraft = ref<PickerValue[]>([]);
|
const eventPickerDraft = ref<PickerValue[]>([]);
|
||||||
const eventPickerColumns = computed(() => [
|
const eventPickerColumns = computed(() => [
|
||||||
@@ -202,28 +242,15 @@ const eventPickerColumns = computed(() => [
|
|||||||
.filter(item => item.value)
|
.filter(item => item.value)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function openStorePicker(): void {
|
const scriptList = ref<GroovyScript[]>([]);
|
||||||
if (!isCreateMode.value || !storePickerColumns.value[0]?.length) {
|
const selectedScript = computed(() => scriptList.value.find(item => item.id === formModel.groovyScriptId));
|
||||||
return;
|
const availableScriptList = computed(() => scriptList.value.filter(item => item.id));
|
||||||
|
const selectedScriptText = computed(() => {
|
||||||
|
if (!selectedScript.value) {
|
||||||
|
return "请选择执行脚本";
|
||||||
}
|
}
|
||||||
storePickerDraft.value = [formModel.storeId];
|
return selectedScript.value.name || selectedScript.value.code || "未命名脚本";
|
||||||
storePickerVisible.value = true;
|
});
|
||||||
}
|
|
||||||
|
|
||||||
async function confirmStorePicker(value: Array<string | number>): Promise<void> {
|
|
||||||
const storeId = String(value[0] || "");
|
|
||||||
if (storeId && storeId !== formModel.storeId) {
|
|
||||||
formModel.storeId = storeId;
|
|
||||||
formModel.triggerRefId = "";
|
|
||||||
try {
|
|
||||||
await loadEventList();
|
|
||||||
} catch {
|
|
||||||
eventList.value = [];
|
|
||||||
Message.error("触发事件加载失败");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
storePickerVisible.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openEventPicker(): void {
|
function openEventPicker(): void {
|
||||||
if (!eventPickerColumns.value[0]?.length) {
|
if (!eventPickerColumns.value[0]?.length) {
|
||||||
@@ -245,14 +272,10 @@ function confirmEventPicker(value: Array<string | number>): void {
|
|||||||
async function prepareStoreScope(): Promise<void> {
|
async function prepareStoreScope(): Promise<void> {
|
||||||
if (isGlobalManager.value) {
|
if (isGlobalManager.value) {
|
||||||
await currentStoreStore.loadStoreList();
|
await currentStoreStore.loadStoreList();
|
||||||
const queryStoreId = String(route.query.storeId || "");
|
} else {
|
||||||
formModel.storeId = currentStoreStore.storeList.some(item => item.id === queryStoreId)
|
await currentStoreStore.loadLoginStore();
|
||||||
? queryStoreId
|
|
||||||
: currentStoreStore.currentStoreId;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const store = await currentStoreStore.loadLoginStore();
|
formModel.storeId = currentStoreStore.currentStoreId;
|
||||||
formModel.storeId = store?.id || "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadEventList(): Promise<void> {
|
async function loadEventList(): Promise<void> {
|
||||||
@@ -265,6 +288,10 @@ async function loadEventList(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadScriptList(): Promise<void> {
|
||||||
|
scriptList.value = await PointAPI.ruleScriptList();
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- 规则选项 ----------
|
// ---------- 规则选项 ----------
|
||||||
|
|
||||||
const statusOptions: Array<{ label: string; value: PointRuleStatus }> = [
|
const statusOptions: Array<{ label: string; value: PointRuleStatus }> = [
|
||||||
@@ -286,9 +313,27 @@ const pointActionOptions: Array<{ label: string; value: PointAction }> = [
|
|||||||
{ label: "发放积分", value: "EARN" },
|
{ label: "发放积分", value: "EARN" },
|
||||||
{ label: "消耗积分", value: "CONSUME" }
|
{ label: "消耗积分", value: "CONSUME" }
|
||||||
];
|
];
|
||||||
|
const ruleModeOptions: Array<{ label: string; value: PointRuleMode }> = [
|
||||||
|
{ label: "简单规则", value: "SIMPLE" },
|
||||||
|
{ label: "脚本规则", value: "GROOVY" }
|
||||||
|
];
|
||||||
const pointActionPickerVisible = ref(false);
|
const pointActionPickerVisible = ref(false);
|
||||||
const pointActionPickerDraft = ref<PickerValue[]>([]);
|
const pointActionPickerDraft = ref<PickerValue[]>([]);
|
||||||
const pointActionPickerColumns = [pointActionOptions];
|
const pointActionPickerColumns = [pointActionOptions];
|
||||||
|
const ruleModePickerVisible = ref(false);
|
||||||
|
const ruleModePickerDraft = ref<PickerValue[]>([]);
|
||||||
|
const ruleModePickerColumns = [ruleModeOptions];
|
||||||
|
const scriptPickerVisible = ref(false);
|
||||||
|
const scriptPickerDraftId = ref("");
|
||||||
|
const expandedScriptIdList = ref<Array<string | number>>([]);
|
||||||
|
|
||||||
|
function getScriptId(script: GroovyScript): string {
|
||||||
|
return script.id || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getScriptName(script: GroovyScript): string {
|
||||||
|
return script.name || script.code || "未命名脚本";
|
||||||
|
}
|
||||||
|
|
||||||
function openStatusPicker(): void {
|
function openStatusPicker(): void {
|
||||||
statusPickerDraft.value = [formModel.status];
|
statusPickerDraft.value = [formModel.status];
|
||||||
@@ -332,6 +377,52 @@ function confirmPointActionPicker(value: Array<string | number>): void {
|
|||||||
pointActionPickerVisible.value = false;
|
pointActionPickerVisible.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openRuleModePicker(): void {
|
||||||
|
ruleModePickerDraft.value = [formModel.ruleMode];
|
||||||
|
ruleModePickerVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmRuleModePicker(value: Array<string | number>): void {
|
||||||
|
const ruleMode = value[0];
|
||||||
|
if (ruleMode !== "SIMPLE" && ruleMode !== "GROOVY") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
formModel.ruleMode = ruleMode;
|
||||||
|
if (ruleMode === "SIMPLE") {
|
||||||
|
formModel.groovyScriptId = "";
|
||||||
|
} else {
|
||||||
|
formModel.pointValueText = "";
|
||||||
|
}
|
||||||
|
ruleModePickerVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openScriptPicker(): void {
|
||||||
|
if (!availableScriptList.value.length) {
|
||||||
|
Message.warning("暂无可选积分脚本");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
scriptPickerDraftId.value = formModel.groovyScriptId;
|
||||||
|
expandedScriptIdList.value = formModel.groovyScriptId ? [formModel.groovyScriptId] : [];
|
||||||
|
scriptPickerVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectScript(scriptId: string): void {
|
||||||
|
if (scriptId && availableScriptList.value.some(item => item.id === scriptId)) {
|
||||||
|
scriptPickerDraftId.value = scriptId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelScriptPicker(): void {
|
||||||
|
scriptPickerVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmScriptPicker(): void {
|
||||||
|
if (scriptPickerDraftId.value && availableScriptList.value.some(item => item.id === scriptPickerDraftId.value)) {
|
||||||
|
formModel.groovyScriptId = scriptPickerDraftId.value;
|
||||||
|
}
|
||||||
|
scriptPickerVisible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
function getStatusText(status: PointRuleStatus): string {
|
function getStatusText(status: PointRuleStatus): string {
|
||||||
return statusOptions.find(item => item.value === status)?.label || "草稿";
|
return statusOptions.find(item => item.value === status)?.label || "草稿";
|
||||||
}
|
}
|
||||||
@@ -344,6 +435,10 @@ function getPointActionText(action: PointAction): string {
|
|||||||
return action === "CONSUME" ? "消耗积分" : "发放积分";
|
return action === "CONSUME" ? "消耗积分" : "发放积分";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getRuleModeText(ruleMode: PointRuleMode): string {
|
||||||
|
return ruleMode === "GROOVY" ? "脚本规则" : "简单规则";
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- 生效时间 ----------
|
// ---------- 生效时间 ----------
|
||||||
|
|
||||||
const minimumDateTimeText = "1970-01-01 00:00";
|
const minimumDateTimeText = "1970-01-01 00:00";
|
||||||
@@ -405,7 +500,7 @@ function resolveTimestamp(value: string): number | undefined {
|
|||||||
|
|
||||||
function validateForm(): boolean {
|
function validateForm(): boolean {
|
||||||
if (!formModel.storeId) {
|
if (!formModel.storeId) {
|
||||||
Message.warning("请选择门店");
|
Message.warning("当前门店不可用");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!formModel.name.trim()) {
|
if (!formModel.name.trim()) {
|
||||||
@@ -416,7 +511,12 @@ function validateForm(): boolean {
|
|||||||
Message.warning("请选择触发事件");
|
Message.warning("请选择触发事件");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!resolvePositiveInteger(formModel.pointValueText)) {
|
if (formModel.ruleMode === "GROOVY") {
|
||||||
|
if (!formModel.groovyScriptId) {
|
||||||
|
Message.warning("请选择执行脚本");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!resolvePositiveInteger(formModel.pointValueText)) {
|
||||||
Message.warning("积分数量必须是正整数");
|
Message.warning("积分数量必须是正整数");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -438,14 +538,14 @@ async function saveRule(): Promise<void> {
|
|||||||
const payload: PointRule = {
|
const payload: PointRule = {
|
||||||
id: formModel.id || undefined,
|
id: formModel.id || undefined,
|
||||||
storeId: formModel.storeId,
|
storeId: formModel.storeId,
|
||||||
ruleMode: "SIMPLE",
|
ruleMode: formModel.ruleMode,
|
||||||
triggerSourceType: formModel.triggerSourceType,
|
triggerSourceType: formModel.triggerSourceType,
|
||||||
triggerRefType: formModel.triggerSourceType === "EVENT_RECORD" ? "EVENT" : undefined,
|
triggerRefType: formModel.triggerSourceType === "EVENT_RECORD" ? "EVENT" : undefined,
|
||||||
triggerRefId: formModel.triggerSourceType === "EVENT_RECORD" ? formModel.triggerRefId : undefined,
|
triggerRefId: formModel.triggerSourceType === "EVENT_RECORD" ? formModel.triggerRefId : undefined,
|
||||||
name: formModel.name.trim(),
|
name: formModel.name.trim(),
|
||||||
status: formModel.status,
|
status: formModel.status,
|
||||||
pointValue: resolvePointValue() ?? null,
|
pointValue: formModel.ruleMode === "SIMPLE" ? resolvePointValue() ?? null : null,
|
||||||
groovyScriptId: undefined,
|
groovyScriptId: formModel.ruleMode === "GROOVY" ? formModel.groovyScriptId : undefined,
|
||||||
beginAt: resolveTimestamp(formModel.beginAtText),
|
beginAt: resolveTimestamp(formModel.beginAtText),
|
||||||
endAt: resolveTimestamp(formModel.endAtText),
|
endAt: resolveTimestamp(formModel.endAtText),
|
||||||
exclusive: formModel.exclusive,
|
exclusive: formModel.exclusive,
|
||||||
@@ -469,17 +569,23 @@ async function saveRule(): Promise<void> {
|
|||||||
|
|
||||||
async function fillForm(): Promise<void> {
|
async function fillForm(): Promise<void> {
|
||||||
await prepareStoreScope();
|
await prepareStoreScope();
|
||||||
|
await loadScriptList();
|
||||||
if (!isCreateMode.value) {
|
if (!isCreateMode.value) {
|
||||||
const current = await PointAPI.ruleDetail(routeId.value);
|
const current = await PointAPI.ruleDetail(routeId.value);
|
||||||
|
if (current.groovyScript?.id && !scriptList.value.some(item => item.id === current.groovyScript?.id)) {
|
||||||
|
scriptList.value.push(current.groovyScript);
|
||||||
|
}
|
||||||
Object.assign(formModel, {
|
Object.assign(formModel, {
|
||||||
id: current.id || "",
|
id: current.id || "",
|
||||||
storeId: current.storeId || formModel.storeId,
|
storeId: current.storeId || formModel.storeId,
|
||||||
|
ruleMode: current.ruleMode || "SIMPLE",
|
||||||
triggerSourceType: current.triggerSourceType || "EVENT_RECORD",
|
triggerSourceType: current.triggerSourceType || "EVENT_RECORD",
|
||||||
triggerRefId: current.triggerRefId || "",
|
triggerRefId: current.triggerRefId || "",
|
||||||
name: current.name || "",
|
name: current.name || "",
|
||||||
status: current.status === "ACTIVE" ? "ACTIVE" : current.status === "INACTIVE" ? "INACTIVE" : "DRAFT",
|
status: current.status === "ACTIVE" ? "ACTIVE" : current.status === "INACTIVE" ? "INACTIVE" : "DRAFT",
|
||||||
pointAction: current.pointValue !== undefined && current.pointValue !== null && current.pointValue < 0 ? "CONSUME" : "EARN",
|
pointAction: current.pointValue !== undefined && current.pointValue !== null && current.pointValue < 0 ? "CONSUME" : "EARN",
|
||||||
pointValueText: current.pointValue === undefined || current.pointValue === null ? "" : String(Math.abs(current.pointValue)),
|
pointValueText: current.pointValue === undefined || current.pointValue === null ? "" : String(Math.abs(current.pointValue)),
|
||||||
|
groovyScriptId: current.groovyScriptId || "",
|
||||||
beginAtText: current.beginAt ? Time.toDateTime(current.beginAt) : "",
|
beginAtText: current.beginAt ? Time.toDateTime(current.beginAt) : "",
|
||||||
endAtText: current.endAt ? Time.toDateTime(current.endAt) : "",
|
endAtText: current.endAt ? Time.toDateTime(current.endAt) : "",
|
||||||
exclusive: current.exclusive ?? false,
|
exclusive: current.exclusive ?? false,
|
||||||
@@ -543,6 +649,13 @@ onUnmounted(() => navBarStore.clearRight());
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.script-collapse {
|
||||||
|
|
||||||
|
:deep(.t-cell) {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
:global(html.theme-dark) {
|
:global(html.theme-dark) {
|
||||||
.point-rule-edit {
|
.point-rule-edit {
|
||||||
.form {
|
.form {
|
||||||
@@ -554,6 +667,10 @@ onUnmounted(() => navBarStore.clearRight());
|
|||||||
color: #94a3b8;
|
color: #94a3b8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.script-description {
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -264,10 +264,7 @@ function getStatusTheme(status?: PointRuleStatus): "default" | "success" | "warn
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function toCreate(): Promise<void> {
|
async function toCreate(): Promise<void> {
|
||||||
await router.push({
|
await router.push({ name: "PointRuleCreate" });
|
||||||
name: "PointRuleCreate",
|
|
||||||
query: currentStoreStore.currentStoreId ? { storeId: currentStoreStore.currentStoreId } : undefined
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toSort(): Promise<void> {
|
async function toSort(): Promise<void> {
|
||||||
|
|||||||
@@ -12,6 +12,23 @@ export type PointRuleTriggerSourceType = "EVENT_RECORD" | "SALES_ORDER_PAID";
|
|||||||
/** 触发对象筛选类型 */
|
/** 触发对象筛选类型 */
|
||||||
export type PointRuleTriggerRefType = "EVENT";
|
export type PointRuleTriggerRefType = "EVENT";
|
||||||
|
|
||||||
|
export type GroovyScript = {
|
||||||
|
bizType?: "GAO_POINT_RULE";
|
||||||
|
code?: string;
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
publishedVersionId?: string;
|
||||||
|
latest?: GroovyScriptVersion | null;
|
||||||
|
} & Model;
|
||||||
|
|
||||||
|
export type GroovyScriptVersion = {
|
||||||
|
scriptId?: string;
|
||||||
|
version?: number;
|
||||||
|
content?: string;
|
||||||
|
checksum?: string;
|
||||||
|
} & Model;
|
||||||
|
|
||||||
export type PointLedgerChangeType = "EARN" | "CONSUME" | "REVOKE" | "ADJUST";
|
export type PointLedgerChangeType = "EARN" | "CONSUME" | "REVOKE" | "ADJUST";
|
||||||
export type PointLedgerDirection = "INCREASE" | "DECREASE";
|
export type PointLedgerDirection = "INCREASE" | "DECREASE";
|
||||||
export type PointAction = Extract<PointLedgerChangeType, "EARN" | "CONSUME">;
|
export type PointAction = Extract<PointLedgerChangeType, "EARN" | "CONSUME">;
|
||||||
@@ -81,6 +98,7 @@ export type PointRule = {
|
|||||||
priority?: number;
|
priority?: number;
|
||||||
exclusive?: boolean;
|
exclusive?: boolean;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
|
groovyScript?: GroovyScript | null;
|
||||||
} & Model;
|
} & Model;
|
||||||
|
|
||||||
export type PointAdjustRequest = {
|
export type PointAdjustRequest = {
|
||||||
|
|||||||
Reference in New Issue
Block a user