Просмотр исходного кода

花掌柜新增雪碧图图标调用

ouyang 3 недель назад
Родитель
Сommit
035eb90bb1

+ 94 - 0
hdApp/src/components/sprite-icon/index.vue

@@ -0,0 +1,94 @@
+<!--
+  雪碧图图标组件
+  用途:按 sheet + name 从 SPRITE_SHEETS 裁切显示图标,支持多张雪碧图
+  小程序:外层裁切 + 内层 view absolute 位移 + image 铺满(position 不能写在 image 上)
+-->
+<template>
+  <view
+    class="sprite-icon"
+    :class="customClass"
+    :style="wrapStyle"
+    @click="$emit('click', $event)"
+  >
+    <view class="sprite-inner" :style="innerStyle">
+      <image
+        class="sprite-image"
+        :src="spriteUrl"
+        :style="imageStyle"
+        mode="scaleToFill"
+      />
+    </view>
+  </view>
+</template>
+
+<script>
+import { getSpriteSheetUrl, resolveSprite, spriteImageStyle } from "@/utils/mainIndexSprite";
+
+export default {
+  name: "SpriteIcon",
+  options: {
+    externalClasses: ["custom-class"]
+  },
+  props: {
+    /** 图标名,需在对应 sheet 的 icons 中配置 */
+    name: {
+      type: String,
+      required: true
+    },
+    /** 雪碧图 key,如 main / stock;不传则按图标名自动匹配 */
+    sheet: {
+      type: String,
+      default: ""
+    },
+    /** 显示宽度 rpx */
+    size: {
+      type: [Number, String],
+      default: 48
+    },
+    /** 额外 class,配合页面 ::v-deep 写间距 */
+    customClass: {
+      type: [String, Array, Object],
+      default: ""
+    }
+  },
+  computed: {
+    resolvedSprite() {
+      return resolveSprite(this.name, this.sheet);
+    },
+    spriteUrl() {
+      if (!this.resolvedSprite) {
+        return "";
+      }
+      return getSpriteSheetUrl(this.resolvedSprite.sheetKey);
+    },
+    spriteLayout() {
+      return spriteImageStyle(this.name, this.size, this.sheet);
+    },
+    wrapStyle() {
+      return this.spriteLayout.wrap;
+    },
+    innerStyle() {
+      return this.spriteLayout.inner;
+    },
+    imageStyle() {
+      return this.spriteLayout.image;
+    }
+  }
+};
+</script>
+
+<style scoped>
+.sprite-icon {
+  display: inline-block;
+  flex-shrink: 0;
+  vertical-align: middle;
+}
+
+.sprite-inner {
+  line-height: 0;
+}
+
+.sprite-image {
+  display: block;
+}
+</style>

+ 3 - 0
hdApp/src/main.js

@@ -15,6 +15,9 @@ Vue.mixin(globalMixins);
 import kdEntrance from "@/components/kd_entrance";
 Vue.component('kd-entrance', kdEntrance)
 
+import SpriteIcon from '@/components/sprite-icon/index.vue';
+Vue.component('sprite-icon', SpriteIcon);
+
 import store from "./store";
 Vue.prototype.$store = store;
 

+ 6 - 0
hdApp/src/pages.json

@@ -1,4 +1,10 @@
 {
+	"easycom": {
+		"autoscan": true,
+		"custom": {
+			"^sprite-icon$": "@/components/sprite-icon/index.vue"
+		}
+	},
 	"pages": [
 		{ "path": "admin/home/workbench", "style": { "navigationBarTitleText": "进货", "enablePullDownRefresh": true, "navigationStyle": "custom" } },
 		{ "path": "admin/home/mall", "style": { "navigationBarTitleText": "店铺", "enablePullDownRefresh": true, "navigationStyle": "custom" } },

+ 218 - 0
hdApp/src/utils/mainIndexSprite.js

@@ -0,0 +1,218 @@
+/**
+ * 雪碧图注册表
+ * 用途:sprite-icon 按 sheet + name 从多张 CDN 雪碧图裁切显示图标
+ * 图片地址随 ext.json / 环境 imgHost 变化,必须用函数运行时读取
+ */
+import constant from "@/constant/index";
+
+/** 默认图床(ext 未配置 imgHost 时的兜底) */
+const DEFAULT_IMG_HOST = "https://img.huaml.com";
+
+/** 未传 sheet 时默认使用的雪碧图 key */
+const DEFAULT_SHEET_KEY = "main";
+
+/**
+ * 雪碧图注册表:每张图含 path、原图尺寸、icons 坐标
+ * icons 的 x/y 为左上角,w/h 为截取区域(基于原图测量)
+ */
+export const SPRITE_SHEETS = {
+  main: {
+    path: "/hzg/sprite_icon/main_index.webp",
+    width: 500,
+    height: 500,
+    icons: {
+      "dengji-biandong": { x: 14, y: 17, w: 32, h: 28 },
+    }
+  },
+  /** 进货/供货商专用雪碧图,坐标待设计稿测量后补全 */
+  /* stock: {
+    path: "/hzg/sprite_icon/main_index.webp?time=2607101747",
+    width: 500,
+    height: 500,
+    icons: {
+      "yue-biandong": { x: 60, y: 17, w: 28, h: 28 }
+				}
+  } */
+};
+
+/** 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 兼容旧引用,等同 SPRITE_SHEETS.main 尺寸 */
+export const MAIN_INDEX_SPRITE = {
+  width: SPRITE_SHEETS.main.width,
+  height: SPRITE_SHEETS.main.height
+};
+
+/** @deprecated 兼容旧引用,等同 SPRITE_SHEETS.main.icons */
+export const MAIN_INDEX_ICONS = SPRITE_SHEETS.main.icons;
+
+/**
+ * 构建全局唯一图标名 -> sheetKey 索引
+ * @param {object} sheets
+ * @returns {object}
+ */
+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);
+
+/**
+ * 获取图床根地址(HTTPS)
+ * @returns {string}
+ */
+function getImgHostBase() {
+  let base = (constant.imgUrl || DEFAULT_IMG_HOST).replace(/\/$/, "");
+  if (base.indexOf("http://") === 0) {
+    base = `https://${base.slice(7)}`;
+  }
+  return base;
+}
+
+/**
+ * rpx 转 px;小程序 absolute 位移必须用 px
+ * @param {number} rpx
+ * @returns {number}
+ */
+function rpx2px(rpx) {
+  if (typeof uni !== "undefined" && typeof uni.upx2px === "function") {
+    return uni.upx2px(rpx);
+  }
+  return rpx;
+}
+
+/**
+ * 解析图标所属雪碧图与坐标
+ * @param {string} name 图标名
+ * @param {string} sheetKey 可选,指定雪碧图 key;空则按索引或 main 查找
+ * @returns {{ sheetKey: string, sheet: object, icon: object }|null}
+ */
+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 };
+}
+
+/**
+ * 获取指定雪碧图完整 URL
+ * @param {string} sheetKey
+ * @returns {string}
+ */
+export function getSpriteSheetUrl(sheetKey = DEFAULT_SHEET_KEY) {
+  const sheet = SPRITE_SHEETS[sheetKey];
+  if (!sheet) {
+    return "";
+  }
+  return `${getImgHostBase()}${sheet.path}`;
+}
+
+/**
+ * 生成雪碧图 image 裁切样式(小程序 absolute 定位)
+ * @param {string} name
+ * @param {number|string} size 显示宽度 rpx
+ * @param {string} sheetKey 可选雪碧图 key
+ * @returns {{ wrap: string, inner: string, image: string }}
+ */
+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 = 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;"
+  };
+}
+
+/**
+ * 生成雪碧图行内样式(view + background)
+ * @param {string} name
+ * @param {number|string} size
+ * @param {string} sheetKey
+ * @returns {string}
+ */
+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 = 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(";");
+}
+
+/** @deprecated 兼容旧 API */
+export function getMainIndexSpriteUrl() {
+  return getSpriteSheetUrl(DEFAULT_SHEET_KEY);
+}
+
+/** @deprecated 兼容旧 API */
+export function mainIndexSpriteImageStyle(name, size = 48) {
+  return spriteImageStyle(name, size, DEFAULT_SHEET_KEY);
+}
+
+/** @deprecated 兼容旧 API */
+export function mainIndexSpriteStyle(name, size = 48) {
+  return spriteStyle(name, size, DEFAULT_SHEET_KEY);
+}