diff --git a/im/MIGRATION-VUE3.md b/im/MIGRATION-VUE3.md deleted file mode 100644 index 665233812..000000000 --- a/im/MIGRATION-VUE3.md +++ /dev/null @@ -1,42 +0,0 @@ -# IM 客服端 Vue 3 迁移说明 - -## 技术栈 - -- Vue 3.5 + Vue Router 4 + Pinia 4 -- Element Plus(替代 element-ui) -- 自研右键菜单 `src/plugins/contextmenu.js`(替代 vue-contextmenujs) -- `vue-virtual-scroller@3`、`vue-cropper@1.x`、`vue-prism-editor@2.0.0-alpha.2` - -## 组合式 API 迁移(2026-10 完成) - -全部 53 个 `.vue` 组件已从 Options API 转换为 ` diff --git a/manager/src/views/promotions/full-discount/full-rate.vue b/manager/src/views/promotions/full-discount/full-rate.vue new file mode 100644 index 000000000..ae446ed74 --- /dev/null +++ b/manager/src/views/promotions/full-discount/full-rate.vue @@ -0,0 +1,14 @@ + + + + + + diff --git a/manager/src/views/statistics/marketing.vue b/manager/src/views/statistics/marketing.vue index c7d497f45..94347e8e0 100644 --- a/manager/src/views/statistics/marketing.vue +++ b/manager/src/views/statistics/marketing.vue @@ -57,7 +57,9 @@ const activityMetrics = ref([ { key: "couponActiveNum", label: "进行中优惠券" }, { key: "seckillActiveNum", label: "进行中秒杀" }, { key: "pintuanActiveNum", label: "进行中拼团" }, - { key: "fullDiscountActiveNum", label: "进行中满减" }, + { key: "fullMinusActiveNum", label: "进行中满减" }, + { key: "fullRateActiveNum", label: "进行中满折" }, + { key: "fullGiftActiveNum", label: "进行中满赠" }, { key: "kanjiaActiveNum", label: "进行中砍价商品" }, { key: "pointsGoodsActiveNum", label: "进行中积分商品" }, ]); @@ -128,7 +130,7 @@ h4 { } .metric-grid { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(4, 1fr); gap: 16px; } .metric-item { diff --git a/seller/scripts/test-seller-navigation.mjs b/seller/scripts/test-seller-navigation.mjs index 52f6b0ec8..a7c13ae58 100644 --- a/seller/scripts/test-seller-navigation.mjs +++ b/seller/scripts/test-seller-navigation.mjs @@ -45,3 +45,16 @@ test('搜索清空与空权限账号正确处理', () => { assert.deepEqual(searchNavigation(flattenNavigation([]), ''), []); assert.equal(findNavigationItem(items, 'unknown-detail'), null); }); +test('满减/满折/满赠详情页按 activityType 定位对应左侧菜单', () => { + const promoMenus = [ + { name: 'promotion', title: '营销', children: [{ title: '店铺活动', children: [ + { name: 'full-discount', path: 'full-discount', title: '满减活动', component }, + { name: 'store-full-rate', path: 'full-rate', title: '满折活动', component }, + { name: 'store-full-gift', path: 'full-gift', title: '满赠活动', component }, + ] }] }, + ]; + const promoItems = flattenNavigation(promoMenus); + assert.equal(findNavigationItem(promoItems, 'full-discount-detail', { activityType: 'FULL_GIFT' })?.name, 'store-full-gift'); + assert.equal(findNavigationItem(promoItems, 'full-discount-detail', { activityType: 'FULL_RATE' })?.name, 'store-full-rate'); + assert.equal(findNavigationItem(promoItems, 'full-discount-detail', { activityType: 'FULL_MINUS' })?.name, 'full-discount'); +}); diff --git a/seller/src/libs/seller-navigation.js b/seller/src/libs/seller-navigation.js index bd229792b..bb0b1881e 100644 --- a/seller/src/libs/seller-navigation.js +++ b/seller/src/libs/seller-navigation.js @@ -39,10 +39,31 @@ export function flattenNavigation(menus) { )); } +const FULL_DISCOUNT_DETAIL_MENUS = { + FULL_GIFT: ["store-full-gift", "full-gift", "full-discount", "full-cut"], + FULL_RATE: ["store-full-rate", "full-rate", "full-discount", "full-cut"], + FULL_MINUS: ["full-discount", "full-cut"], +}; + +function resolveDetailMenuCandidates(routeName, routeQuery = {}) { + if (routeName === "full-discount-detail") { + const type = routeQuery?.activityType; + if (type && FULL_DISCOUNT_DETAIL_MENUS[type]) { + return FULL_DISCOUNT_DETAIL_MENUS[type]; + } + } + return DETAIL_MENUS[routeName] || []; +} + /** 优先精确匹配菜单,再匹配详情页业务入口;无授权入口时返回空,不沿用旧账号栏目。 */ -export function findNavigationItem(items, routeName) { - return items.find((item) => item.name === routeName) || - (DETAIL_MENUS[routeName] || []).map((name) => items.find((item) => item.name === name)).find(Boolean) || null; +export function findNavigationItem(items, routeName, routeQuery = {}) { + return ( + items.find((item) => item.name === routeName) || + resolveDetailMenuCandidates(routeName, routeQuery) + .map((name) => items.find((item) => item.name === name || item.path === name)) + .find(Boolean) || + null + ); } /** 按菜单、分组和栏目检索,空格分隔的关键词须全部匹配。 */ diff --git a/seller/src/libs/util.js b/seller/src/libs/util.js index dc47b0bb8..0fd099f13 100644 --- a/seller/src/libs/util.js +++ b/seller/src/libs/util.js @@ -503,6 +503,15 @@ util.initRouterNode = function (routers, data) { } const menu = Object.assign({}, item); const hasChildren = item.children && item.children.length > 0; + if ( + !hasChildren && + (menu.frontRoute === "promotion/full-discount/full-discount" || + menu.name === "full-discount" || + menu.name === "full-cut") && + menu.title === "满额活动" + ) { + menu.title = "满减活动"; + } if (hasChildren) { menu.children = []; diff --git a/seller/src/views/Main.vue b/seller/src/views/Main.vue index fd67dad83..2af63aaa1 100644 --- a/seller/src/views/Main.vue +++ b/seller/src/views/Main.vue @@ -128,10 +128,19 @@ const load = ref(false); const loading = computed(() => appStore.loading); const unreadMessageCount = computed(() => appStore.unreadMessageCount); const pageTagsList = computed(() => appStore.storeOpenedList); -const currentPageTitle = computed(() => route.meta.title || pageTagsList.value.find((item) => item.name === route.name)?.title || '工作台'); +const currentPageTitle = computed(() => { + if (route.name === "full-discount-detail") { + const type = route.query?.activityType; + const isDetail = Boolean(route.query?.id); + if (type === "FULL_GIFT") return isDetail ? "满赠活动详情" : "添加满赠活动"; + if (type === "FULL_RATE") return isDetail ? "满折活动详情" : "添加满折活动"; + return isDetail ? "满减活动详情" : "添加满减活动"; + } + return route.meta.title || pageTagsList.value.find((item) => item.name === route.name)?.title || '工作台'; +}); const navigationItems = computed(() => flattenNavigation(appStore.navigationMenus || [])); const currentPathParts = computed(() => { - const matched = findNavigationItem(navigationItems.value, route.name); + const matched = findNavigationItem(navigationItems.value, route.name, route.query); const rawParts = matched ? [ matched.topTitle, diff --git a/seller/src/views/main-components/shrinkable-menu/shrinkable-menu.vue b/seller/src/views/main-components/shrinkable-menu/shrinkable-menu.vue index 3121ba8d3..edce49c58 100644 --- a/seller/src/views/main-components/shrinkable-menu/shrinkable-menu.vue +++ b/seller/src/views/main-components/shrinkable-menu/shrinkable-menu.vue @@ -44,7 +44,7 @@ const route = useRoute(); const router = useRouter(); const closedSections = ref([]); const allItems = computed(() => flattenNavigation(appStore.navigationMenus).filter((item) => router.hasRoute(item.name))); -const activeItem = computed(() => findNavigationItem(allItems.value, route.name)); +const activeItem = computed(() => findNavigationItem(allItems.value, route.name, route.query)); const sections = computed(() => appStore.navigationMenus.map((top) => ({ ...top, items: allItems.value.filter((item) => item.topName === top.name), })).filter((top) => top.items.length)); diff --git a/seller/src/views/promotion/coupon/coupon.vue b/seller/src/views/promotion/coupon/coupon.vue index 3c122800f..6282f264e 100644 --- a/seller/src/views/promotion/coupon/coupon.vue +++ b/seller/src/views/promotion/coupon/coupon.vue @@ -55,7 +55,7 @@ 添加优惠券 - 添加活动优惠券 + 添加活动优惠券 批量关闭 优惠券领取记录 diff --git a/seller/src/views/promotion/full-discount/full-discount-add.vue b/seller/src/views/promotion/full-discount/full-discount-add.vue index 6f10407ca..4cea9ec45 100644 --- a/seller/src/views/promotion/full-discount/full-discount-add.vue +++ b/seller/src/views/promotion/full-discount/full-discount-add.vue @@ -1,46 +1,71 @@ + - 基本信息 + 基本信息({{ activityTypeLabel }}) {{ form.promotionName }} - + + {{ activityTypeLabel }} + + {{ form.startTime }} ~ {{ form.endTime }} + + {{ form.description || "-" }} + - 优惠设置 + 阶梯优惠设置(共 {{ form.ruleList.length }} 级阶梯) - - 满 {{ form.fullMoney }} 元 - - {{ form.discountType === "fullMinusFlag" ? "减现金" : "打折" }} - - - 减 {{ form.fullMinus }} 元 - - {{ form.fullRate }} 折 - - {{ form.scopeType === "ALL" ? "全品类" : "指定商品" }} - - - - - {{ gift }} - + + + + 满 {{ tier.fullMoney }} 元,立减现金 + {{ tier.fullMinus }} 元 - 无 + + 满 {{ tier.fullMoney }} 元,立享 + {{ tier.fullRate }} 折 + + + + 满 {{ tier.fullMoney }} 元,赠送以下商品: + + + + {{ formatGiftDisplayName(g) }} × {{ g.num || 1 }}件 + + + + + + {{ form.scopeType === "ALL" ? "全品类" : "指定商品" }} - {{ - row.goodsName - }} - - - - - - + + {{ row.goodsName }} + - - {{ row?.sn || "-" }} - + {{ row?.sn || "-" }} - - {{ row?.simpleSpecs || "-" }} - + {{ row?.simpleSpecs || "-" }} - {{ $filters.unitPrice(row.price, "¥") }} + {{ $filters.unitPrice(row.price, "¥") }} + @@ -106,20 +109,23 @@ - 基本信息 + 基本信息({{ activityTypeLabel }}) + + {{ activityTypeLabel }} + + + + - 优惠设置 + + 阶梯优惠设置(最多支持 3 级阶梯) + + 添加阶梯({{ form.ruleList.length }}/3) + + - - - - 消费达到当前金额可以参与优惠 - - - - 减现金 - 打折 - - - - - - - - 优惠折扣为0-10之间数字,可有一位小数 - - - 免邮费 - 送优惠券 - 送赠品 - 送积分 - - - - - - - - - - - - 电子卡券赠品支付后自动发卡;卡池不足时仅取消赠品子单,不影响主订单。 - - - - - - - - 全品类 - 指定商品 - - - - - 选择商品 - - 批量删除 + + + 第 {{ tierIdx + 1 }} 级阶梯 + + 删除此阶梯 + + + + + 满 + + 元 + 须大于上一级阶梯门槛金额 + + + + + 减 + + 元 + 须小于当前阶梯门槛,且随阶梯递增 + + + + + 打 + + 折 + 0.1~9.9 折,阶梯越高折扣数值须越小(如 9折 → 8折 → 7折) + + + + + + - - + - {{ - row.goodsName - }} - - - - - - + {{ formatGiftDisplayName(row) }} - + + {{ row?.simpleSpecs || "-" }} + + - {{ row?.sn || "-" }} + + {{ $filters.unitPrice(row.price, "¥") }} + + - - + - {{ row?.simpleSpecs || "-" }} + - - - - {{ $filters.unitPrice(row.price, "¥") }} - - - - - {{ row.quantity }} - - - + - 删除 + 删除 - - + + + + + + 全品类 + 指定商品 + + + 同一时段内全品类活动互斥;指定商品活动只要所选商品不重叠即可同时开启 + + + + + + 选择商品 + + 批量删除 + + + + + + + + + + {{ row.goodsName }} + + + + + + {{ row?.sn || "-" }} + + + {{ row?.simpleSpecs || "-" }} + + + + + {{ $filters.unitPrice(row.price, "¥") }} + + + + + + {{ row.quantity }} + + + + + 删除 + + + + @@ -345,14 +376,10 @@ import { computed, onMounted, ref } from "vue"; import { useRoute, useRouter } from "vue-router"; import { - getShopCouponList, getFullDiscountById, newFullDiscount, editFullDiscount, } from "@/api/promotion"; -import { getGoodsSkuListDataSeller } from "@/api/goods"; -import { regular } from "@/utils"; -import { goodsTypeLabel } from "@/constants/goodsType"; import { Message, Modal } from "@/utils/message"; import { unixToDate } from "@/utils/filters"; @@ -361,57 +388,46 @@ defineOptions({ name: "add-full-discount" }); const route = useRoute(); const router = useRouter(); -const checkWeight = (rule, value, callback) => { - if (!value && typeof value !== "number") { - callback(new Error("优惠门槛不能为空")); - } else if (!regular.money.test(value)) { - callback(new Error("请输入正整数或者两位小数")); - } else if (parseFloat(value) > 99999999) { - callback(new Error("优惠门槛设置超过上限值")); - } else { - callback(); +const id = ref(route.query.id); +const initialActivityType = String(route.query.activityType || "FULL_MINUS"); + +function createEmptyTier(activityType) { + if (activityType === "FULL_RATE") { + return { fullMoney: 100, fullRate: 9.0 }; } -}; -const checkDiscount = (rule, value, callback) => { - if (value === null || value === undefined || value === "") { - callback(new Error("请填写优惠折扣")); - } else if (value < 0.1 || value > 9.9) { - callback(new Error("请输入0-10的数字,可有一位小数")); - } else { - callback(); + if (activityType === "FULL_GIFT") { + return { + fullMoney: 100, + giftItems: [], + }; } -}; + return { fullMoney: 100, fullMinus: 10 }; +} const form = ref({ - discountType: "fullMinusFlag", + activityType: initialActivityType, scopeType: "ALL", promotionGoodsList: [], promotionStatus: "NEW", + description: "", + ruleList: [createEmptyTier(initialActivityType)], }); -const id = ref(route.query.id); + const submitLoading = ref(false); const selectedGoods = ref([]); -const couponList = ref([]); -const giftList = ref([]); -const giftLoading = ref(false); -const couponLoading = ref(false); +const pickerTargetTierIndex = ref(null); + const formRule = { promotionName: [{ required: true, message: "活动名称不能为空" }], rangeTime: [{ required: true, message: "请选择活动时间" }], - fullMoney: [{ required: true, validator: checkWeight }], - fullMinus: [ - { required: true, message: "请填写优惠金额" }, - { pattern: regular.money, message: "请输入正确金额" }, - ], - fullRate: [{ required: true, validator: checkDiscount }], }; + const options = { disabledDate(date) { return date && date.valueOf() < Date.now() - 86400000; }, }; -// formRef:模板 ref 改名避免与顶层变量互相覆盖 const formRef = ref(null); const liliDialogRef = ref(null); @@ -419,23 +435,16 @@ const fieldDisabled = computed(() => { return !!id.value && form.value.promotionStatus !== "NEW"; }); -const extraGifts = computed(() => { - const list = []; - if (form.value.freeFreightFlag) { - list.push("免邮费"); - } - if (form.value.couponFlag) { - const coupon = couponList.value.find((item) => item.id === form.value.couponId); - list.push(`送优惠券${coupon ? `(${coupon.couponName})` : ""}`); - } - if (form.value.giftFlag) { - const gift = giftList.value.find((item) => item.id === form.value.giftId); - list.push(`送赠品${gift ? `(${gift.goodsName})` : ""}`); - } - if (form.value.pointFlag) { - list.push(`送积分(${form.value.point})`); - } - return list; +const activityTypeLabel = computed(() => { + if (form.value.activityType === "FULL_RATE") return "满折活动"; + if (form.value.activityType === "FULL_GIFT") return "满赠活动"; + return "满减活动"; +}); + +const activityTagType = computed(() => { + if (form.value.activityType === "FULL_RATE") return "warning"; + if (form.value.activityType === "FULL_GIFT") return "success"; + return "danger"; }); onMounted(() => { @@ -445,72 +454,225 @@ onMounted(() => { const now = new Date(); form.value.rangeTime = [now, new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)]; } - getCouponList(); - getGiftList(); }); -function giftOptionLabel(item) { - if (!item) return ""; - const typeLabel = - item.goodsType === "E_COUPON" ? ` [${goodsTypeLabel(item.goodsType)}]` : ""; - const stock = - item.goodsType === "E_COUPON" && item.quantity != null - ? ` · 库存${item.quantity}` - : ""; - return `${item.goodsName || ""}${typeLabel}${stock}`; +function addTier() { + if (form.value.ruleList.length >= 3) return; + const lastTier = form.value.ruleList[form.value.ruleList.length - 1]; + const nextMoney = lastTier && lastTier.fullMoney ? Number(lastTier.fullMoney) + 100 : 200; + if (form.value.activityType === "FULL_RATE") { + const prevRate = lastTier && lastTier.fullRate ? Number(lastTier.fullRate) : 9.0; + const nextRate = Math.max(0.1, Number((prevRate - 1.0).toFixed(1))); + form.value.ruleList.push({ fullMoney: nextMoney, fullRate: nextRate }); + } else if (form.value.activityType === "FULL_GIFT") { + form.value.ruleList.push({ + fullMoney: nextMoney, + giftItems: [], + }); + } else { + const prevMinus = lastTier && lastTier.fullMinus ? Number(lastTier.fullMinus) : 10; + form.value.ruleList.push({ fullMoney: nextMoney, fullMinus: prevMinus + 10 }); + } } + +function removeTier(index) { + if (form.value.ruleList.length <= 1) return; + form.value.ruleList.splice(index, 1); +} + +function openGiftSkuPicker(tierIdx) { + pickerTargetTierIndex.value = tierIdx; + const tier = form.value.ruleList[tierIdx]; + const currentGifts = ((tier && tier.giftItems) || []) + .filter((g) => g && g.skuId) + .map((g) => ({ + ...g, + id: g.skuId, + })); + liliDialogRef.value.goodsData = JSON.parse(JSON.stringify(currentGifts)); + liliDialogRef.value.open("goods"); +} + +function removeGiftFromTier(tier, index) { + if (!tier.giftItems) return; + tier.giftItems.splice(index, 1); +} + +/** + * 赠品展示名称:优先使用商品选择器回填或后端快照的名称,缺失时回退显示 SKU ID。 + * @param {object} giftItem 赠品项 + * @returns {string} 展示名称 + */ +function formatGiftDisplayName(giftItem) { + if (giftItem?.goodsName) return giftItem.goodsName; + return `SKU:${giftItem?.skuId || "-"}`; +} + function closeCurrentPage() { router.back(); } + function openSkuList() { + pickerTargetTierIndex.value = null; const data = JSON.parse(JSON.stringify(form.value.promotionGoodsList || [])); data.forEach((e) => { e.id = e.skuId || e.id; }); - // 先写入已选商品,再打开选择器,保证回显时 selectedWay 已就绪 liliDialogRef.value.goodsData = data; liliDialogRef.value.open("goods"); } + +function normalizeDetailRules(data) { + const type = + data.activityType || + (data.fullRateFlag + ? "FULL_RATE" + : !data.fullMinusFlag && data.giftFlag + ? "FULL_GIFT" + : "FULL_MINUS"); + data.activityType = type; + + let rules = Array.isArray(data.ruleList) && data.ruleList.length ? data.ruleList : null; + if (!rules && data.rulesJson) { + try { + rules = JSON.parse(data.rulesJson); + } catch (e) { + rules = null; + } + } + if (!rules || !rules.length) { + if (type === "FULL_RATE") { + rules = [{ fullMoney: data.fullMoney || 100, fullRate: data.fullRate || 9.0 }]; + } else if (type === "FULL_GIFT") { + const ids = String(data.giftId || "") + .split(",") + .map((s) => s.trim()) + .filter(Boolean); + rules = [ + { + fullMoney: data.fullMoney || 100, + giftItems: ids.map((skuId) => ({ skuId, goodsName: data.giftSkuName || "", num: 1 })), + }, + ]; + } else { + rules = [{ fullMoney: data.fullMoney || 100, fullMinus: data.fullMinus || 10 }]; + } + } + rules.forEach((r) => { + if (type === "FULL_GIFT") { + // 赠品统一由商品选择器维护,过滤历史数据中的空 SKU 占位项 + r.giftItems = (Array.isArray(r.giftItems) ? r.giftItems : []).filter((g) => g && g.skuId); + } + }); + data.ruleList = rules; +} + function getDetail() { getFullDiscountById(id.value).then((res) => { const data = res.result; if (data.scopeType === "ALL") { data.promotionGoodsList = []; } - if (data.fullMinusFlag) { - data.discountType = "fullMinusFlag"; - delete data.fullMinusFlag; - } else { - data.discountType = "fullRateFlag"; - delete data.fullRateFlag; - } + normalizeDetailRules(data); data.rangeTime = [new Date(data.startTime), new Date(data.endTime)]; - if (data.fullRate !== null && data.fullRate !== undefined && data.fullRate !== "") { - data.fullRate = parseFloat(Number(data.fullRate).toFixed(1)); - } form.value = data; + if (data.activityType && route.query.activityType !== data.activityType) { + router.replace({ + query: { ...route.query, activityType: data.activityType }, + }); + } }); } + +function validateTiers(activityType, ruleList) { + if (!Array.isArray(ruleList) || ruleList.length < 1 || ruleList.length > 3) { + Message.warning("请配置 1 至 3 级阶梯优惠"); + return false; + } + let prevMoney = 0; + let prevMinus = 0; + let prevRate = 10; + + for (let i = 0; i < ruleList.length; i++) { + const tier = ruleList[i]; + const label = `第 ${i + 1} 级阶梯`; + const money = Number(tier.fullMoney); + if (!money || money <= 0 || money > 99999999) { + Message.warning(`${label}:请填写有效的优惠门槛金额`); + return false; + } + if (i > 0 && money <= prevMoney) { + Message.warning(`${label}:优惠门槛金额必须大于上一级阶梯(>${prevMoney}元)`); + return false; + } + prevMoney = money; + + if (activityType === "FULL_MINUS") { + const minus = Number(tier.fullMinus); + if (!minus || minus <= 0) { + Message.warning(`${label}:请填写有效的优惠金额`); + return false; + } + if (minus >= money) { + Message.warning(`${label}:优惠金额必须小于当前阶梯门槛金额`); + return false; + } + if (i > 0 && minus <= prevMinus) { + Message.warning(`${label}:优惠金额必须大于上一级阶梯(>${prevMinus}元)`); + return false; + } + prevMinus = minus; + } else if (activityType === "FULL_RATE") { + const rate = Number(tier.fullRate); + if (!rate || rate < 0.1 || rate > 9.9) { + Message.warning(`${label}:优惠折扣须在 0.1 至 9.9 折之间`); + return false; + } + if (i > 0 && rate >= prevRate) { + Message.warning(`${label}:折扣数值须小于上一级阶梯(<${prevRate}折),以保证折扣力度递增`); + return false; + } + prevRate = rate; + } else if (activityType === "FULL_GIFT") { + const gifts = tier.giftItems || []; + if (!gifts.length || gifts.length > 3) { + Message.warning(`${label}:请选择 1 至 3 个赠品`); + return false; + } + const seenSku = new Set(); + for (let gIdx = 0; gIdx < gifts.length; gIdx++) { + const g = gifts[gIdx]; + if (!g.skuId) { + Message.warning(`${label}:第 ${gIdx + 1} 个赠品尚未选择`); + return false; + } + if (seenSku.has(g.skuId)) { + Message.warning(`${label}:同一阶梯内不能重复选择相同的赠品 SKU`); + return false; + } + seenSku.add(g.skuId); + const num = Number(g.num); + if (!num || num < 1 || num > 10) { + Message.warning(`${label}:单个赠品赠送数量须在 1 至 10 件之间`); + return false; + } + } + } + } + return true; +} + function handleSubmit() { formRef.value.validate((valid) => { if (!valid) return; + if (!validateTiers(form.value.activityType, form.value.ruleList)) { + return; + } + const params = JSON.parse(JSON.stringify(form.value)); params.startTime = unixToDate(form.value.rangeTime[0] / 1000); params.endTime = unixToDate(form.value.rangeTime[1] / 1000); - if (params.couponFlag && !params.couponId) { - Message.warning("请选择优惠券"); - return; - } - if (params.giftFlag && !params.giftId) { - Message.warning("请选择赠品"); - return; - } - if (params.pointFlag && !params.point) { - Message.warning("请填写积分"); - return; - } - if ( params.scopeType === "PORTION_GOODS" && (!params.promotionGoodsList || params.promotionGoodsList.length === 0) @@ -531,12 +693,28 @@ function handleSubmit() { }); params.scopeId = scopeId.toString(); } - if (params.discountType === "fullMinusFlag") { + + // 同步首档冗余字段以保持接口兼容 + const firstTier = params.ruleList[0]; + params.fullMoney = firstTier.fullMoney; + if (params.activityType === "FULL_MINUS") { params.fullMinusFlag = true; - } else { + params.fullMinus = firstTier.fullMinus; + params.fullRateFlag = false; + params.giftFlag = false; + } else if (params.activityType === "FULL_RATE") { + params.fullMinusFlag = false; params.fullRateFlag = true; + params.fullRate = firstTier.fullRate; + params.giftFlag = false; + } else if (params.activityType === "FULL_GIFT") { + params.fullMinusFlag = false; + params.fullRateFlag = false; + params.giftFlag = true; + params.giftId = (firstTier.giftItems || []).map((g) => g.skuId).join(","); } - delete params.discountType; + params.rulesJson = JSON.stringify(params.ruleList); + delete params.rangeTime; if (!id.value) { delete params.id; @@ -546,18 +724,24 @@ function handleSubmit() { submitLoading.value = true; const request = id.value ? editFullDiscount(params) : newFullDiscount(params); - request.then((res) => { - submitLoading.value = false; - if (res.success) { - Message.success(id.value ? "编辑活动成功" : "添加活动成功"); - closeCurrentPage(); - } - }); + request + .then((res) => { + submitLoading.value = false; + if (res.success) { + Message.success(id.value ? "编辑活动成功" : "添加活动成功"); + closeCurrentPage(); + } + }) + .catch(() => { + submitLoading.value = false; + }); }); } + function changeSelect(e) { selectedGoods.value = e; } + function delSelectGoods() { if (selectedGoods.value.length <= 0) { Message.warning("您还未选择要删除的数据"); @@ -574,10 +758,43 @@ function delSelectGoods() { }, }); } + function delGoods(index) { form.value.promotionGoodsList.splice(index, 1); } + function selectedGoodsData(item) { + if (pickerTargetTierIndex.value !== null) { + const tier = form.value.ruleList[pickerTargetTierIndex.value]; + pickerTargetTierIndex.value = null; + if (!tier) return; + let selectedList = Array.isArray(item) ? item : []; + if (selectedList.length > 3) { + Message.warning("每级阶梯最多只能选择 3 个赠品,已为您保留前 3 个"); + selectedList = selectedList.slice(0, 3); + } + const prevNumMap = new Map( + (tier.giftItems || []) + .filter((g) => g && g.skuId) + .map((g) => [String(g.skuId), g.num || 1]) + ); + tier.giftItems = selectedList.map((e) => { + const skuId = e.id || e.skuId; + return { + skuId, + goodsId: e.goodsId, + goodsName: e.goodsName, + thumbnail: e.thumbnail || e.small, + simpleSpecs: e.simpleSpecs, + goodsType: e.goodsType, + price: e.price, + quantity: e.quantity, + num: prevNumMap.get(String(skuId)) || 1, + }; + }); + return; + } + const list = []; item.forEach((e) => { list.push({ @@ -596,39 +813,6 @@ function selectedGoodsData(item) { }); form.value.promotionGoodsList = list; } -function getCouponList(query) { - const params = { - pageSize: 20, - pageNumber: 1, - getType: "ACTIVITY", - couponName: query, - promotionStatus: "START", - }; - couponLoading.value = true; - getShopCouponList(params).then((res) => { - couponLoading.value = false; - if (res.success) { - couponList.value = res.result.records; - } - }); -} -function getGiftList(query) { - const params = { - pageSize: 20, - pageNumber: 1, - id: query === form.value.giftId ? form.value.giftId : null, - goodsName: query === form.value.giftId ? null : query, - marketEnable: "UPPER", - authFlag: "PASS", - }; - giftLoading.value = true; - getGoodsSkuListDataSeller(params).then((res) => { - giftLoading.value = false; - if (res.success) { - giftList.value = res.result.records; - } - }); -} diff --git a/seller/src/views/promotion/full-discount/full-discount.vue b/seller/src/views/promotion/full-discount/full-discount.vue index b15713eba..9896a560f 100644 --- a/seller/src/views/promotion/full-discount/full-discount.vue +++ b/seller/src/views/promotion/full-discount/full-discount.vue @@ -1,3 +1,8 @@ + @@ -48,7 +53,7 @@ - 新增 + {{ addBtnText }} - - - - - + + - {{ row.fullMinusFlag ? "满减" : "满折" }} + {{ formatRowRules(row) }} - + + + + + {{ row.scopeType === "ALL" ? "全品类" : "指定商品" }} + + + + + + {{ activityTypeText(row) }} + + + + {{ promotionStatusText(row.promotionStatus) }} @@ -106,13 +122,45 @@ diff --git a/seller/src/views/promotion/full-discount/full-gift.vue b/seller/src/views/promotion/full-discount/full-gift.vue new file mode 100644 index 000000000..c67d0bf1d --- /dev/null +++ b/seller/src/views/promotion/full-discount/full-gift.vue @@ -0,0 +1,14 @@ + + + + + + diff --git a/seller/src/views/promotion/full-discount/full-rate.vue b/seller/src/views/promotion/full-discount/full-rate.vue new file mode 100644 index 000000000..ef3565741 --- /dev/null +++ b/seller/src/views/promotion/full-discount/full-rate.vue @@ -0,0 +1,14 @@ + + + + + + diff --git a/seller/src/views/shop/wap/decorate.vue b/seller/src/views/shop/wap/decorate.vue index 50b91f682..0b098465b 100644 --- a/seller/src/views/shop/wap/decorate.vue +++ b/seller/src/views/shop/wap/decorate.vue @@ -433,9 +433,15 @@ 秒杀 - + 满减 + + 满折 + + + 满赠 + 拼团 {{ item.url.title || item.url.goodsName }}
电子卡券赠品支付后自动发卡;卡池不足时仅取消赠品子单,不影响主订单。