ouyang 1 gün önce
ebeveyn
işleme
09ef84f6ae

+ 5 - 1
hdApp/src/admin/home/me.vue

@@ -384,8 +384,12 @@ export default {
       const number = Number(value) || 0;
       return number.toFixed(2);
     },
+    /**
+     * 续费:跳转年度会员开通页(与 cg/me 到期卡一致)
+     * 原 /admin/recharge/renew 已停用
+     */
     goRenew () {
-      //this.$util.pageTo({ url: "/admin/recharge/renew" })
+      this.$util.pageTo({ url: "/pagesClient/official/applyInvite", type: 2 })
     },
     handleMenuTap (item) {
       if (item.key == "address") {

+ 53 - 14
mallApp/src/pages/home/shop-category.vue

@@ -1,6 +1,7 @@
 <!--
   店铺分类页(合并花材 + 花束)
-  用途:商城消费者在一个页面浏览花材/花束分类、公告、广告与商品
+  用途:商城消费者在一个页面浏览花材/花束分类、公告、广告与商品;
+  顶部搜索框点搜索/键盘确认后跳转商品列表页,并带上 searchText 关键词
   路由:pages/home/shop-category?account=xxx&hdId=xxx
 -->
 <template>
@@ -9,15 +10,20 @@
     <view class="page-top">
       <view class="search-section">
         <view class="search-input-wrap">
-          <text class="search-placeholder-icon">🔍</text>
+          <view class="search-icon">
+            <zui-svg-icon icon="general-search" :width="24" :height="24" color="#999" />
+          </view>
           <input
             class="search-input"
-            v-model="searchKeyword"
-            placeholder="搜索鲜花、花束、绿植等"
-            disabled
+            type="text"
+            confirm-type="search"
+            :value="searchKeyword"
+            placeholder="搜索商品"
+            @input="onSearchKeywordInput"
+            @confirm="goSearchToGoodsList"
           />
         </view>
-        <view class="search-btn">搜索</view>
+        <view class="search-btn" @tap.stop="goSearchToGoodsList">搜索</view>
       </view>
 
       <!-- 通知公告:点击文案进详情,点击 › 进列表 -->
@@ -75,7 +81,7 @@
             <block v-if="itemExpanded">
               <view
                 v-for="cls in normalItemClasses"
-                :key="'item-top-' + cls.classId"
+                :key="cls.classId"
                 class="item-cate-sub"
                 :class="{ 'item-cate-sub--active': isItemNavActive(cls.classId) }"
                 :data-id="cls.classId"
@@ -93,7 +99,7 @@
         <!-- 花束分类(category/list,平铺展示) -->
         <view
           v-for="cat in bouquetCategories"
-          :key="'bouquet-' + cat.id"
+          :key="cat.id"
           class="nav-item nav-item--bouquet"
           :class="{ 'nav-item--active': isBouquetNavActive(cat.id) }"
           :data-id="cat.id"
@@ -138,7 +144,7 @@
             <block v-if="itemExpanded">
               <view
                 v-for="cls in normalItemClasses"
-                :key="'item-bottom-' + cls.classId"
+                :key="cls.classId"
                 class="item-cate-sub"
                 :class="{ 'item-cate-sub--active': isItemNavActive(cls.classId) }"
                 :data-id="cls.classId"
@@ -182,7 +188,7 @@
           <view class="ad-dots" v-if="adList.length > 1">
             <view
               v-for="(ad, dotIndex) in adList"
-              :key="'dot-' + dotIndex"
+              :key="dotIndex"
               class="ad-dot"
               :class="{ 'ad-dot--active': currentAdIndex === dotIndex }"
             ></view>
@@ -256,9 +262,10 @@
     <!-- 花材自定义扎数弹窗 -->
     <modal-module
       :show="isModel"
+      :use-custom-content="true"
       @cancel="modalCancel"
       @click="affirm"
-      :title="customData.itemName"
+      :title="modalTitle"
       color="#333"
       :size="32"
       padding="30rpx 30rpx"
@@ -383,6 +390,7 @@ export default {
       },
       cartItemShow: false,
       limitBuyWarnList: [],
+      /** 分类页搜索框输入,点搜索后带到 goods/list 的 searchText */
       searchKeyword: '',
       hdId: 0,
       shopAccount: '',
@@ -462,6 +470,11 @@ export default {
     },
     bouquetEmptyTitle() {
       return this.bouquetCategories.length ? '暂无商品' : '暂无分类'
+    },
+    /** 弹窗标题:保证传给 modal 的是字符串,避免空对象/异常类型 */
+    modalTitle() {
+      const name = this.customData && this.customData.itemName
+      return name ? String(name) : ''
     }
   },
   onShareAppMessage() {
@@ -509,6 +522,27 @@ export default {
     syncLoginStyle() {
       this.globalLoginStyle = this.$util.isEmpty(this.loginInfo) ? 0 : 1
     },
+    /**
+     * 同步搜索框内容(不用 v-model,与首页 topNav 一致,避免小程序端取值不稳)
+     */
+    onSearchKeywordInput(e) {
+      this.searchKeyword = (e && e.detail && e.detail.value != null) ? e.detail.value : ''
+    },
+    /**
+     * 分类页搜索:跳转商品列表并带上 searchText,由 list 页回填并请求
+     */
+    goSearchToGoodsList() {
+      const keyword = (this.searchKeyword || '').trim()
+      if (!keyword) {
+        this.$msg('请输入搜索内容')
+        return
+      }
+      const account = this.shopAccount || uni.getStorageSync('account') || ''
+      const hdId = this.hdId || uni.getStorageSync('hdId') || 0
+      this.$util.pageTo({
+        url: `/pages/goods/list?account=${account}&hdId=${hdId}&searchText=${encodeURIComponent(keyword)}`
+      })
+    },
     /** 拉取门店信息,同步 hdId 后再请求花材接口 */
     loadShopInfo() {
       return getInfo({ hdId: this.hdId }).then((res) => {
@@ -1070,10 +1104,15 @@ export default {
   border-radius: 36upx;
 }
 
-.search-placeholder-icon {
-  font-size: 28upx;
-  margin-right: 12upx;
+.search-icon {
   flex-shrink: 0;
+  width: 48upx;
+  height: 48upx;
+  margin-right: 8upx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  line-height: 1;
 }
 
 .search-input {

+ 53 - 30
mallApp/src/pages/interest/level.vue

@@ -20,22 +20,28 @@
       </view>
       <view class="level-list-wrap">
         <view class="level-list-tit">
-          <view>会员等级</view>
-          <view>成长值</view>
-          <view>折扣</view>
+          <view class="col-icon">图标</view>
+          <view class="col-level">等级</view>
+          <view class="col-growth">成长值</view>
+          <view class="col-discount">折扣</view>
         </view>
         <view class="level-list-blo">
           <view class="level-block" v-for="(item, index) in list" :key="index">
             <view class="level-list">
-              <view class="level-name-cell">
-                <appVipModule
+              <view class="col-icon">
+                <!-- 等级图标:雪碧图 member sheet,name 为 member-levle1~5 -->
+                <sprite-icon
                   class="level-img"
-                  :vipImgUrl="`${constant.imgUrl}/retail/member/grade/${item.level}.png`"
+                  :name="memberLevelIconName(item)"
+                  sheet="member"
+                  :height="64"
                 />
+              </view>
+              <view class="col-level">
                 <text class="level-name">{{ item.name || '会员' }}</text>
               </view>
-              <view>{{ formatGrowth(item) }}</view>
-              <view>{{ formatDiscount(item.discount) }}</view>
+              <view class="col-growth">{{ formatGrowth(item) }}</view>
+              <view class="col-discount">{{ formatDiscount(item.discount) }}</view>
             </view>
             <!-- 生日权益 / 会员权益说明:与 hd 后台设置字段一致,有内容才展示 -->
             <view class="level-extra" v-if="item.birthdayBenefit || item.benefitDesc">
@@ -62,14 +68,12 @@
 </template>
 
 <script>
-import AppVipModule from "@/components/module/app-vip";
 import AppTrademarkModule from "@/components/module/app-trademark";
 import { userGrade } from "@/api/member";
 
 export default {
   name: "interest-rank",
   components: {
-    AppVipModule,
     AppTrademarkModule
   },
   data() {
@@ -107,6 +111,20 @@ export default {
         }
       });
     },
+    /**
+     * 会员等级雪碧图 name:member-levle1~5(拼写与 sprite 图集一致)
+     * 优先 level/member,越界时回落到 1
+     */
+    memberLevelIconName(item) {
+      let level = Number((item && (item.level != null ? item.level : item.member)) || 1);
+      if (!level || level < 1) {
+        level = 1;
+      }
+      if (level > 5) {
+        level = 5;
+      }
+      return "member-levle" + level;
+    },
     /** 成长值门槛:优先 amount(新字段),兼容旧 growth */
     formatGrowth(item) {
       const val = item.amount != null && item.amount !== "" ? item.amount : item.growth;
@@ -171,29 +189,37 @@ export default {
     font-size: 26upx;
     .level-list-tit,
     .level-list {
+      border-bottom: 1upx solid #eeeeee;
       @include disFlex(center, space-between);
-      & > view {
-        margin: 0 auto;
-        flex: 1;
+      .col-icon,
+      .col-level,
+      .col-growth,
+      .col-discount {
         @include disFlex(center, center);
-        height: 70upx;
+        min-height: 88upx;
+        box-sizing: border-box;
+      }
+      .col-icon {
+        width: 140upx;
+        flex-shrink: 0;
+      }
+      .col-level {
+        flex: 1.2;
+        padding: 0 8upx;
+      }
+      .col-growth,
+      .col-discount {
+        flex: 1;
       }
       .level-img {
-        ::v-deep.vip-text {
-          left: 42upx;
-          transform: scale(0.8);
-        }
+        flex-shrink: 0;
       }
-    }
-    .level-name-cell {
-      flex-direction: column !important;
-      height: auto !important;
-      padding: 12upx 0;
       .level-name {
-        margin-top: 4upx;
-        font-size: 22upx;
-        color: #666;
-        line-height: 1.2;
+        font-size: 26upx;
+        color: #333;
+        text-align: center;
+        line-height: 1.3;
+        word-break: break-all;
       }
     }
     .level-list-tit {
@@ -208,9 +234,6 @@ export default {
     }
     .level-block {
       border-top: 1upx solid #eeeeee;
-      &:nth-child(2n) {
-        background-color: #f7f7ed;
-      }
       &:first-child {
         border-top: none;
       }

+ 260 - 248
mallApp/src/utils/mainIndexSprite.js

@@ -1,248 +1,260 @@
-/**
- * 雪碧图注册表
- * 用途:sprite-icon 按 sheet + name 从多张 CDN 雪碧图裁切显示图标
- * 图片地址随 ext.json / 环境 imgHost 变化,必须用函数运行时读取,不能用 this.$constant
- */
-import constant from "@/constant/index";
-
-/** 默认图床(ext 未配置 imgHost 时的兜底) */
-const DEFAULT_IMG_HOST = "https://img.huaml.com";
-
-/** 未传 sheet 时默认使用的雪碧图 key */
-const DEFAULT_SHEET_KEY = "main";
-
-/**
- * 雪碧图注册表:每张图含 path、原图尺寸、icons 坐标
- */
-export const SPRITE_SHEETS = {
-  main: {
-    path: "/hhb/sprite_icon/main-index.webp?time=2607171640",
-    width: 500,
-    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 },
-      "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 },
-      "shaixuan": { x: 71, y: 67, w: 25, h: 25 },
-      "dianpu-fill": { x: 116, y: 63, w: 31, h: 30 },
-      "huoche-cheng": { x: 171, y: 68, w: 23, h: 21 },
-      "huoche-lv": { x: 204, y: 68, w: 23, h: 21 },
-      "gou-quan": { x: 246, y: 68, w: 22, h: 21 },
-      "huashu": { x: 4, y: 118, w: 52, h: 49 },
-      "hua": { x: 94, y: 118, w: 52, h: 49 },
-      "dianpu-line": { x: 167, y: 114, w: 51, h: 51 },
-      "tuichu": { x: 243, y: 111, w: 49, h: 51 },
-      "xiaoxi": { x: 8, y: 186, w: 45, h: 45 },
-      "qianbao": { x: 68, y: 182, w: 55, h: 50 },
-      "huoche-da": { x: 142, y: 182, w: 60, h: 50 },
-      "baoguo": { x: 219, y: 182, w: 50, h: 50 },
-      "fanghu-yuan": { x: 282, y: 182, w: 42, h: 50 },
-      "youhuiquan-yellow": { x: 344, y: 184, w: 50, h: 50 },
-      "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 },
-      "icon-home": { x: 274, y: 102, w: 52, h: 52 },
-      "icon-home-active": { x: 274, y: 102, w: 52, h: 52 },
-      "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 },
-	  "icon-home": { x: 7, y: 245, w: 50, h: 50 },
-	  "icon-home-active": { x: 60, y: 245, w: 50, h: 50 },
-	  "icon-category": { x: 116, y: 245, w: 50, h: 50 },
-	  "icon-category-active": { x: 176, y: 245, w: 50, h: 50 },
-	  "icon-cart": { x: 238, y: 245, w: 50, h: 50 },
-	  "icon-cart-active": { x: 293, y: 245, w: 50, h: 50 },
-	  "icon-order": { x: 366, y: 245, w: 50, h: 50 },
-	  "icon-order-active": { x: 418, y: 245 , w: 50, h: 50 },
-	  "cart-delete": { x: 7, y: 298 , w: 46, h: 52 },//购物车-删除
-	  "notice": { x: 216, y: 15 , w: 31, h: 31 },//公告通知
-    }
-  },
-  /** 进货/供货商专用雪碧图,坐标待设计稿测量后补全 */
-  order: {
-    path: "/hhb/sprite_icon/icon-order.webp?time=26072009",
-    width: 600,
-    height: 600,
-    icons: {
-		"ps-shop": { x: 257, y: 8 , w: 57, h: 52 },//商家配送
-		"ps-shop-active": { x: 190, y: 8 , w: 57, h: 52 },//商家配送-active
-		"ps-paotui": { x: 198, y: 63 , w: 60, h: 52 },//跑腿配送
-		"ps-paotui-active": { x: 129, y: 63 , w: 60, h: 52 },//跑腿配送-active
-		"ps-daodian": { x: 383, y: 8 , w: 48, h: 50 },//到店自取
-		"ps-daodian-active": { x: 324, y: 8 , w: 49, h: 50 },//到店自取-active
-		"ps-kuaidi": { x: 72, y: 63 , w: 49, h: 50 },//快递
-		"ps-kuaidi-active": { x: 12, y: 63 , w: 49, h: 50 },//快递-active
-		"ps-wuliu": { x: 441, y: 8 , w: 67, h: 52 },//物流
-		"ps-wuliu-active": { x: 518, y: 8 , w: 67, h: 52 },//物流-active
-		"huoche": { x: 7, y: 8 , w: 46, h: 50 },//配送说明-小货车
-		"youhuiquan": { x: 267, y: 63 , w: 58, h: 50 },//优惠券
-		"beizhu": { x: 12, y: 8 , w: 50, h: 50 },//订单-备注
-		"shijian": { x: 70, y: 8 , w: 52, h: 52 },//配送时间
-		"shijianduan": { x: 129, y: 8 , w: 52, h: 52 },//配送时间段
-		"dingwei": { x: 339, y: 63 , w: 42, h: 51 },//定位
-	}
-  }
-};
-
-/** tabBar 未选中 / 选中 图标名映射(main 雪碧图) */
-export const TAB_SPRITE_MAP = {
-  home: { normal: "dianpu-line", active: "dianpu-fill" },
-  order: { normal: "huoche-lv", active: "huoche-da" },
-  mine: { normal: "gou-quan", active: "qianbao" }
-};
-
-/** @deprecated 兼容旧引用 */
-export const MAIN_INDEX_SPRITE = {
-  width: SPRITE_SHEETS.main.width,
-  height: SPRITE_SHEETS.main.height
-};
-
-/** @deprecated 兼容旧引用 */
-export const MAIN_INDEX_ICONS = SPRITE_SHEETS.main.icons;
-
-function buildIconSheetIndex(sheets) {
-  const index = {};
-  Object.keys(sheets).forEach((sheetKey) => {
-    const icons = sheets[sheetKey].icons || {};
-    Object.keys(icons).forEach((name) => {
-      if (index[name] && index[name] !== sheetKey && process.env.NODE_ENV !== "production") {
-        console.warn(`[sprite] 图标名 "${name}" 在 ${index[name]} 与 ${sheetKey} 中重复,以 ${sheetKey} 为准`);
-      }
-      index[name] = sheetKey;
-    });
-  });
-  return index;
-}
-
-const ICON_SHEET_INDEX = buildIconSheetIndex(SPRITE_SHEETS);
-
-function getImgHostBase() {
-  let base = (constant.imgUrl || DEFAULT_IMG_HOST).replace(/\/$/, "");
-  if (base.indexOf("http://") === 0) {
-    base = `https://${base.slice(7)}`;
-  }
-  return base;
-}
-
-function rpx2px(rpx) {
-  if (typeof uni !== "undefined" && typeof uni.upx2px === "function") {
-    return uni.upx2px(rpx);
-  }
-  return rpx;
-}
-
-/**
- * 计算图标显示宽高与缩放比(支持按宽度 size 或按高度 height 等比缩放)
- * @param {{ w: number, h: number }} icon 雪碧图坐标中的原始宽高
- * @param {{ size?: number|string, height?: number|string }} opts
- * @returns {{ displayW: number, displayH: number, scale: number }}
- */
-function resolveSpriteDisplaySize(icon, opts = {}) {
-  const heightNum = Number(opts.height);
-  const useHeight = opts.height !== "" && opts.height != null && !Number.isNaN(heightNum) && heightNum > 0;
-
-  let scale;
-  let displayW;
-  let displayH;
-
-  if (useHeight) {
-    displayH = Math.round(heightNum);
-    scale = displayH / icon.h;
-    displayW = Math.round(icon.w * scale);
-  } else {
-    const sizeNum = Number(opts.size);
-    displayW = !Number.isNaN(sizeNum) && sizeNum > 0 ? sizeNum : 48;
-    scale = displayW / icon.w;
-    displayH = Math.round(icon.h * scale);
-  }
-
-  return { displayW, displayH, scale };
-}
-
-export function resolveSprite(name, sheetKey = "") {
-  if (!name) {
-    return null;
-  }
-  let key = sheetKey;
-  if (!key) {
-    key = ICON_SHEET_INDEX[name] || DEFAULT_SHEET_KEY;
-  }
-  const sheet = SPRITE_SHEETS[key];
-  if (!sheet) {
-    return null;
-  }
-  const icon = (sheet.icons || {})[name];
-  if (!icon) {
-    return null;
-  }
-  return { sheetKey: key, sheet, icon };
-}
-
-export function getSpriteSheetUrl(sheetKey = DEFAULT_SHEET_KEY) {
-  const sheet = SPRITE_SHEETS[sheetKey];
-  if (!sheet) {
-    return "";
-  }
-  return `${getImgHostBase()}${sheet.path}`;
-}
-
-export function spriteImageStyle(name, size = 48, sheetKey = "", height = "") {
-  const resolved = resolveSprite(name, sheetKey);
-  if (!resolved) {
-    if (process.env.NODE_ENV !== "production") {
-      console.warn(`[sprite-icon] 未找到图标: name=${name}${sheetKey ? ` sheet=${sheetKey}` : ""}`);
-    }
-    return { wrap: "", inner: "", image: "" };
-  }
-  const { sheet, icon } = resolved;
-  const { displayW, displayH, scale } = resolveSpriteDisplaySize(icon, { size, height });
-  const sheetW = sheet.width * scale;
-  const sheetH = sheet.height * scale;
-  const left = -rpx2px(icon.x * scale);
-  const top = -rpx2px(icon.y * scale);
-  const sheetWPx = rpx2px(sheetW);
-  const sheetHPx = rpx2px(sheetH);
-  return {
-    wrap: `position:relative;width:${displayW}rpx;height:${displayH}rpx;overflow:hidden;line-height:0;`,
-    inner: `position:absolute;left:${left}px;top:${top}px;width:${sheetWPx}px;height:${sheetHPx}px;`,
-    image: "width:100%;height:100%;display:block;"
-  };
-}
-
-export function spriteStyle(name, size = 48, sheetKey = "", height = "") {
-  const resolved = resolveSprite(name, sheetKey);
-  if (!resolved) {
-    return "";
-  }
-  const { sheetKey: key, sheet, icon } = resolved;
-  const { displayW, displayH, scale } = resolveSpriteDisplaySize(icon, { size, height });
-  const sheetW = sheet.width * scale;
-  const sheetH = sheet.height * scale;
-  const posX = rpx2px(icon.x * scale);
-  const posY = rpx2px(icon.y * scale);
-  const sheetWPx = rpx2px(sheetW);
-  const sheetHPx = rpx2px(sheetH);
-  const url = getSpriteSheetUrl(key);
-  return [
-    `width:${displayW}rpx`,
-    `height:${displayH}rpx`,
-    `background-image:url(${url})`,
-    `background-size:${sheetWPx}px ${sheetHPx}px`,
-    `background-position:-${posX}px -${posY}px`,
-    "background-repeat:no-repeat",
-    "display:inline-block",
-    "flex-shrink:0",
-    "vertical-align:middle"
-  ].join(";");
-}
-
-export function getMainIndexSpriteUrl() {
-  return getSpriteSheetUrl(DEFAULT_SHEET_KEY);
-}
-
-export function mainIndexSpriteImageStyle(name, size = 48, height = "") {
-  return spriteImageStyle(name, size, DEFAULT_SHEET_KEY, height);
-}
-
-export function mainIndexSpriteStyle(name, size = 48, height = "") {
-  return spriteStyle(name, size, DEFAULT_SHEET_KEY, height);
-}
+/**
+ * 雪碧图注册表
+ * 用途:sprite-icon 按 sheet + name 从多张 CDN 雪碧图裁切显示图标
+ * 图片地址随 ext.json / 环境 imgHost 变化,必须用函数运行时读取,不能用 this.$constant
+ */
+import constant from "@/constant/index";
+
+/** 默认图床(ext 未配置 imgHost 时的兜底) */
+const DEFAULT_IMG_HOST = "https://img.huaml.com";
+
+/** 未传 sheet 时默认使用的雪碧图 key */
+const DEFAULT_SHEET_KEY = "main";
+
+/**
+ * 雪碧图注册表:每张图含 path、原图尺寸、icons 坐标
+ */
+export const SPRITE_SHEETS = {
+  main: {
+    path: "/hhb/sprite_icon/main-index.webp?time=2607171640",
+    width: 500,
+    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 },
+      "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 },
+      "shaixuan": { x: 71, y: 67, w: 25, h: 25 },
+      "dianpu-fill": { x: 116, y: 63, w: 31, h: 30 },
+      "huoche-cheng": { x: 171, y: 68, w: 23, h: 21 },
+      "huoche-lv": { x: 204, y: 68, w: 23, h: 21 },
+      "gou-quan": { x: 246, y: 68, w: 22, h: 21 },
+      "huashu": { x: 4, y: 118, w: 52, h: 49 },
+      "hua": { x: 94, y: 118, w: 52, h: 49 },
+      "dianpu-line": { x: 167, y: 114, w: 51, h: 51 },
+      "tuichu": { x: 243, y: 111, w: 49, h: 51 },
+      "xiaoxi": { x: 8, y: 186, w: 45, h: 45 },
+      "qianbao": { x: 68, y: 182, w: 55, h: 50 },
+      "huoche-da": { x: 142, y: 182, w: 60, h: 50 },
+      "baoguo": { x: 219, y: 182, w: 50, h: 50 },
+      "fanghu-yuan": { x: 282, y: 182, w: 42, h: 50 },
+      "youhuiquan-yellow": { x: 344, y: 184, w: 50, h: 50 },
+      "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 },
+      "icon-home": { x: 274, y: 102, w: 52, h: 52 },
+      "icon-home-active": { x: 274, y: 102, w: 52, h: 52 },
+      "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 },
+	  "icon-home": { x: 7, y: 245, w: 50, h: 50 },
+	  "icon-home-active": { x: 60, y: 245, w: 50, h: 50 },
+	  "icon-category": { x: 116, y: 245, w: 50, h: 50 },
+	  "icon-category-active": { x: 176, y: 245, w: 50, h: 50 },
+	  "icon-cart": { x: 238, y: 245, w: 50, h: 50 },
+	  "icon-cart-active": { x: 293, y: 245, w: 50, h: 50 },
+	  "icon-order": { x: 366, y: 245, w: 50, h: 50 },
+	  "icon-order-active": { x: 418, y: 245 , w: 50, h: 50 },
+	  "cart-delete": { x: 7, y: 298 , w: 46, h: 52 },//购物车-删除
+	  "notice": { x: 216, y: 15 , w: 31, h: 31 },//公告通知
+    }
+  },
+  /** 进货/供货商专用雪碧图,坐标待设计稿测量后补全 */
+  order: {
+    path: "/hhb/sprite_icon/icon-order.webp?time=26072009",
+    width: 600,
+    height: 600,
+    icons: {
+		"ps-shop": { x: 257, y: 8 , w: 57, h: 52 },//商家配送
+		"ps-shop-active": { x: 190, y: 8 , w: 57, h: 52 },//商家配送-active
+		"ps-paotui": { x: 198, y: 63 , w: 60, h: 52 },//跑腿配送
+		"ps-paotui-active": { x: 129, y: 63 , w: 60, h: 52 },//跑腿配送-active
+		"ps-daodian": { x: 383, y: 8 , w: 48, h: 50 },//到店自取
+		"ps-daodian-active": { x: 324, y: 8 , w: 49, h: 50 },//到店自取-active
+		"ps-kuaidi": { x: 72, y: 63 , w: 49, h: 50 },//快递
+		"ps-kuaidi-active": { x: 12, y: 63 , w: 49, h: 50 },//快递-active
+		"ps-wuliu": { x: 441, y: 8 , w: 67, h: 52 },//物流
+		"ps-wuliu-active": { x: 518, y: 8 , w: 67, h: 52 },//物流-active
+		"huoche": { x: 7, y: 8 , w: 46, h: 50 },//配送说明-小货车
+		"youhuiquan": { x: 267, y: 63 , w: 58, h: 50 },//优惠券
+		"beizhu": { x: 12, y: 8 , w: 50, h: 50 },//订单-备注
+		"shijian": { x: 70, y: 8 , w: 52, h: 52 },//配送时间
+		"shijianduan": { x: 129, y: 8 , w: 52, h: 52 },//配送时间段
+		"dingwei": { x: 339, y: 63 , w: 42, h: 51 },//定位
+	}
+  },
+  member: {
+    path: "/hhb/sprite_icon/icon-member.webp",
+    width: 500,
+    height: 500,
+    icons: {
+  		"member-levle1": { x: 0, y: 0, w: 80, h: 80 },
+  		"member-levle2": { x: 80, y: 0, w: 80, h: 80 },
+  		"member-levle3": { x: 160, y: 0, w: 80, h: 80 },
+  		"member-levle4": { x: 240, y: 0, w: 80, h: 80 },
+  		"member-levle5": { x: 320, y: 0, w: 80, h: 80 },
+	}
+  }
+};
+
+/** tabBar 未选中 / 选中 图标名映射(main 雪碧图) */
+export const TAB_SPRITE_MAP = {
+  home: { normal: "dianpu-line", active: "dianpu-fill" },
+  order: { normal: "huoche-lv", active: "huoche-da" },
+  mine: { normal: "gou-quan", active: "qianbao" }
+};
+
+/** @deprecated 兼容旧引用 */
+export const MAIN_INDEX_SPRITE = {
+  width: SPRITE_SHEETS.main.width,
+  height: SPRITE_SHEETS.main.height
+};
+
+/** @deprecated 兼容旧引用 */
+export const MAIN_INDEX_ICONS = SPRITE_SHEETS.main.icons;
+
+function buildIconSheetIndex(sheets) {
+  const index = {};
+  Object.keys(sheets).forEach((sheetKey) => {
+    const icons = sheets[sheetKey].icons || {};
+    Object.keys(icons).forEach((name) => {
+      if (index[name] && index[name] !== sheetKey && process.env.NODE_ENV !== "production") {
+        console.warn(`[sprite] 图标名 "${name}" 在 ${index[name]} 与 ${sheetKey} 中重复,以 ${sheetKey} 为准`);
+      }
+      index[name] = sheetKey;
+    });
+  });
+  return index;
+}
+
+const ICON_SHEET_INDEX = buildIconSheetIndex(SPRITE_SHEETS);
+
+function getImgHostBase() {
+  let base = (constant.imgUrl || DEFAULT_IMG_HOST).replace(/\/$/, "");
+  if (base.indexOf("http://") === 0) {
+    base = `https://${base.slice(7)}`;
+  }
+  return base;
+}
+
+function rpx2px(rpx) {
+  if (typeof uni !== "undefined" && typeof uni.upx2px === "function") {
+    return uni.upx2px(rpx);
+  }
+  return rpx;
+}
+
+/**
+ * 计算图标显示宽高与缩放比(支持按宽度 size 或按高度 height 等比缩放)
+ * @param {{ w: number, h: number }} icon 雪碧图坐标中的原始宽高
+ * @param {{ size?: number|string, height?: number|string }} opts
+ * @returns {{ displayW: number, displayH: number, scale: number }}
+ */
+function resolveSpriteDisplaySize(icon, opts = {}) {
+  const heightNum = Number(opts.height);
+  const useHeight = opts.height !== "" && opts.height != null && !Number.isNaN(heightNum) && heightNum > 0;
+
+  let scale;
+  let displayW;
+  let displayH;
+
+  if (useHeight) {
+    displayH = Math.round(heightNum);
+    scale = displayH / icon.h;
+    displayW = Math.round(icon.w * scale);
+  } else {
+    const sizeNum = Number(opts.size);
+    displayW = !Number.isNaN(sizeNum) && sizeNum > 0 ? sizeNum : 48;
+    scale = displayW / icon.w;
+    displayH = Math.round(icon.h * scale);
+  }
+
+  return { displayW, displayH, scale };
+}
+
+export function resolveSprite(name, sheetKey = "") {
+  if (!name) {
+    return null;
+  }
+  let key = sheetKey;
+  if (!key) {
+    key = ICON_SHEET_INDEX[name] || DEFAULT_SHEET_KEY;
+  }
+  const sheet = SPRITE_SHEETS[key];
+  if (!sheet) {
+    return null;
+  }
+  const icon = (sheet.icons || {})[name];
+  if (!icon) {
+    return null;
+  }
+  return { sheetKey: key, sheet, icon };
+}
+
+export function getSpriteSheetUrl(sheetKey = DEFAULT_SHEET_KEY) {
+  const sheet = SPRITE_SHEETS[sheetKey];
+  if (!sheet) {
+    return "";
+  }
+  return `${getImgHostBase()}${sheet.path}`;
+}
+
+export function spriteImageStyle(name, size = 48, sheetKey = "", height = "") {
+  const resolved = resolveSprite(name, sheetKey);
+  if (!resolved) {
+    if (process.env.NODE_ENV !== "production") {
+      console.warn(`[sprite-icon] 未找到图标: name=${name}${sheetKey ? ` sheet=${sheetKey}` : ""}`);
+    }
+    return { wrap: "", inner: "", image: "" };
+  }
+  const { sheet, icon } = resolved;
+  const { displayW, displayH, scale } = resolveSpriteDisplaySize(icon, { size, height });
+  const sheetW = sheet.width * scale;
+  const sheetH = sheet.height * scale;
+  const left = -rpx2px(icon.x * scale);
+  const top = -rpx2px(icon.y * scale);
+  const sheetWPx = rpx2px(sheetW);
+  const sheetHPx = rpx2px(sheetH);
+  return {
+    wrap: `position:relative;width:${displayW}rpx;height:${displayH}rpx;overflow:hidden;line-height:0;`,
+    inner: `position:absolute;left:${left}px;top:${top}px;width:${sheetWPx}px;height:${sheetHPx}px;`,
+    image: "width:100%;height:100%;display:block;"
+  };
+}
+
+export function spriteStyle(name, size = 48, sheetKey = "", height = "") {
+  const resolved = resolveSprite(name, sheetKey);
+  if (!resolved) {
+    return "";
+  }
+  const { sheetKey: key, sheet, icon } = resolved;
+  const { displayW, displayH, scale } = resolveSpriteDisplaySize(icon, { size, height });
+  const sheetW = sheet.width * scale;
+  const sheetH = sheet.height * scale;
+  const posX = rpx2px(icon.x * scale);
+  const posY = rpx2px(icon.y * scale);
+  const sheetWPx = rpx2px(sheetW);
+  const sheetHPx = rpx2px(sheetH);
+  const url = getSpriteSheetUrl(key);
+  return [
+    `width:${displayW}rpx`,
+    `height:${displayH}rpx`,
+    `background-image:url(${url})`,
+    `background-size:${sheetWPx}px ${sheetHPx}px`,
+    `background-position:-${posX}px -${posY}px`,
+    "background-repeat:no-repeat",
+    "display:inline-block",
+    "flex-shrink:0",
+    "vertical-align:middle"
+  ].join(";");
+}
+
+export function getMainIndexSpriteUrl() {
+  return getSpriteSheetUrl(DEFAULT_SHEET_KEY);
+}
+
+export function mainIndexSpriteImageStyle(name, size = 48, height = "") {
+  return spriteImageStyle(name, size, DEFAULT_SHEET_KEY, height);
+}
+
+export function mainIndexSpriteStyle(name, size = 48, height = "") {
+  return spriteStyle(name, size, DEFAULT_SHEET_KEY, height);
+}