Przeglądaj źródła

零售端-雪碧图支持多张

ouyang 3 tygodni temu
rodzic
commit
c28987dfe5

+ 21 - 9
mallApp/src/components/sprite-icon/index.vue

@@ -1,5 +1,6 @@
 <!--
   雪碧图图标组件
+  用途:按 sheet + name 从 SPRITE_SHEETS 裁切显示图标,支持多张雪碧图
   小程序:外层裁切 + 内层 view absolute 位移 + image 铺满(position 不能写在 image 上)
 -->
 <template>
@@ -21,7 +22,7 @@
 </template>
 
 <script>
-import { getMainIndexSpriteUrl, mainIndexSpriteImageStyle } from "@/utils/mainIndexSprite";
+import { getSpriteSheetUrl, resolveSprite, spriteImageStyle } from "@/utils/mainIndexSprite";
 
 export default {
   name: "SpriteIcon",
@@ -29,11 +30,16 @@ export default {
     externalClasses: ["custom-class"]
   },
   props: {
-    /** MAIN_INDEX_ICONS 中的图标名 */
+    /** 图标名,需在对应 sheet 的 icons 中配置 */
     name: {
       type: String,
       required: true
     },
+    /** 雪碧图 key,如 main / stock;不传则按图标名自动匹配 */
+    sheet: {
+      type: String,
+      default: ""
+    },
     /** 显示宽度 rpx */
     size: {
       type: [Number, String],
@@ -46,19 +52,25 @@ export default {
     }
   },
   computed: {
-    spriteUrl () {
-      return getMainIndexSpriteUrl();
+    resolvedSprite() {
+      return resolveSprite(this.name, this.sheet);
+    },
+    spriteUrl() {
+      if (!this.resolvedSprite) {
+        return "";
+      }
+      return getSpriteSheetUrl(this.resolvedSprite.sheetKey);
     },
-    spriteLayout () {
-      return mainIndexSpriteImageStyle(this.name, this.size);
+    spriteLayout() {
+      return spriteImageStyle(this.name, this.size, this.sheet);
     },
-    wrapStyle () {
+    wrapStyle() {
       return this.spriteLayout.wrap;
     },
-    innerStyle () {
+    innerStyle() {
       return this.spriteLayout.inner;
     },
-    imageStyle () {
+    imageStyle() {
       return this.spriteLayout.image;
     }
   }

+ 130 - 74
mallApp/src/utils/mainIndexSprite.js

@@ -1,74 +1,98 @@
 /**
- * main_index.webp 雪碧图配置
+ * 雪碧图注册表
+ * 用途:sprite-icon 按 sheet + name 从多张 CDN 雪碧图裁切显示图标
  * 图片地址随 ext.json / 环境 imgHost 变化,必须用函数运行时读取,不能用 this.$constant
  */
 import constant from "@/constant/index";
 
-/** 雪碧图相对路径(拼在 imgHost 后) */
-const MAIN_INDEX_SPRITE_PATH = "/ghs/main_index.webp?time=2607101747";
-
 /** 默认图床(ext 未配置 imgHost 时的兜底) */
 const DEFAULT_IMG_HOST = "https://img.huaml.com";
 
-/**
- * 获取雪碧图完整 URL(运行时读取 imgHost)
- * @returns {string}
- */
-export function getMainIndexSpriteUrl() {
-  let base = (constant.imgUrl || DEFAULT_IMG_HOST).replace(/\/$/, "");
-  // 真机端网络图要求 HTTPS,ext 里 imgHost 常为 http
-  if (base.indexOf("http://") === 0) {
-    base = `https://${base.slice(7)}`;
-  }
-  return `${base}${MAIN_INDEX_SPRITE_PATH}`;
-}
-
-export const MAIN_INDEX_SPRITE = {
-  width: 500,
-  height: 500
-};
+/** 未传 sheet 时默认使用的雪碧图 key */
+const DEFAULT_SHEET_KEY = "main";
 
 /**
- * 雪碧图内各图标坐标(基于 500×500 原图测量)
- * x/y 为图标左上角,w/h 为截取区域宽高
+ * 雪碧图注册表:每张图含 path、原图尺寸、icons 坐标
  */
-export const MAIN_INDEX_ICONS = {
-  "dengji-biandong": { x: 14, y: 17, w: 32, h: 28 },
-  "yue-biandong": { x: 60, y: 17, w: 28, h: 28 },
-  "huiyuan-quanyi": { x: 102, y: 17, w: 28, h: 28 },
-  "lianxikefu": { x: 145, y: 17, w: 28, h: 28 },
-  "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: 43, h: 44 },
-  "qianbao": { x: 65, y: 180, w: 58, h: 52 },
-  "huoche-da": { x: 140, y: 184, w: 63, h: 53 },
-  "baoguo": { x: 218, y: 180, w: 57, h: 53 },
-  "fanghu-yuan": { x: 299, y: 174, w: 53, h: 61 },
-  "youhuiquan": { x: 382, y: 183, w: 51, h: 49 },
-  "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 }
+export const SPRITE_SHEETS = {
+  main: {
+    path: "/ghs/main_index.webp?time=2607101747",
+    width: 500,
+    height: 500,
+    icons: {
+      "dengji-biandong": { x: 14, y: 17, w: 32, h: 28 },
+      "yue-biandong": { x: 60, y: 17, w: 28, h: 28 },
+      "huiyuan-quanyi": { x: 102, y: 17, w: 28, h: 28 },
+      "lianxikefu": { x: 145, y: 17, w: 28, h: 28 },
+      "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: 43, h: 44 },
+      "qianbao": { x: 65, y: 180, w: 58, h: 52 },
+      "huoche-da": { x: 140, y: 184, w: 63, h: 53 },
+      "baoguo": { x: 218, y: 180, w: 57, h: 53 },
+      "fanghu-yuan": { x: 299, y: 174, w: 53, h: 61 },
+      "youhuiquan": { x: 382, y: 183, w: 51, h: 49 },
+      "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 }
+    }
+  },
+  /** 进货/供货商专用雪碧图,坐标待设计稿测量后补全 */
+  stock: {
+    path: "/retail/stock_sprite.webp?time=2607140000",
+    width: 500,
+    height: 500,
+    icons: {}
+  }
 };
 
-/** tabBar 未选中 / 选中 图标名映射 */
+/** 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" }
 };
 
-/**
- * rpx 转 px;小程序 transform / margin 位移必须用 px,rpx 在 transform 里常不生效
- * @param {number} rpx
- * @returns {number}
- */
+/** @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);
@@ -76,22 +100,47 @@ function rpx2px(rpx) {
   return rpx;
 }
 
-/**
- * 生成雪碧图 image 裁切样式(小程序用 absolute 定位,避免 transform/rpx 兼容问题)
- * @param {string} name
- * @param {number|string} size 显示宽度 rpx
- * @returns {{ wrap: string, inner: string, image: string }}
- */
-export function mainIndexSpriteImageStyle(name, size = 48) {
-  const icon = MAIN_INDEX_ICONS[name];
+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 = "") {
+  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 = Number(size) || 48;
   const scale = displayW / icon.w;
   const displayH = Math.round(icon.h * scale);
-  const sheetW = MAIN_INDEX_SPRITE.width * scale;
-  const sheetH = MAIN_INDEX_SPRITE.height * scale;
+  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);
@@ -103,27 +152,22 @@ export function mainIndexSpriteImageStyle(name, size = 48) {
   };
 }
 
-/**
- * 生成雪碧图行内样式(view + background,小程序端裁切最稳定)
- * @param {string} name
- * @param {number|string} size 显示宽度 rpx
- * @returns {string}
- */
-export function mainIndexSpriteStyle(name, size = 48) {
-  const icon = MAIN_INDEX_ICONS[name];
-  if (!icon) {
+export function spriteStyle(name, size = 48, sheetKey = "") {
+  const resolved = resolveSprite(name, sheetKey);
+  if (!resolved) {
     return "";
   }
+  const { sheetKey: key, sheet, icon } = resolved;
   const displayW = Number(size) || 48;
   const scale = displayW / icon.w;
   const displayH = Math.round(icon.h * scale);
-  const sheetW = MAIN_INDEX_SPRITE.width * scale;
-  const sheetH = MAIN_INDEX_SPRITE.height * scale;
+  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 = getMainIndexSpriteUrl();
+  const url = getSpriteSheetUrl(key);
   return [
     `width:${displayW}rpx`,
     `height:${displayH}rpx`,
@@ -136,3 +180,15 @@ export function mainIndexSpriteStyle(name, size = 48) {
     "vertical-align:middle"
   ].join(";");
 }
+
+export function getMainIndexSpriteUrl() {
+  return getSpriteSheetUrl(DEFAULT_SHEET_KEY);
+}
+
+export function mainIndexSpriteImageStyle(name, size = 48) {
+  return spriteImageStyle(name, size, DEFAULT_SHEET_KEY);
+}
+
+export function mainIndexSpriteStyle(name, size = 48) {
+  return spriteStyle(name, size, DEFAULT_SHEET_KEY);
+}