ouyang 20 часов назад
Родитель
Сommit
3de3a08789

+ 1 - 1
hdApp/src/admin/hb/list.vue

@@ -7,7 +7,7 @@
     <view class="fixed-header">
       <view class="search-row">
         <view class="search-container">
-          <app-search-module ref="searchRef" placeholder="输入客户名搜索" v-model="searchText" @input="searchFn" />
+          <app-search-module ref="searchRef" placeholder="输入客户名" v-model="searchText" @input="searchFn" />
           <view v-if="customId" class="search-clear" @click.stop="clearCustomer">清空</view>
         </view>
         <button class="header-btn filter-btn" @click="openFilter">筛选</button>

+ 1 - 3
hdApp/src/admin/home/homeMenus.js

@@ -98,9 +98,7 @@ export const APPLY_MENU_GROUPS = [
       { name: '工作单列表', icon: homeIcon('list-3'), url: '/admin/work/list', pf: 1 },
       { name: '图文', icon: homeIcon('pic-text'), url: '/admin/picText/picTextList', pf: 1 },
       { name: '花材统计', icon: homeIcon('stats-bar-filled'), url: '/admin/stat/kind', pf: 1 },
-      { name: '员工统计', icon: homeIcon('staff'), url: '/admin/stat/makeStat', pf: 1 },
-      { name: '设置', icon: homeIcon('settings-filled'), url: '/admin/work/set', pf: 1 },
-      { name: '公告', icon: homeIcon('notice'), url: '/admin/shopNotice/list', pf: 1 }
+      { name: '员工统计', icon: homeIcon('staff'), url: '/admin/stat/makeStat', pf: 1 }
     ]
   }
 ]

+ 26 - 9
hdApp/src/admin/home/shopInviteHub.vue

@@ -1,6 +1,7 @@
 <!--
   邀请花店 / 我的分佣 hub
   用途:我的-邀请花店;最近邀请手机号来自接口 mobile(表 inviteeMobile 快照)
+  「我的邀请码」仅当当前门店 hasRenew=1 且 deadline 未过期时展示(接口 inviteCodeEligible)
 -->
 <template>
   <view class="hub-page app-content">
@@ -21,7 +22,7 @@
       </view>
     </view>
 
-    <view class="invite-code-card">
+    <view v-if="inviteCodeEligible" class="invite-code-card">
       <view class="code-head">
         <view class="code-icon-wrap">
           <text class="code-icon-text">邀</text>
@@ -111,6 +112,8 @@ export default {
         hdDiscountAmount: 0,
         inviteCount: 0
       },
+      /** 当前门店已续费且未到期时才为 true,控制「我的邀请码」模块 */
+      inviteCodeEligible: false,
       recentList: [],
       depositSubmitting: false
     }
@@ -128,6 +131,10 @@ export default {
       const comm = this.moneyText(this.invite.commissionAmount)
       const disc = this.moneyText(this.invite.hdDiscountAmount)
       return `花店开通账号并使用邀请码支付,花店享受${disc}元优惠,邀请人获得${comm}元佣金。`
+    },
+    /** 是否允许分享(须有码且门店邀请权益有效) */
+    canShareInvite () {
+      return this.inviteCodeEligible && !!this.invite.inviteCode
     }
   },
   onShow () {
@@ -177,6 +184,8 @@ export default {
       if (res.data.invite) {
         this.invite = { ...this.invite, ...res.data.invite }
       }
+      // 已续费且未到期才展示「我的邀请码」
+      this.inviteCodeEligible = Number(res.data.inviteCodeEligible) === 1
       this.recentList = res.data.recentList || []
     },
     onCommissionTip () {
@@ -222,7 +231,23 @@ export default {
         this.depositSubmitting = false
       }
     },
+    onShareInviteClick () {
+      if (!this.canShareInvite) {
+        uni.showToast({ title: '暂无邀请码', icon: 'none' })
+      }
+    },
+    onShareInvite () {
+      if (!this.canShareInvite) {
+        uni.showToast({ title: '暂无邀请码', icon: 'none' })
+        return
+      }
+      uni.showToast({ title: '请在微信小程序内分享邀请', icon: 'none' })
+    },
     copyInviteCode () {
+      if (!this.canShareInvite) {
+        uni.showToast({ title: '暂无邀请码', icon: 'none' })
+        return
+      }
       const code = this.invite.inviteCode
       if (!code) {
         uni.showToast({ title: '暂无邀请码', icon: 'none' })
@@ -235,14 +260,6 @@ export default {
         }
       })
     },
-    onShareInviteClick () {
-      if (!this.invite.inviteCode) {
-        uni.showToast({ title: '暂无邀请码', icon: 'none' })
-      }
-    },
-    onShareInvite () {
-      uni.showToast({ title: '请在微信小程序中使用分享', icon: 'none' })
-    },
     goInviteShopList () {
       this.$util.pageTo({ url: '/admin/home/shopInviteList' })
     },

+ 67 - 10
hdApp/src/pagesClient/official/applyInvite.vue

@@ -1,6 +1,7 @@
 <!--
   邀请开店 / 会员开通页
   用途:分享带邀请码新用户注册+支付;已开店用户续费(仅首次付费享邀请减免与佣金)
+  规则:自己的邀请码不带入;邀请人须 hasRenew=1 且未到期,否则提示「邀请码无效」且无减免,均可继续支付
 -->
 <template>
   <view class="apply-page app-content">
@@ -164,6 +165,8 @@
               @confirm="onInviteCodeBlur"
             />
           </view>
+          <!-- 邀请码无效提示:邀请人未续费或已到期,仍可支付(无减免) -->
+          <view v-if="inviteCodeStatusTip" class="fee-invite-tip">{{ inviteCodeStatusTip }}</view>
           <view class="fee-row fee-row--border">
             <text class="fee-label">会员费</text>
             <text class="fee-value">¥{{ formatMoney(feePreview.price) }}</text>
@@ -279,6 +282,10 @@ export default {
         memberTypeName: '年度会员',
         inviteValid: 0,
         inviteNotFound: 0,
+        /** 是否为自己的邀请码(前端不带入) */
+        inviteSelf: 0,
+        /** 邀请人未续费或已到期 */
+        inviteInvalid: 0,
         hdDiscountAmount: 0,
         showInviteDiscount: 0,
         payAmount: 0
@@ -329,6 +336,26 @@ export default {
     inviteBenefitAvailable () {
       return !this.hasPaidRegisterRenew
     },
+    /**
+     * 邀请码状态文案:无效时展示「邀请码无效」,不阻断支付
+     * 自邀会清空输入,不在此提示
+     */
+    inviteCodeStatusTip () {
+      if (!this.inviteBenefitAvailable) {
+        return ''
+      }
+      const code = (this.form.inviteCode || '').trim()
+      if (!code) {
+        return ''
+      }
+      if (this.feePreview.inviteInvalid) {
+        return '邀请码无效'
+      }
+      if (this.feePreview.inviteNotFound) {
+        return '邀请人不存在'
+      }
+      return ''
+    },
     /** 提交按钮文案 */
     submitBtnText () {
       if (this.hasShop && this.hasPaidRegisterRenew) {
@@ -578,6 +605,23 @@ export default {
         if (res.code === 1 && res.data) {
           this.syncInviteBenefitFlags(res.data)
           this.feePreview = { ...this.feePreview, ...res.data }
+          // 自己的邀请码:不带入输入框,可继续支付(无减免)
+          if (res.data.inviteSelf && code) {
+            this.form.inviteCode = ''
+            this.inviteCodeFromLink = ''
+            this.feePreview = {
+              ...this.feePreview,
+              inviteSelf: 1,
+              inviteValid: 0,
+              showInviteDiscount: 0,
+              hdDiscountAmount: 0,
+              payAmount: this.feePreview.price
+            }
+            if (notifyInvalid) {
+              uni.showToast({ title: '不能使用自己的邀请码', icon: 'none' })
+            }
+            return
+          }
           if (notifyInvalid && code !== '' && res.data.inviteNotFound) {
             uni.showToast({ title: '邀请人不存在', icon: 'none' })
           }
@@ -985,7 +1029,7 @@ export default {
       }
       this.confirmPayOnly()
     },
-    /** 已开店:保存邀请码并唤起支付(续费跳过邀请码) */
+    /** 已开店:保存邀请码并唤起支付(续费跳过邀请码;自邀/无效码仍可支付) */
     confirmPayOnly () {
       const that = this
       if (this.hasPaidRegisterRenew) {
@@ -993,10 +1037,13 @@ export default {
         that.startPayFlow(that.applyId)
         return
       }
-      const inviteCode = (this.form.inviteCode || '').trim()
-      if (inviteCode !== '' && this.feePreview.inviteNotFound) {
-        this.$msg('邀请人不存在')
-        return false
+      // 自邀已清空;无效/不存在由后端落库时清空邀请码,前端不阻断支付
+      let inviteCode = (this.form.inviteCode || '').trim()
+      if (
+        inviteCode &&
+        (this.feePreview.inviteSelf || this.feePreview.inviteInvalid || this.feePreview.inviteNotFound)
+      ) {
+        inviteCode = ''
       }
       uni.showLoading({ mask: true })
       saveRegisterInvite({ inviteCode }).then(res => {
@@ -1135,10 +1182,13 @@ export default {
     confirmFn () {
       restorePendingInviteFromLegacy()
       const inviteParams = getRegisterInviteParams(this.option)
-      const inviteCode = (this.form.inviteCode || '').trim()
-      if (inviteCode !== '' && this.feePreview.inviteNotFound) {
-        this.$msg('邀请人不存在')
-        return false
+      // 自邀已清空;无效/不存在提交时空邀请码,不阻断注册支付
+      let inviteCode = (this.form.inviteCode || '').trim()
+      if (
+        inviteCode &&
+        (this.feePreview.inviteSelf || this.feePreview.inviteInvalid || this.feePreview.inviteNotFound)
+      ) {
+        inviteCode = ''
       }
       uni.showLoading({ mask: true })
       const currentMiniOpenId = uni.getStorageSync('currentMiniOpenId')
@@ -1154,8 +1204,8 @@ export default {
         hdShopAdminId: inviteParams.hdShopAdminId,
         ghsShopAdminId: inviteParams.ghsShopAdminId,
         inviteGhsShopId: inviteParams.inviteGhsShopId,
-        inviteCode,
         ...this.form,
+        inviteCode,
         style,
         from,
         miniOpenId: currentMiniOpenId,
@@ -1266,6 +1316,13 @@ export default {
   padding: 12upx 0;
 }
 
+.fee-invite-tip {
+  padding: 0 0 16upx;
+  font-size: 24upx;
+  color: #e54d42;
+  line-height: 1.4;
+}
+
 .fee-row--border {
   border-top: 1upx solid #eef1f5;
 }

+ 6 - 2
mallApp/src/api/group-buy/index.js

@@ -13,8 +13,12 @@ export const getGroupDetail = data => https.get('/group-buy/detail', data)
 /** 我要开团 */
 export const createGroup = data => https.post('/group-buy/create', data)
 
-/** 我要参团 */
-export const joinGroup = data => https.post('/group-buy/join', data)
+/**
+ * 我要参团
+ * @param {Object} data 业务参数
+ * @param {Object} config 请求控制,如 hideError 由页面自行弹失败提示
+ */
+export const joinGroup = (data, config = {}) => https.post('/group-buy/join', data, {}, config)
 
 /** 我的拼团列表 */
 export const getMyList = data => https.get('/group-buy/my-list', data)

+ 8 - 14
mallApp/src/components/shop-tab-bar/index.vue

@@ -1,7 +1,7 @@
 <!--
   店铺底部导航栏(通用组件)
-  用途:店铺首页/分类/购物车/订单四个入口,供店铺相关页面复用
-  说明:首页跳转暂未实现;购物车跳转 pages/home/cart
+  用途:店铺首页/分类/购物车个入口,供店铺相关页面复用
+  说明:互切用 redirectTo 原页替换,避免栈上新开一层
 -->
 <template>
   <view class="shop-tab-bar">
@@ -35,7 +35,7 @@
 export default {
   name: 'ShopTabBar',
   props: {
-    /** 当前选中项:home | category | cart | order */
+    /** 当前选中项:home | category | cart */
     current: {
       type: String,
       default: 'category'
@@ -56,14 +56,13 @@ export default {
       tabList: [
         { key: 'home', label: '首页', icon: 'icon-home', activeIcon: 'icon-home-active' },
         { key: 'category', label: '分类', icon: 'icon-category', activeIcon: 'icon-category-active' },
-        { key: 'cart', label: '购物车', icon: 'icon-cart', activeIcon: 'icon-cart-active' },
-        { key: 'order', label: '订单', icon: 'icon-order', activeIcon: 'icon-order-active' }
+        { key: 'cart', label: '购物车', icon: 'icon-cart', activeIcon: 'icon-cart-active' }
       ]
     }
   },
   methods: {
     /**
-     * 底部 Tab 点击:首页/购物车保留占位,分类刷新当前页,订单跳转订单页
+     * 底部 Tab 点击:店铺内页互切用 redirectTo,避免 navigateTo 叠一层「新页面」
      */
     onTabTap(item) {
       if (item.key === this.current) {
@@ -81,6 +80,7 @@ export default {
       if (item.key === 'home') {
         this.$util.pageTo({
           url: '/pages/home/index',
+          type: 2,
           query: shopQuery
         })
         return
@@ -88,6 +88,7 @@ export default {
       if (item.key === 'category') {
         this.$util.pageTo({
           url: '/pages/home/shop-category',
+          type: 2,
           query: shopQuery
         })
         return
@@ -95,16 +96,9 @@ export default {
       if (item.key === 'cart') {
         this.$util.pageTo({
           url: '/pages/home/cart',
+          type: 2,
           query: shopQuery
         })
-        return
-      }
-      if (item.key === 'order') {
-        this.$util.pageTo({
-          url: '/pages/home/order',
-          type: 4,
-          query: Object.assign({}, shopQuery, { shopNav: 1 })
-        })
       }
     }
   }

+ 26 - 1
mallApp/src/pages/goods/list.vue

@@ -310,6 +310,31 @@ export default {
         url: '/pages/home/cart?account=' + account + '&hdId=' + hdId
       })
     },
+    /**
+     * 解码路由里的搜索词
+     * 首页跳转会 encodeURIComponent;微信/uni-app 可能再编一层,onLoad 只解一次时仍是 %E9..,需再解才是中文
+     */
+    decodeRouteKeyword(raw) {
+      let s = String(raw || '')
+      if (!s) {
+        return ''
+      }
+      // 最多解两层,兼容「只编一次已被框架解开」和「双重编码仍带 %」
+      let i = 0
+      while (i < 2 && s.indexOf('%') !== -1) {
+        i += 1
+        try {
+          const next = decodeURIComponent(s)
+          if (next === s) {
+            break
+          }
+          s = next
+        } catch (e) {
+          break
+        }
+      }
+      return s
+    },
     /**
      * 解析路由带入的筛选与搜索关键词
      * searchText:首页搜索框跳转带入,回填搜索框并参与首次列表请求
@@ -318,7 +343,7 @@ export default {
      */
     applyRouteFilter() {
       // 首页搜索跳转:回填关键词,init 随后 loadGoods 会带上 searchText
-      const keyword = (this.option && this.option.searchText) ? String(this.option.searchText) : ''
+      const keyword = (this.option && this.option.searchText) ? this.decodeRouteKeyword(this.option.searchText) : ''
       if (keyword) {
         this.searchText = keyword
       }

+ 22 - 5
mallApp/src/pages/groupBuy/detail.vue

@@ -544,16 +544,23 @@ export default {
       if (!this.openGroup || !this.openGroup.id) return
       this.doJoin(this.openGroup.id)
     },
+    /**
+     * 参团:默认自取下单,成功后去待付款
+     * hideError 避免拦截器先弹默认 1.5s toast,再被 hideLoading 立刻关掉
+     */
     doJoin(groupBuyId) {
       if (this.submitting) return
       if (!this.ensureLogin()) return
       this.submitting = true
       uni.showLoading({ title: '参团中', mask: true })
-      joinGroup({
-        groupBuyId,
-        goodsNum: 1,
-        sendType: 1
-      })
+      joinGroup(
+        {
+          groupBuyId,
+          goodsNum: 1,
+          sendType: 1
+        },
+        { hideError: true }
+      )
         .then((res) => {
           uni.hideLoading()
           this.submitting = false
@@ -561,6 +568,16 @@ export default {
             this.groupBuyId = res.data.groupBuyId
             this.setupShare()
             this.goPay(res.data)
+            return
+          }
+          // 如「已在该团中」:先关 loading 再弹,时长在默认 1500ms 上加长 1.5s
+          if (res && res.msg) {
+            uni.showToast({
+              title: res.msg,
+              icon: 'none',
+              mask: true,
+              duration: 3000
+            })
           }
         })
         .catch(() => {

+ 1 - 1
mallApp/src/pages/home/recent.vue

@@ -784,7 +784,7 @@ page {
     flex-shrink: 1;
     min-width: 0;
     color: #6b7280;
-    font-size: 20upx;
+    font-size: 24upx;
     line-height: 1.2;
     text-align: left;
     white-space: nowrap;

+ 2 - 2
mallApp/src/utils/mainIndexSprite.js

@@ -21,8 +21,8 @@ export const SPRITE_SHEETS = {
     height: 500,
     icons: {
       "dengji-biandong": { x: 14, y: 17, w: 32, h: 32 },
-      "yue-biandong": { x: 60, y: 15, w: 32, h: 32 },
-      "huiyuan-quanyi": { x: 102, y: 17, w: 33, h: 32 },
+      "yue-biandong": { x: 60, y: 16, w: 32, h: 32 },
+      "huiyuan-quanyi": { x: 102, y: 14, w: 33, h: 32 },
       "lianxikefu": { x: 145, y: 17, w: 32, h: 32 },
       "huangguan": { x: 189, y: 20, w: 22, h: 21 },
       "shezhi": { x: 13, y: 62, w: 34, h: 36 },