From eb017244d3b158e71470ee5d543d978d23990687 Mon Sep 17 00:00:00 2001 From: "pikachu1995@126.com" Date: Tue, 10 Nov 2026 15:55:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(promotion):=20=E6=96=B0=E5=A2=9E=E6=BB=A1?= =?UTF-8?q?=E6=8A=98=E6=BB=A1=E8=B5=A0=E6=B4=BB=E5=8A=A8=E5=8F=8A=E9=98=B6?= =?UTF-8?q?=E6=A2=AF=E4=BC=98=E6=83=A0=E8=A7=84=E5=88=99=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 满额活动列表页支持满减、满折、满赠三种类型筛选与标签展示 - 新增阶梯优惠规则列,按活动类型格式化展示各级优惠详情 - 活动详情页支持1至3级阶梯优惠配置及满赠赠品选择 - 页面装修组件增加满折、满赠活动类型标签展示 - 隐藏"添加活动优惠券"按钮入口 - 优化活动类型、状态、时间等表格列宽与样式 --- im/MIGRATION-VUE3.md | 42 - .../lili-dialog/template/marketing.vue | 34 +- manager/src/libs/util.js | 9 + .../shrinkable-menu/shrinkable-menu.vue | 18 + .../views/page-decoration/wap/decorate.vue | 8 +- .../full-discount/full-discount-detail.vue | 175 ++-- .../full-discount/full-discount.vue | 132 ++- .../promotions/full-discount/full-gift.vue | 14 + .../promotions/full-discount/full-rate.vue | 14 + manager/src/views/statistics/marketing.vue | 6 +- seller/scripts/test-seller-navigation.mjs | 13 + seller/src/libs/seller-navigation.js | 27 +- seller/src/libs/util.js | 9 + seller/src/views/Main.vue | 13 +- .../shrinkable-menu/shrinkable-menu.vue | 2 +- seller/src/views/promotion/coupon/coupon.vue | 2 +- .../full-discount/full-discount-add.vue | 971 +++++++++++------- .../promotion/full-discount/full-discount.vue | 150 ++- .../promotion/full-discount/full-gift.vue | 14 + .../promotion/full-discount/full-rate.vue | 14 + seller/src/views/shop/wap/decorate.vue | 8 +- 21 files changed, 1135 insertions(+), 540 deletions(-) delete mode 100644 im/MIGRATION-VUE3.md create mode 100644 manager/src/views/promotions/full-discount/full-gift.vue create mode 100644 manager/src/views/promotions/full-discount/full-rate.vue create mode 100644 seller/src/views/promotion/full-discount/full-gift.vue create mode 100644 seller/src/views/promotion/full-discount/full-rate.vue 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 @@ +