ouyang 2 дней назад
Родитель
Сommit
be53a4f7f8

+ 2 - 1
hdApp/src/admin/home/walletChangeList.vue

@@ -110,7 +110,8 @@ export default {
       // value 为 -1 表示不传 capitalType;其余与 dict capitalType 对齐后可扩展
       capitalTypeOptions: [
         { label: '--请选择--', value: -1 },
-        { label: '充值', value: 4 },
+        { label: '微信充值', value: 86 },
+        { label: '佣金存入', value: 85 },
         { label: '提现', value: 6 }
       ]
     }

+ 107 - 8
hdApp/src/admin/home/walletRecharge.vue

@@ -1,6 +1,6 @@
 <!--
-  中央钱包充值页(仅 UI)
-  用途:我的-可用余额-充值;支付与下单后续迭代
+  中央钱包充值页
+  用途:我的-可用余额-充值;调 main-wallet/recharge 拉起微信支付,回调入账
 -->
 <template>
   <view class="wallet-recharge-page">
@@ -43,16 +43,21 @@
     </view>
 
     <view class="footer-wrap">
-      <button class="pay-btn" @click="onPayPlaceholder">微信支付</button>
+      <button class="pay-btn" :loading="paying" :disabled="paying" @click="onWxPay">微信支付</button>
     </view>
   </view>
 </template>
 
 <script>
 import { mainMy } from '@/api/home'
+import { mainWalletRecharge } from '@/api/main-wallet'
+import { postWxCode } from '@/api/mini'
+import wexinPay from '@/utils/pay/wxPay'
 
 /** 预设充值档位(元) */
 const PRESET_AMOUNTS = [300, 500, 1000, 2000, 3000]
+/** 与后端 MainWalletRechargeClass::MAX_AMOUNT 一致 */
+const MAX_AMOUNT = 50000
 
 export default {
   name: 'walletRecharge',
@@ -62,7 +67,8 @@ export default {
       presetAmounts: PRESET_AMOUNTS,
       selectedPreset: PRESET_AMOUNTS[0],
       isCustom: false,
-      customAmount: ''
+      customAmount: '',
+      paying: false
     }
   },
   computed: {
@@ -75,6 +81,17 @@ export default {
         return this.formatPlainAmount(n)
       }
       return String(this.selectedPreset)
+    },
+    /** 当前待充值金额(元) */
+    payAmount () {
+      if (this.isCustom) {
+        const n = parseFloat(this.customAmount)
+        if (isNaN(n) || n <= 0) {
+          return 0
+        }
+        return Math.floor(n * 100) / 100
+      }
+      return Number(this.selectedPreset) || 0
     }
   },
   onShow () {
@@ -113,11 +130,93 @@ export default {
     selectCustomMode () {
       this.isCustom = true
     },
-    onPayPlaceholder () {
-      uni.showToast({
-        title: '功能开发中',
-        icon: 'none'
+    /** 校验金额后发起微信充值 */
+    onWxPay () {
+      const amount = this.payAmount
+      if (amount <= 0) {
+        this.$msg('请输入充值金额')
+        return
+      }
+      if (amount > MAX_AMOUNT) {
+        this.$msg('单笔充值不能超过' + MAX_AMOUNT + '元')
+        return
+      }
+      this.doRechargePay()
+    },
+    /**
+     * 调后端建单并拉起微信支付;无 openId 时补授权后重试
+     */
+    doRechargePay () {
+      if (this.paying) {
+        return
+      }
+      this.paying = true
+      uni.showLoading({ title: '加载中', mask: true })
+      const miniOpenId = uni.getStorageSync('currentMiniOpenId') || ''
+      mainWalletRecharge({
+        amount: this.payAmount,
+        miniOpenId: miniOpenId
+      }).then(res => {
+        uni.hideLoading()
+        this.paying = false
+        if (res.code != 1) {
+          this.$msg(res.msg || '下单失败')
+          return
+        }
+        // 后端提示缺少小程序 openId,先登录再重试
+        if (res.data && res.data.hasNoMiniOpenId == 1) {
+          this.bindMiniOpenIdThenPay()
+          return
+        }
+        wexinPay(res.data, this.paySuccess, this.payFail)
+      }).catch(() => {
+        uni.hideLoading()
+        this.paying = false
+        this.$msg('网络异常,请重试')
+      })
+    },
+    /** 无 openId:uni.login + postWxCode 后再次支付 */
+    bindMiniOpenIdThenPay () {
+      const that = this
+      uni.login({
+        provider: 'weixin',
+        success: loginRes => {
+          uni.setStorageSync('code', loginRes.code)
+          postWxCode({ code: loginRes.code }).then(subRes => {
+            if (subRes.data && subRes.data.currentMiniOpenId) {
+              uni.setStorageSync('currentMiniOpenId', subRes.data.currentMiniOpenId)
+              that.$util.confirmModal(
+                { content: '网络开小差了,请继续', okText: '确认', singer: true },
+                () => {
+                  that.doRechargePay()
+                }
+              )
+            } else {
+              that.$util.confirmModal(
+                { content: '出错了,请添加客服微信反馈', okText: '复制微信' },
+                () => {
+                  uni.setClipboardData({
+                    data: '15280215347',
+                    success: function () {
+                      that.$msg('复制成功')
+                    }
+                  })
+                }
+              )
+            }
+          })
+        },
+        fail: () => {
+          that.$msg('微信登录失败')
+        }
       })
+    },
+    paySuccess () {
+      this.$msg('支付成功')
+      this.loadWalletBalance()
+    },
+    payFail () {
+      this.$msg('支付取消或失败')
     }
   }
 }

+ 9 - 1
hdApp/src/api/main-wallet/index.js

@@ -1,5 +1,5 @@
 /**
- * 中央钱包(可用余额)接口 — hdApp 变动明细
+ * 中央钱包(可用余额)接口 — hdApp 变动明细、微信充值
  */
 import https from '@/plugins/luch-request_0.0.7/request'
 
@@ -7,3 +7,11 @@ import https from '@/plugins/luch-request_0.0.7/request'
 export const getMainWalletChangeList = data => {
   return https.get('/main-wallet/change-list', data)
 }
+
+/**
+ * 中央钱包微信充值下单,返回 JSAPI 支付参数
+ * @param {{ amount: number|string, miniOpenId?: string }} data
+ */
+export const mainWalletRecharge = data => {
+  return https.post('/main-wallet/recharge', data)
+}

+ 20 - 6
mallApp/src/components/home/noticeBar.vue

@@ -1,6 +1,7 @@
 <!--
-  店铺首页-公告条
-  拉取 hdApp「店铺公告」中展示位置为「商城首页」(position=1) 的上架公告;
+  店铺公告条(首页 / 购物车等复用)
+  拉取 /shop-notice/show-list;position 与 hdApp shopNotice positionOptions 一致:
+  1=商城首页 2=分类菜单 4=购物车 8=订单提交
   同位置多条时每 6 秒纵向轮播,点击当前条跳转公告详情。
 -->
 <template>
@@ -14,7 +15,7 @@
     <text class="notice-tag">公告</text>
     <swiper
       class="notice-swiper"
-      :key="'home-notice-' + noticeList.length"
+      :key="'home-notice-' + position + '-' + noticeList.length"
       vertical
       :autoplay="noticeList.length > 1"
       :circular="noticeList.length > 1"
@@ -34,7 +35,7 @@
 <script>
 import { shopNoticeShowList } from '@/api/shop-notice'
 
-/** 公告展示位置:商城首页(与 hdApp shopNotice/add positionOptions 一致) */
+/** 默认:商城首页(与 hdApp shopNotice/add positionOptions 一致) */
 const NOTICE_POSITION_HOME = 1
 
 export default {
@@ -44,6 +45,13 @@ export default {
     account: {
       type: [String, Number],
       default: ''
+    },
+    /**
+     * 公告展示位置:1 首页 / 2 分类 / 4 购物车 / 8 订单提交
+     */
+    position: {
+      type: [Number, String],
+      default: NOTICE_POSITION_HOME
     }
   },
   data() {
@@ -52,16 +60,22 @@ export default {
       currentNoticeIndex: 0
     }
   },
+  watch: {
+    position() {
+      this.loadNoticeList()
+    }
+  },
   created() {
     this.loadNoticeList()
   },
   methods: {
     /**
-     * 拉取商城首页公告列表
+     * 拉取指定位置公告列表
      * 无数据时组件自隐藏,避免空条占位
      */
     loadNoticeList() {
-      return shopNoticeShowList({ position: NOTICE_POSITION_HOME }).then((res) => {
+      const position = Number(this.position) || NOTICE_POSITION_HOME
+      return shopNoticeShowList({ position }).then((res) => {
         const data = res.data || {}
         this.noticeList = data.list || []
         this.currentNoticeIndex = 0

+ 6 - 0
mallApp/src/constant/storageKeys.js

@@ -3,3 +3,9 @@ export const LOGIN_INFO_STORAGE_KEY = "mallLoginInfo";
 
 // 兼容历史版本,启动时可迁移
 export const LEGACY_TOKEN_STORAGE_KEY = "token";
+
+/**
+ * 店铺内导航标记:shop-tab-bar 进入订单 Tab 时写入;
+ * 切回「我的花店/消息/我的」时清除,避免原生 tabBar 被长期隐藏
+ */
+export const SHOP_NAV_ACTIVE_KEY = "shopNavActive";

+ 6 - 1
mallApp/src/pages/billing/affirmMix.vue

@@ -1,10 +1,13 @@
 <!--
   混合结算页(花束+花材),立即购买
   用途:商城混合购物车确认下单,支持多种配送方式、红包、贺卡与匿名配送
+  顶部公告:/shop-notice/show-list position=8(订单提交)
 -->
 <template>
 	<view class="app-main app-content affirm-page">
 		<view class="affirm-scroll">
+			<!-- 订单提交公告:position=8,UI 复用首页 noticeBar -->
+			<home-notice-bar :account="account" :position="8" />
 			<form>
 				<!-- 商品卡片 -->
 				<view class="affirm-card product-card">
@@ -501,6 +504,7 @@ import { getAllPsMethod, calcMixFeeBatch } from "@/api/ps-method";
 import MxDatePicker from "@/components/mx-datepicker/mx-datepicker.vue";
 import HbSelect from "@/components/hb/hb-select";
 import { isHbAvailable } from "@/utils/hbScope";
+import HomeNoticeBar from "@/components/home/noticeBar.vue";
 
 /** 配送方式 style 与雪碧图图标映射(名称取自接口 name) */
 const SEND_TYPE_ICON = {
@@ -516,7 +520,8 @@ export default {
 	components: {
 		SpriteIcon,
 		MxDatePicker,
-		HbSelect
+		HbSelect,
+		HomeNoticeBar
 	},
 	mixins: [productMins],
 	data() {

+ 0 - 39
mallApp/src/pages/custom/dividendContrib.vue

@@ -52,10 +52,6 @@
             <text class="contrib-line-icon">🕒</text>
             <text class="contrib-line-text">{{ item.timeLabel }} {{ formatTime(item.timeValue) }}</text>
           </view>
-          <view class="contrib-order-link" hover-class="contrib-order-link--hover" @click.stop="goOrderDetail(item)">
-            <text>查看订单详情</text>
-            <text class="contrib-order-arrow">›</text>
-          </view>
         </view>
       </view>
     </block>
@@ -221,17 +217,6 @@ export default {
         return str.substr(0, 16)
       }
       return str
-    },
-    goOrderDetail(item) {
-      const orderId = item && item.orderId
-      if (!orderId) {
-        return
-      }
-      const account = this.option.account || uni.getStorageSync('account') || ''
-      const hdId = this.option.hdId || uni.getStorageSync('hdId') || ''
-      this.pageTo({
-        url: '/pages/order/detail?id=' + orderId + '&account=' + account + '&hdId=' + hdId
-      })
     }
   },
   async onPullDownRefresh() {
@@ -413,28 +398,4 @@ export default {
   font-size: 24upx;
   color: #dddddd;
 }
-
-.contrib-order-link {
-  display: flex;
-  flex-direction: row;
-  align-items: center;
-  justify-content: flex-end;
-  margin-top: 20upx;
-  padding: 16upx 0 4upx;
-  border-top: 1upx solid #f5f5f5;
-  font-size: 24upx;
-  color: #666666;
-  min-height: 72upx;
-}
-
-.contrib-order-link--hover {
-  opacity: 0.7;
-}
-
-.contrib-order-arrow {
-  margin-left: 6upx;
-  font-size: 36upx;
-  color: #cccccc;
-  line-height: 1;
-}
 </style>

+ 7 - 1
mallApp/src/pages/home/cart.vue

@@ -1,11 +1,15 @@
 <!--
   店铺购物车页
   用途:展示花材+花束购物车(Vuex cg 选型,property 区分),支持勾选、改数量、删除;去结算统一跳转 affirmMix
+  顶部公告:/shop-notice/show-list position=4(购物车),UI 复用首页 noticeBar
   路由:pages/home/cart?account=xxx&hdId=xxx
 -->
 <template>
   <view class="cart-page">
     <scroll-view scroll-y class="cart-scroll" :show-scrollbar="false">
+      <!-- 购物车公告:与首页公告条同组件,位置为购物车(4) -->
+      <home-notice-bar :account="shopAccount" :position="4" />
+
       <!-- 店铺信息 -->
       <view class="store-card" v-if="displayCartList.length">
         <view class="store-card-top">
@@ -148,6 +152,7 @@ import { mapGetters } from 'vuex'
 import AppWrapperEmpty from '@/components/app-wrapper-empty'
 import ShopTabBar from '@/components/shop-tab-bar/index.vue'
 import ModalModule from '@/components/item/plugin/modal'
+import HomeNoticeBar from '@/components/home/noticeBar.vue'
 import productMins from '@/mixins/cgProduct'
 import { getInfo } from '@/api/shop'
 import { getLimitBuyInfo } from '@/api/order'
@@ -158,7 +163,8 @@ export default {
   components: {
     AppWrapperEmpty,
     ShopTabBar,
-    ModalModule
+    ModalModule,
+    HomeNoticeBar
   },
   mixins: [productMins, share],
   data() {

+ 5 - 1
mallApp/src/pages/home/course.vue

@@ -45,7 +45,11 @@ export default {
   },
   mixins: [share],
   onShow () {
-    uni.hideTabBar();
+    uni.hideTabBar({ animation: false });
+  },
+  onHide () {
+    // 离开时恢复原生 tabBar,避免返回「我的花店」等 Tab 后底栏消失
+    uni.showTabBar({ animation: false });
   },
   onPullDownRefresh() {
       this.pxClassList().then(res => {

+ 4 - 0
mallApp/src/pages/home/message.vue

@@ -54,6 +54,7 @@ import AppWrapperEmpty from "@/components/app-wrapper-empty";
 import { list } from "@/mixins";
 import { getLatestUsers } from "@/api/chat";
 import { mapGetters } from "vuex";
+import { SHOP_NAV_ACTIVE_KEY } from "@/constant/storageKeys";
 export default {
   name: "MessageIndex",
   components: {
@@ -105,6 +106,9 @@ export default {
       : this.$constant.imgUrl + '/hhb_small.png?x-oss-process=image/resize,m_fill,h_130,w_130';
   },
   onShow() {
+    // 从店铺订单 Tab 返回时恢复原生底栏,并清除店铺导航标记
+    uni.removeStorageSync(SHOP_NAV_ACTIVE_KEY);
+    uni.showTabBar({ animation: false });
     // Tab 页保活,每次切回消息 Tab 都刷新会话列表与未读数
     this.getMyShopList();
   },

+ 26 - 1
mallApp/src/pages/home/order.vue

@@ -92,8 +92,10 @@
  * 订单 Tab 页(主包)
  * 展示订单状态 Tab、店铺筛选与订单卡片列表
  * 待付款「立即付款」、待配送「联系客服」由卡片事件在此处理
- * 店铺场景:query.shopNav=1 时隐藏原生 tabBar,改用 shop-tab-bar
+ * 店铺场景:query.shopNav=1 时隐藏原生 tabBar,改用 shop-tab-bar;
+ * 切回其它原生 Tab 时清除店铺模式,避免底栏消失
  */
+import { SHOP_NAV_ACTIVE_KEY } from "@/constant/storageKeys";
 import OrderItem from "@/components/order/order-item.vue";
 import TuiBottomPopup from "@/components/plugin/bottom-popup.vue";
 import ShopTabBar from "@/components/shop-tab-bar/index.vue";
@@ -199,6 +201,10 @@ export default {
       uni.removeStorageSync("switchTabQuery");
       this.applyPageQuery(tabQuery);
       queryUpdated = true;
+    } else {
+      // 无新 query:若其它原生 Tab 已清除店铺标记,则退出店铺底栏模式
+      // (从订单详情 navigateBack 时标记仍在,继续店铺模式)
+      this.syncShopNavModeFromStorage();
     }
     this.syncShopNavBar();
 
@@ -236,8 +242,12 @@ export default {
       if (query.hdId) {
         uni.setStorageSync("hdId", Number(query.hdId) || 0);
       }
+      // 仅 shopNav=1 进入店铺底栏模式;其它带参 switchTab 入口强制退出
       if (query.shopNav == 1 || query.shopNav === "1") {
         this.shopNavMode = true;
+        uni.setStorageSync(SHOP_NAV_ACTIVE_KEY, 1);
+      } else if (query.shopNav !== undefined || query.status !== undefined) {
+        this.exitShopNavMode();
       }
       // 「我的」页订单快捷入口通过 switchTabQuery.status 指定 Tab
       if (query.status !== undefined && query.status !== null && query.status !== "") {
@@ -254,6 +264,21 @@ export default {
       }
     },
 
+    /**
+     * 根据全局标记同步店铺模式:其它 Tab 清除标记后,订单页退出店铺底栏
+     */
+    syncShopNavModeFromStorage() {
+      if (uni.getStorageSync(SHOP_NAV_ACTIVE_KEY) != 1) {
+        this.shopNavMode = false;
+      }
+    },
+
+    /** 退出店铺底栏模式并清除标记 */
+    exitShopNavMode() {
+      this.shopNavMode = false;
+      uni.removeStorageSync(SHOP_NAV_ACTIVE_KEY);
+    },
+
     /** 店铺模式隐藏原生 tabBar,普通 tab 入口则显示原生 tabBar */
     syncShopNavBar() {
       if (this.shopNavMode) {

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

@@ -160,7 +160,7 @@ import SpriteIcon from "@/components/sprite-icon/index.vue";
 import { mapGetters } from "vuex";
 import { currentInfo } from "@/api/user";
 import { list } from "@/mixins";
-import { TOKEN_STORAGE_KEY } from "@/constant/storageKeys";
+import { TOKEN_STORAGE_KEY, SHOP_NAV_ACTIVE_KEY } from "@/constant/storageKeys";
 export default {
   name: "recent",
   components: {
@@ -200,6 +200,9 @@ export default {
     },
   },
   onShow() {
+    // 从店铺订单 Tab 返回时恢复原生底栏,并清除店铺导航标记
+    uni.removeStorageSync(SHOP_NAV_ACTIVE_KEY);
+    uni.showTabBar({ animation: false });
     if (!this.$util.isEmpty(this.loginInfo)) {
       this.loginStyle = 1;
     } else {

+ 5 - 1
mallApp/src/pages/home/shop.vue

@@ -69,7 +69,11 @@ export default {
   onLoad () {
   },
   onShow () {
-    uni.hideTabBar();
+    uni.hideTabBar({ animation: false });
+  },
+  onHide () {
+    // 离开时恢复原生 tabBar,避免返回「我的花店」等 Tab 后底栏消失
+    uni.showTabBar({ animation: false });
   },
   methods: {
     init () {

+ 4 - 0
mallApp/src/pages/home/user.vue

@@ -108,6 +108,7 @@ import SpriteIcon from "@/components/sprite-icon/index.vue";
 import { getNavBarMetrics } from "@/utils/navBar";
 import { getShopUser } from "@/utils/auth";
 import { clearLogin } from "@/api/user";
+import { SHOP_NAV_ACTIVE_KEY } from "@/constant/storageKeys";
 
 export default {
   name: "user",
@@ -167,6 +168,9 @@ export default {
     }
   },
   onShow() {
+    // 从店铺订单 Tab 返回时恢复原生底栏,并清除店铺导航标记
+    uni.removeStorageSync(SHOP_NAV_ACTIVE_KEY);
+    uni.showTabBar({ animation: false });
     if (this.isLoggedIn) {
       getShopUser(true).then(() => {});
     }