skillZs
LIVE SKILL TAGS
>>> LIVE SKILLS INDEX <<<
* OPEN SOURCE *
NO LOGIN, NO TRACKING
REAL INSTALL DATA
← back to all skills
mercury-api.wepieoa.com34 installs

custom-components-skill

指导在活动 H5 中生成与维护「活动积木组件」。根据 page-center 下发的 uiMeta 渲染可配置玩法块(礼盒、盲盒、榜单规则、榜单礼物、单榜 Top3 等)。代码规范与 ui2 组件一致,遵循 ui2-upgrade-guide。适用场景:新建积木组件、从已有积木复制改版、按沉淀规范补齐 types/config/composables。

How do I install this agent skill?

npx skills add https://mercury-api.wepieoa.com --skill fe-custom-components-skill
view source ↗

Is this agent skill safe to install?

No partner audit is available yet. Read the source before installing.

What does this agent skill do?

活动积木组件生成规范

代码规范对齐:积木组件的代码规范与 ui2 组件一致,生成或修改积木时须同时遵循 ui2-upgrade-guide 中的核心原则、Vue/TS 语法、样式与 pageCenter 数据结构约定。下文在 ui2 规范基础上补充积木特有的目录、props、use-meta 与页面使用方式。

何时使用本 Skill

  • 用户要求「生成/新增一个积木组件」「做一个类似礼盒/盲盒/榜单规则的玩法块」
  • 需要在新活动中复用或改版 apps/short/20260401-i/recharge/components 下已有积木(礼盒、盲盒、榜单规则、榜单礼物、single-top3 等)
  • 需要统一积木的目录结构、props、样式与多区服文案接入方式

积木组件标准结构

活动内积木放在 活动目录 components/ 下,每个积木一个子目录,命名 kebab-case(如 gift-boxrank-points)。与 ui2 组件结构对齐:composables、types、子组件、utils 分目录放置。

components/
└── <积木名>/                    # 如 gift-box、blind-box、rank-points、rank-rewards
    ├── components/              # 见下文「子组件目录(强制约定)」
    │   └── *.vue                # 从主文件拆出的布局块、卡片等,与主文件同级会违反规范
    ├── composables/
    │   └── use-meta.ts          # 从 uiMeta + context 派生展示与样式
    ├── config/
    │   ├── index.ts             # 可选,工具函数(如 crownMap、isA 等常量)
    │   ├── default-styles.ts    # 必需,主题样式配置(themeConfig 对象)
    │   ├── default-ui-meta.ts   # 必需,组件默认元数据(不含活动资源)
    │   └── ui-meta.ts           # 必需,页面配置示例(供 page-center 使用)
    ├── types/
    │   └── index.ts             # UiMeta、Props、Emits 等 TypeScript 类型(对外类型统一放此)
    ├── styles/
    │   └── index.scss           # 必需,固定样式文件(布局、尺寸等)
    └── custom-<积木名>.vue      # 主入口组件,对外暴露
  • 业务特有逻辑:可增加 composable(如礼盒的 use-guarantee.ts)或子组件目录。

子组件目录 components/(积木拆分强制约定)

为控制单文件行数(业务约定 ≤300 行 / 模板可读性),从主入口 custom-<积木名>.vue 拆出的 Vue 子块(多布局、重复 UI 卡片、颁奖台与列表分区等)必须放在本积木目录下的 components/ 内,禁止custom-*.vue 平级散落。

要求说明
路径apps/.../components/<积木名>/components/<子组件>.vue
命名文件名 kebab-case,建议带积木前缀避免与页面其他组件混淆,如 lucky-gift-layout-arrow.vuerank-single-top3-podium.vue
引用主入口使用相对路径 ./components/xxx.vue;子组件内引用积木根下 types / config / composables../types../config
样式子组件 一般不单独建全局样式文件,类名仍挂在积木根 BEM(如 .lucky-gift-wrapper__*),由主入口或积木 styles/index.scss 统一维护
参考实现custom-lucky-gift/components/lucky-gift-gift-cardlucky-gift-layout-* 等)、custom-rank-single-top3/components/rank-single-top3-podiumrank-single-top3-rest-list
  • 主组件命名:入口 Vue 文件必须为 custom-<积木名>.vue,与 config/default-ui-meta.ts 中的 type: 'custom-xxx' 一致。
  • config/ 三文件规范:必须包含 default-styles.ts(主题样式)、default-ui-meta.ts(组件默认配置)、ui-meta.ts(页面配置示例),详见下文「config/ 配置文件规范」。

文案与角标(强制)

  • 对用户可见的文案(标题、按钮、角标文字、连接符占位、玩法说明等)须来自 page-center 下发的 uiMeta(如 props.text.*、礼物上的 tag / attachedTag 等),并在 default-ui-meta.ts(及 ui-meta.ts 示例)里保留可编辑默认值,禁止只在源码里写死一处、搭建侧无法覆盖。
  • custom-*.vue 模板中禁止硬编码具体语言文案(例如写死 New附赠主页特效|/+/OR 等展示串);应绑定配置字段或 textConfig / use-meta 派生值。注释、无语义的结构符号若与配置重复,以配置为准。
  • 礼物角标(示例:小礼物爆大礼物):小礼物 / 大礼物主卡角标文案用 tag(空字符串表示不展示),可选配图 images.newTag主页特效预览卡角标文案用 attachedTag,可选配图 images.attachedTag。旧字段 showNewtagType 仅作兼容,新配置以 tag / attachedTag 为准;仅有 tagType 而无文案时可用 props.giftCornerTagFallback 由 page-center 配置兜底文案。

命名规范(与 ui2 一致)

类别规范示例
组件名PascalCaseCustomGiftBox
文件名kebab-casecustom-gift-box.vue
PropscamelCaseuiMetacontext
Eventskebab-case@update-value
样式scoped + 组件前缀/ BEM.rank-rule-wrapper__title-row

config/ 配置文件规范

积木组件的 config/ 目录必须包含以下三个文件(三文件规范):

1. config/default-styles.ts — 主题样式配置

职责:定义多套主题色系配置,导出 themeConfig 对象。

规范

  • 导出类型接口(如 ThemeStyleConfig),定义主题对象的结构
  • 导出 themeConfig: Record<string, ThemeStyleConfig>,key 为主题名
  • 每个主题包含完整的样式变量(颜色、背景、边框等)
  • 纯样式配置,不包含业务逻辑或图片 URL

主题命名规范(强制)

  1. 按颜色命名(推荐主题名):

    • blue - 蓝色主题(深蓝/紫蓝色调)
    • red - 红色主题(红色/粉色调)
    • green - 绿色主题(绿色调)
    • purple - 紫色主题(紫色调)
    • gold - 金色主题(金黄色调)
    • brown - 棕色主题(棕色调)
  2. 禁止按区服命名

    • ❌ 不使用 indiabrazilusaindonesia 等区服名
    • 原因:不同区服可共用同一颜色主题,减少重复配置
  3. 配置项用途说明(以榜单组件为例):

    • listContainerBg - 整体弹框背景(容器背景),通常为 transparent
    • listItemBg - Top4-n 榜单项背景,多为渐变色linear-gradient(...)
    • mineBg - 我的排名背景(非固定时),半透明色:rgba(...)
    • fixedBg - 当前用户吸底背景(固定时),多为渐变色linear-gradient(...)
    • textColor* - 文字颜色,固定色,各主题可复用
    • borderColor* - 边框颜色,固定色,各主题可复用

示例

export interface ThemeStyleConfig {
  /** 整体弹框背景 */
  listContainerBg: string
  /** Top4-n 榜单项背景(多为渐变色) */
  listItemBg: string
  /** 我的排名背景 */
  mineBg: string
  /** 吸底背景(多为渐变色) */
  fixedBg: string
  /** 文字颜色(固定色,可各主题复用) */
  textColorFirst: string
  textColorSecond: string
  textColorThird: string
  textColorOther: string
  textColorScore: string
  textColorStar: string
  /** 边框颜色(固定色,可各主题复用) */
  borderColorTop1: string
  borderColorTop2: string
  borderColorTop3: string
  borderColorOther: string
  borderColorMine: string
}

export const themeConfig: Record<string, ThemeStyleConfig> = {
  /** 蓝色主题(深蓝紫色调) */
  blue: {
    listContainerBg: 'transparent',
    // Top4-n 榜单项背景使用渐变色
    listItemBg: 'linear-gradient(90deg, rgba(78, 91, 186, 0.6) 0%, rgba(98, 111, 206, 0.6) 100%)',
    mineBg: 'rgba(98, 111, 206, 0.8)',
    // 吸底背景使用渐变色
    fixedBg: 'linear-gradient(90deg, rgba(78, 91, 186, 0.8) 0%, rgba(98, 111, 206, 0.8) 100%)',
    // 文字与边框颜色(固定色)
    textColorFirst: '#8B4513',
    textColorSecond: '#1E5A8E',
    textColorThird: '#8B3A1E',
    textColorOther: '#FFFFFF',
    textColorScore: '#FFFFFF',
    textColorStar: '#FFFFFF',
    borderColorTop1: 'rgba(255, 215, 0, 0.6)',
    borderColorTop2: 'rgba(192, 192, 192, 0.6)',
    borderColorTop3: 'rgba(205, 127, 50, 0.6)',
    borderColorOther: 'rgba(255, 255, 255, 0.2)',
    borderColorMine: 'rgba(255, 255, 255, 0.4)'
  },

  /** 红色主题 */
  red: {
    listContainerBg: 'transparent',
    listItemBg: 'linear-gradient(90deg, rgba(180, 60, 60, 0.6) 0%, rgba(200, 80, 80, 0.6) 100%)',
    mineBg: 'rgba(200, 80, 80, 0.8)',
    fixedBg: 'linear-gradient(90deg, rgba(180, 60, 60, 0.8) 0%, rgba(200, 80, 80, 0.8) 100%)',
    // 文字与边框颜色(固定色,与 blue 相同)
    textColorFirst: '#8B4513',
    // ... 其他颜色
  },

  /** 绿色主题 */
  green: { /* ... */ },

  /** 紫色主题 */
  purple: { /* ... */ }
}

要点

  • 主题命名规范(强制):
    • 按颜色命名blueredgreenpurplegoldbrown
    • 禁止按区服命名:不使用 indiabrazilusa 等区服名
    • 原因:不同区服可共用同一颜色主题,减少重复配置
  • 所有主题的 key 必须保持一致(通过类型接口约束)
  • 使用标准 CSS 属性名 + rem 单位,禁止简写(见后文样式规范)
  • 参考实现:components/custom-rank-single-top3/config/default-styles.tscomponents/custom-lucky-gift/config/default-styles.ts

2. config/default-ui-meta.ts — 组件默认元数据

职责:定义组件级默认配置,不包含具体活动资源(图片 URL、act_id 等)。

规范

  • 导出 defaultUiMeta 对象,类型为该组件的 UiMeta 类型
  • ./default-styles 引入 themeConfig,theme 字段引用默认主题
  • 图片资源使用空字符串占位
  • 所有必填字段都有默认值
  • 组件内部使用此配置作为 fallback(props 默认值)

示例

import type { CustomRankSingleTop3UiMeta } from '../types'
import { themeConfig } from './default-styles'

export const defaultUiMeta: CustomRankSingleTop3UiMeta = {
  key: 'custom-rank-single-top3',
  type: 'custom-rank-single-top3',
  props: {
    act_id: 0,
    rankKey: 'total',
    images: {
      empty: '',
      crown1: '',
      // ... 空字符串占位
    },
    theme: {
      themeType: 'blue',
      ...themeConfig.blue  // 引用蓝色主题作为默认
    },
    rankConfig: {
      rankLength: 10,
      // ... 默认配置
    }
  }
}

要点

  • 不包含活动特定资源(图片 URL 为空串、act_id 为 0)
  • theme 引用 default-styles.ts 中的主题配置
  • 在组件中作为 props 默认值使用:uiMeta: () => defaultUiMeta
  • 参考实现:components/custom-rank-single-top3/config/default-ui-meta.ts

3. config/ui-meta.ts — 页面配置示例

职责:供 page-center 使用的实际配置示例,包含完整的活动资源。

规范

  • 导出 uiMeta 对象(可导出多个示例配置)
  • 包含完整的活动资源(图片 URL、act_id 等)
  • theme 引用特定主题(如 'india')
  • 可导出多个配置示例(如 luckyGiftArrowExample, luckyGiftPlusExample)
  • 必须包含使用说明注释

示例

import type { CustomRankSingleTop3UiMeta } from '../types'
import { themeConfig } from './default-styles'

/**
 * 印巴活动 20260401-i 榜单配置示例(使用 blue 蓝色主题)
 */
export const uiMeta: CustomRankSingleTop3UiMeta = {
  key: 'custom-rank-single-top3',
  type: 'custom-rank-single-top3',
  props: {
    act_id: 12345,
    rankKey: 'total',
    images: {
      empty: 'https://res.weplayapp.com/.../default-avatar.png',
      crown1: 'https://res.weplayapp.com/.../crown-1.png',
      // ... 实际图片 URL
    },
    theme: {
      themeType: 'blue',
      ...themeConfig.blue  // 引用蓝色主题
    },
    rankConfig: {
      rankLength: 10,
      // ... 页面配置
    }
  }
}

/**
 * 使用说明:
 *
 * 在 page-center 配置中使用:
 * ```ts
 * compUiMeta: {
 *   rankSingleTop3: uiMeta
 * }
 * ```
 *
 * 或在页面中直接引入:
 * ```ts
 * import { uiMeta as rankMeta } from './components/custom-rank-single-top3/config/ui-meta'
 * ```
 */

要点

  • 完整的活动配置,可直接用于 page-center 或页面引入
  • theme 引用具体主题(按颜色:blueredgreenpurple 等)
  • 必须包含使用说明注释(在 page-center 中如何配置、在页面中如何引入)
  • 可导出多个配置示例(针对不同场景,如 arrow/plus/or 布局)
  • 参考实现:components/custom-rank-single-top3/config/ui-meta.tscomponents/custom-lucky-gift/config/ui-meta.ts

4. config/index.ts — 工具函数(可选)

职责:导出组件内部使用的工具函数和常量配置。

规范

  • 可选文件,仅在有工具函数或常量时创建
  • 导出工具函数(如 formatItem、getDefaultValue 等)
  • 导出常量配置(如 crownMap、isA 等)

示例

export const crownMap = {
  1: 'default-crown-1.png',
  2: 'default-crown-2.png',
  3: 'default-crown-3.png'
}

export const isA = region === 'A'

export const formatItem = (item: any) => {
  // ... 格式化逻辑
}

要点

  • 仅在需要时创建,不强制要求
  • 不包含样式配置(样式配置在 default-styles.ts)
  • 不包含 UiMeta 配置(UiMeta 在 default-ui-meta.ts 和 ui-meta.ts)

配置文件使用规范

在组件中引入

<script setup lang="ts">
// 从 config/ 引入默认配置
import { defaultUiMeta } from './config/default-ui-meta'
import { crownMap, isA } from './config'  // 工具函数和常量
import type { CustomRankSingleTop3UiMeta } from './types'

// 使用 defaultUiMeta 作为 props 默认值
const props = withDefaults(defineProps<Props>(), {
  uiMeta: () => defaultUiMeta,
  context: () => ({})
})

// 合并主题配置
const themeConfig = computed(() => ({
  ...defaultUiMeta.props.theme,
  ...props.uiMeta?.props?.theme
}))
</script>

在页面中引入

<script setup lang="ts">
// 从 config/ 引入页面配置
import CustomRankSingleTop3 from './components/custom-rank-single-top3/custom-rank-single-top3.vue'
import { uiMeta as rankMeta } from './components/custom-rank-single-top3/config/ui-meta'

// 使用配置
</script>

<template>
  <custom-rank-single-top3 :ui-meta="rankMeta" :context="context" />
</template>

在 page-center 中使用

// page-center 配置文件中
import { uiMeta as rankMeta } from './components/custom-rank-single-top3/config/ui-meta'

export const pageConfig = {
  compUiMeta: {
    rankSingleTop3: rankMeta
  }
}

必须遵守的约定

1. Props、Emits 与类型(对齐 ui2)

  • 主组件仅接收 uiMetacontext 两个 props,使用 声明式类型 + withDefaults,类型定义在 types/index.ts,不使用 Record<string, unknown>any
// types/index.ts 中定义
export interface XxxUiMeta { type: string; props: { ... } }
export interface XxxProps {
  /** 通过 page-center 配置的组件元信息 */
  uiMeta?: XxxUiMeta
  /** 上下文(区服等) */
  context?: Record<string, unknown>
}

// custom-xxx.vue 中
const props = withDefaults(defineProps<XxxProps>(), {
  uiMeta: () => defaultUiMeta,
  context: () => ({})
})
  • 若有事件,使用 defineEmits + 调用签名形式(Vue 2.7 不支持具名元组):interface Emits { (e: 'click', id: string): void }const emit = defineEmits<Emits>()
  • 接口字段使用 /** */ 注释;服务端返回类型保持 snake_case,前端 pageCenter 静态配置用 camelCase。

2. this → ins、全局 API 与生命周期(对齐 ui2)

  • 所有 this.$xxx 统一用 useInstance() 得到 ins,例如:ins.$base.amBasic()ins.$propsConfig(id)ins.$toast()
  • ref、computed、watch、useInstance、useCompUiMeta、useLifeCycle 等已 auto-import,不要在积木内再写 import { ref } from 'vue'
  • 生命周期用 useLifeCycle()const { onAfterInit, onPageShow, onPageHide } = useLifeCycle(),弹窗等 DOM 操作放在 onAfterInit 之后。

3. use-meta composable

  • 入参:props: { uiMeta: UiMeta; context?: Record<string, unknown> },避免 any
  • props.uiMeta?.props 读取业务配置;样式优先走 主题(如 themeType + themeConfig),再回退到 props.styles
  • 通用字段outerStyletitleStylerewardBorderStylerewardBgStylestretchConfigimagestextConfig;若积木带「去送礼」按钮,需增加 xxxBtnStyle(如 goGiftBtnStylesendGiftBtnStyle),来自 themeConfig[themeType].goGiftBtn / sendGiftBtnstyles.sendGiftBtn
  • 文本统一用 genComponentCurRegionText(uiMeta.type, context) 与 default 合并,再与 props.text 合并,禁止硬编码文案。
  • 读取配置用 可选链与空值合并props.uiMeta?.props?.images ?? {},不使用 lodash get()

3.1 多主题配置规范(themeConfig)

强制标准:所有积木组件的主题配置必须统一使用 themeConfig 对象格式

每个积木组件的 config/default-styles.ts 文件必须按以下规范导出主题配置:

/**
 * 组件名称主题色系配置
 * 支持多主题:blue(默认)/ red / purple / gold / brown
 * 每个主题必含核心字段:outer、title + 组件特定字段
 */
export const themeConfig = {
  blue: {
    outer: {
      background: '#061B29'  // 外层容器背景色
    },
    title: {
      color: 'linear-gradient(0deg, #D3DBFF, #ffffff)',  // 标题渐变色
      fontSize: '0.4rem',    // 可选:字号
      fontWeight: '700'      // 可选:字重
    },
    // 组件特定字段:根据组件实际需要配置
    rewardBorder: {
      background: 'linear-gradient(to bottom, #93EFFF, #57B9F6)'
    },
    rewardBg: {
      background: 'linear-gradient(to bottom, #0D399F, #2176DFCC)'
    },
    // 其他组件特定样式...
  },
  red: {
    outer: {
      background: '#3a1322'
    },
    title: {
      color: 'linear-gradient(0deg, #ffd3e5, #ffffff)',
      fontSize: '0.4rem',
      fontWeight: '700'
    },
    rewardBorder: {
      background: 'linear-gradient(to bottom, #ffb5b5, #d05959)'
    },
    rewardBg: {
      background: 'linear-gradient(to bottom, #5d1f1f 0%, #7c2b2c 42%, #983738 100%)'
    }
  },
  purple: {
    outer: {
      background: '#291048'
    },
    title: {
      color: 'linear-gradient(0deg, #E5D3FF, #ffffff)',
      fontSize: '0.4rem',
      fontWeight: '700'
    },
    rewardBorder: {
      background: 'linear-gradient(to bottom, #EBDBFF, #C991FD)'
    },
    rewardBg: {
      background: 'linear-gradient(to bottom, #311EA7, #8B6BFFCC)'
    }
  }
  // gold、brown 等主题按需添加
}

主题命名约定

主题名称说明主色调
blue蓝色主题(默认)#061B29(深蓝)
red红色主题#3a1322(深红)
purple紫色主题#291048(深紫)
gold金色主题#C4956A(金棕色)
brown棕色主题#B5997A(浅棕色)

核心字段约定

每个主题必须包含以下核心字段(使用标准 CSS 属性):

  • outer:外层容器样式,至少包含 background
  • title:标题样式,至少包含 color(渐变色),可选 fontSizefontWeight

组件特定字段

根据组件类型添加必需的样式字段(参考同类组件):

  • 礼盒/盲盒/榜单类rewardBorder(奖励边框)、rewardBg(奖励背景)、按钮样式
  • 红包雨类stageIconOuter(阶段icon外圈)、stageIconInner(阶段icon内圈)、rewardCard(红包卡片)、rewardCardInner(红包卡片内层)
  • 小礼物爆大礼物类giftCardBorder(礼物卡片边框)、giftCardBg(礼物卡片背景)、sendGiftBtn(去送礼按钮)

在 use-meta 中使用主题配置

import { themeConfig } from '../config/default-styles'

export const useMeta = ({ uiMeta, context }: UseMetaParams) => {
  const props = uiMeta.props

  // 获取当前主题配置,默认为 blue
  const currentTheme = themeConfig[props.themeType] || themeConfig.blue

  // 样式:主题配置 + props.styles 合并覆盖
  const outerStyle = computed(() => ({
    ...currentTheme.outer,
    ...props.styles?.outer
  }))

  const titleStyle = computed(() => ({
    ...currentTheme.title,
    ...props.styles?.title
  }))

  // 其他样式字段同理...
}

禁止的做法

  • ❌ 导出独立的主题变量(如 export const defaultTheme = {...}, export const redTheme = {...}
  • ❌ 使用 getThemeStyles 函数进行主题选择(直接用对象索引:themeConfig[themeType]
  • ❌ 主题名称不一致(如混用 defaultblue 作为默认主题)
  • ❌ 核心字段缺失或命名不一致

参考实现:礼盒 components/box/gift-box/config/default-styles.ts、榜单奖励 components/rank-rewards/config/default-styles.ts

4. 布局与样式规范(与 ui2 一致)

4.1 样式标准化:强烈禁止简写

积木组件内所有样式(含 default-styles.tsthemeConfig、use-meta 导出的 xxxStyle禁止使用简写属性。必须使用标准 CSS 属性名且数值显式加 rem 单位。完整映射与规则见 ui2-upgrade-guide 第四章节「样式标准化」(约 4.1–4.5)。

禁止使用的简写(仅列举,全部禁止):

禁止简写必须使用标准属性说明
pt / pb / pl / prtop / bottom / left / right定位,单位 rem
pc展开为定位组合pc: 'x'{ left: '50%', transform: 'translateX(-50%)' }pc: 'y' / 'xy' 同理
pzzIndex
w / hwidth / height'1.9rem'
whwidth + height拆成两个属性
ffontSize + color + fontWeight合写必须拆开,注意是 color 不是 fontColor
fs / fc / fwfontSize / color / fontWeight
m / mt / mb / ml / mrmargin / marginTop
pd / pdt / pdb / pdl / pdrpadding / paddingTop
bg-i / bg-c / bg-s / bg-pbackgroundImage / backgroundColor / backgroundSize / backgroundPosition若通过 :style 绑定须用标准名
b / bw / bc / bdrborder / borderWidth / borderColor / borderRadius

约定

  • 所有样式对象通过 :style 绑定时,一律使用上表中的标准属性名 + rem 单位(如 top: '0.28rem'fontSize: '0.24rem'color: '#ffffff')。
  • :style 与 v-bind:纯 CSS 走 :style;组件/元素自有 prop(如 bg-i 在模板上直接写)可保留,但 config/default-styles 等配置里的样式对象禁止出现简写
  • 合写必须拆分:例如 f: '.22 #FFFFFF 600' 改为 { fontSize: '0.22rem', color: '#FFFFFF', fontWeight: '600' }

4.2 样式文件组织:固定样式与动态样式分离

为提高可维护性,积木组件的样式采用 SCSS 文件 + :style 动态绑定 相结合的方式:

  • 固定样式(布局、尺寸、间距、圆角等不随主题变化的样式)→ 独立的 .scss 文件
  • 动态样式(主题色、渐变、根据配置变化的样式)→ :style 绑定

目录结构

components/<积木名>/
├── styles/
│   └── index.scss              # 固定样式文件
├── custom-<积木名>.vue         # 主组件
└── ...

在 Vue 文件中引入(强制)

积木组件的 styles/index.scss 必须在 <script> 内通过 import 引入,否则在插槽、ui2-stretch 等场景下样式可能不生效。

<script setup lang="ts">
// 必须:在 script 内引入样式,确保固定样式生效
import './styles/index.scss'
// ... 其他导入(useMeta、defaultUiMeta、types 等)
</script>

<template>...</template>

<style scoped lang="scss">
/* 仅保留无法抽离到 styles/index.scss 的少量样式;主样式勿用 <style src="..."> 替代 script import */
</style>

禁止仅使用 <style src="./styles/index.scss" scoped></style> 而不在 script 中 import,否则可能导致内容区、标题行等内边距/布局不生效。

SCSS 文件编写规范

  1. 使用 BEM 命名.组件名__元素名--修饰符(如 .gift-box-wrapper__title-row
  2. 嵌套层级 ≤3 层
  3. 使用 rem 单位:所有尺寸值显式加 rem
  4. 不使用 !important
  5. 固定样式包括
    • 宽高、内外边距、圆角
    • 布局属性(display、flex、position 等)
    • 文字大小、粗细(不含颜色,颜色可能随主题变)
    • 固定的颜色(如 #ffffff 白色文字)

动态样式通过 :style 绑定

  • 主题色、渐变(来自 themeConfig
  • 背景色、边框色(根据 themeType 动态变化)
  • 需要通过 props 传入的样式

示例

<template>
  <!-- class 控制固定样式,:style 绑定动态样式 -->
  <a-section class="gift-box-wrapper" :style="outerStyle">
    <a-section class="gift-box-wrapper__title-row">
      <ui-gradient-text
        class="gift-box-wrapper__title"
        f="0.4 transparent 700"
        :gradient-color="titleStyle.color"
        :content="textConfig.title"
      />
    </a-section>
    <a-section class="gift-box-wrapper__reward-border" :style="rewardBorderStyle">
      <a-section class="gift-box-wrapper__reward-bg" :style="rewardBgStyle">
        <ui-prop-wrapper wh="100% 100%" :id="rewardId" />
      </a-section>
    </a-section>
  </a-section>
</template>

<script setup lang="ts">
import './styles/index.scss'
// ...
</script>
// styles/index.scss
.gift-box-wrapper {
  width: 100%;

  &__title-row {
    display: flex;
    justify-content: center;
    align-items: center;
    padding-top: 0.28rem;
  }

  &__title {
    margin: 0 0.16rem;
  }

  &__reward-border {
    width: 1.6rem;
    height: 1.6rem;
    padding: 0.02rem;
    border-radius: 0.16rem;
    box-sizing: border-box;
    // 背景、边框色通过 :style="rewardBorderStyle" 绑定
  }

  &__reward-bg {
    width: 100%;
    height: 100%;
    border-radius: 0.16rem;
    display: flex;
    align-items: center;
    justify-content: center;
    // 背景色通过 :style="rewardBgStyle" 绑定
  }
}

4.3 布局与 pageCenter 约定

  • 最外层<a-section> + <ui2-stretch>(九宫格拉伸),stretch 使用 stretchConfig.stretchBgminHeightcentralAreaHeight
  • 玩法标题ui-gradient-text + 左右装饰图(images.leftIcon / rightIcon);上下与水平间距的固定 rem 见下节 4.3.1(与礼盒、盲盒、榜单礼物沉淀一致)。
  • 玩法说明:单一块用 v-html="textConfig.explain";外边距见 4.3.1(礼盒/盲盒与榜单礼物略有差异),字号 0.24rem、颜色 #ffffff。
  • 奖励区:外框用 rewardBorderStyle,内层用 rewardBgStyle;道具用 <ui-prop-wrapper :id="rewardId" />;名称/价值等从 ins.$propsConfig(id) 或 props 取,不写死常量。

4.3.1 玩法标题上下间距固定样式(礼盒 / 盲盒 / 榜单礼物沉淀)

以下数值来自 components/box/gift-boxcomponents/box/blind-boxcomponents/rank-rewardsstyles/index.scss新建或改版带玩法标题的积木应优先对齐,避免同一活动内多块间距不一致。

强制标准:玩法标题部分固定样式

带玩法标题的积木必须styles/index.scss 中为标题行与标题文案使用下列固定样式,类名与属性不可省略或改写(可随积木前缀命名,如 lucky-gift-wrapper__title-row):

// 标题行:居中、仅上内边距
&__title-row {
  display: flex;
  justify-content: center;
  align-items: center;
  padding-top: 0.28rem;
}

// 标题文案(ui-gradient-text 等):与左右装饰水平间距
&__title {
  margin: 0 0.16rem;
}

模板约定:标题行容器使用 xxx-wrapper__title-row,标题文案节点(如 <ui-gradient-text>)使用 xxx-wrapper__title;若有左右装饰图,使用 xxx-wrapper__title-icon(宽高 1.08rem × 0.34rem),与 __title 同排即可。

层级类名约定固定样式(rem)说明
玩法块内容区xxx-wrapper__contentpadding-top: 0.4rempadding-bottom: 0.4rem盲盒/礼盒写法;榜单礼物等价于 padding: 0.4rem 0
标题行xxx-wrapper__title-rowpadding-top: 0.28rem仅上内边距,不设 padding-bottom;标题与下方说明/首块内容的垂直间距由下一区块的 margin 承担
左右装饰图xxx-wrapper__title-iconwidth: 1.08remheight: 0.34rem与三处参考实现一致
标题文案与装饰图水平间距标题文案节点使用 xxx-wrapper__title强制margin: 0 0.16rem见上文强制标准代码块,与 __title-row 配套使用
标题行下方 → 玩法说明xxx-wrapper__explain礼盒 / 盲盒margin: 0.4rem(四边一致)说明块顶边距即标题行下方的视觉间距 0.4rem
标题行下方 → 玩法说明xxx-wrapper__explain榜单礼物margin: 0.4rem 0.4rem 0.3rem 0.4rem上/左/右仍为 0.4rem,下边距 0.3rem(略紧于礼盒);新积木若无设计特殊要求,推荐与礼盒/盲盒统一为 margin: 0.4rem

垂直关系小结(从 ui2-stretch 内容顶到标题文字):

  1. 先经过 __contentpadding-top: 0.4rem
  2. 再经过 __title-rowpadding-top: 0.28rem
  3. 标题行下方到说明文案:由 __explainmargin-top(0.4rem) 形成统一留白。

样式与 pageCenter 对齐 ui2

  • images:使用复数 images,为完整 URL 或资源 key,不写死单数 image
  • text:统一收口到 props.text(title、explain、tip 等)。
  • styles:仅使用标准 CSS 属性 + rem,禁止简写(见 4.1);xxxStyle 通过 :style 绑定。

4.3.2 礼物/奖励卡片布局规范(通用沉淀)

根据礼盒、盲盒、榜单奖励、小礼物爆大礼物等积木的实践,礼物/奖励卡片布局分为两类:

A. 奖励卡片(榜单奖励、礼盒周边奖励等)

用于展示多个小奖励,尺寸 1.6rem × 1.6rem(正方形):

// 奖励外框
&__reward-box-outer {
  position: relative;
  width: 1.6rem;
  height: 1.6rem;
  padding: 0.02rem;
  border-radius: 0.16rem;  // 或 50% 圆形
  box-sizing: border-box;
}

// 奖励内层
&__reward-box-inner {
  width: 100%;
  height: 100%;
  border-radius: 0.16rem;  // 或 50% 圆形
  display: flex;
  align-items: center;
  justify-content: center;
}
  • 外框 padding 0.02rem,borderRadius 0.16rem(圆形用 50%
  • 背景/边框通过 :style="rewardBorderStyle":style="rewardBgStyle" 绑定
  • 名称字号 0.24rem、颜色 #ffffff、font-weight 400
  • 价值字号 0.22rem、颜色 #fffa00、font-weight 700,金币图标 0.24rem × 0.24rem

B. 礼物卡片(小礼物爆大礼物、单独礼物展示等)

用于展示较大的礼物,卡片宽度 2.6-2.8rem(根据一行放几个调整):

// 礼物卡片
&__gift-card {
  position: relative;
  flex-shrink: 0;
  box-sizing: border-box;
  padding: 0.04rem;  // 边框粗细
}

&__gift-card-inner {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0.24rem 0.16rem;  // 内边距
  box-sizing: border-box;
}

// 礼物道具区域:固定正方形
&__gift-prop {
  width: 1.6rem;
  height: 1.6rem;
  flex-shrink: 0;
  margin-bottom: 0.16rem;
}
  • 卡片 padding 0.04rem(边框),borderRadius 0.16rem
  • 内容区 padding 0.24rem 0.16rem(上下 左右)
  • 道具区域固定 1.6rem × 1.6rem,margin-bottom 0.16rem
  • 名称字号 0.26rem、颜色 #ffffff、font-weight 400,margin-bottom 0.08rem
  • 价值字号 0.32rem、颜色 #FFFA00、font-weight 700,金币图标 0.28rem × 0.28rem
  • 背景/边框通过 :style="giftCardBorderStyle":style="giftCardBgStyle" 绑定

角标通用规范(适用于两类卡片):

&__gift-tag {  // 或 __reward-tag
  position: absolute;
  top: -0.08rem;
  left: -0.08rem;
  z-index: 2;
}

&__gift-tag-img {
  width: 0.8rem;
  height: 0.4rem;
}

&__gift-tag-text {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.22rem;
  color: #57280f;
  font-weight: 700;
  padding: 0.04rem 0.16rem;
}
  • 角标位置:左上角外溢(top/left -0.08rem
  • 角标图片:宽 0.8rem、高 0.4rem
  • 角标文字(无图片时):字号 0.22rem、颜色 #57280f、padding 0.04rem 0.16rem、圆角 0.08rem、渐变背景 linear-gradient(to bottom, #f5be28, #f4ea7b)

强制:礼物/奖励角标 tag 必须有背景样式

  • 角标(如 New、热等)禁止仅设字色无背景,否则在深色卡片上可读性差。
  • 两种写法二选一
    1. 组件 SCSS 写死:在 styles/index.scss 中为 __reward-tag / __gift-tag 设置默认背景(如 background: linear-gradient(to bottom, #f5be28, #f4ea7b))、paddingborder-radiusposition: absolute; top: 0; left: 0; z-index: 1,与礼盒/盲盒一致;
    2. 配置传入:通过 uiMeta.props.styles.rewardTag(或等价字段)传入完整样式对象,用 :style="rewardTagStyle" 绑定。
  • 若组件内已写死背景,config 中的 rewardTag 仍可叠加覆盖(如主题色差异)。

强制:礼物/奖励边框仅包裹切图,名称与金币在框下展示

  • 边框容器(如 __reward-item仅包裹:道具切图(ui-prop-wrapper)+ 角标 tag;不包裹名称、金币数。
  • 名称与金币数放在边框下方,使用独立信息块(如 __reward-info),通过 margin-top 或定位在框下展示。
  • DOM 结构约定:定位外层(__reward-wrap,位置由 layout 传入)→ 边框盒(__reward-item,内层 __reward-inner + 切图 + tag)→ 信息块(__reward-info,名称 + 价值行)。
  • 参考实现:礼盒 components/box/gift-boxreward-wrap / reward-item / reward-info 分离)。

4.3.3 红包雨玩法布局规范(节点列表 + 镜像区域)

红包雨玩法采用横向两栏布局:左侧镜像区域(可选)+ 右侧节点列表。节点纵向排列,每个节点包含左侧图标/数量、连接线、右侧红包雨奖励卡片列表(多个奖励用 OR 连接)。

主布局结构

// 主内容区域:左侧镜像区 + 右侧节点列表
&__main-section {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  margin: 0.4rem 0.4rem;
  gap: 0.32rem;
}

// 左侧镜像区域(可选)
&__mirror-section {
  flex-shrink: 0;
  width: 3rem;  // 固定宽度
}

// 右侧节点列表
&__nodes-section {
  flex: 1;
  display: flex;
  flex-direction: column;
}

节点结构(纵向排列)

// 单个节点项
&__node-item {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  position: relative;
  margin-bottom: 0.32rem;

  &:last-child {
    margin-bottom: 0;
  }
}

// 节点左侧:图标 + 数量标签
&__node-left {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  width: 1.2rem;
  margin-right: 0.24rem;
  position: relative;
}

&__node-icon {
  width: 0.8rem;
  height: 0.8rem;
  // 背景、边框等通过 :style 绑定
}

&__node-quantity {
  margin-top: 0.08rem;
  display: inline-block;
  white-space: nowrap;
  text-align: center;
  // 背景、字号等通过 :style 绑定
}

// 节点连接线(垂直线条)
&__node-connector {
  position: absolute;
  left: 0.6rem;  // 图标中心位置
  top: 0.8rem;   // 从图标底部开始
  bottom: -0.32rem;  // 连接到下一个节点
  width: 0.04rem;
  transform: translateX(-50%);
  z-index: 1;
  // 背景色通过 :style 绑定
}

红包雨奖励卡片列表(横向排列,OR 连接)

// 节点右侧:红包雨奖励卡片列表
&__node-rewards {
  flex: 1;
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.16rem;
}

// 红包雨奖励卡片
&__reward-card {
  position: relative;
  flex-shrink: 0;
  box-sizing: border-box;
  width: 2.4rem;  // 固定宽度
  // 背景、边框等通过 :style 绑定
}

&__reward-card-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  // padding 等通过 :style 绑定
}

// 红包雨图片
&__reward-image {
  width: 1.6rem;
  height: 1.6rem;
  flex-shrink: 0;
  margin-bottom: 0.12rem;
}

// 金币数量文本
&__coins-label {
  text-align: center;
  white-space: nowrap;
  // 字号、颜色通过 :style 绑定
}

// OR连接符
&__or-text {
  flex-shrink: 0;
  padding: 0 0.16rem;
  // 字号、颜色通过 :style 绑定
}

左侧镜像区域(可选展示)

&__mirror-container {
  box-sizing: border-box;
  padding: 0.24rem;
  // 边框等通过 :style 绑定
}

&__mirror-title {
  margin-bottom: 0.16rem;
  text-align: center;
  // 字号、颜色通过 :style 绑定
}

&__mirror-description {
  margin-bottom: 0.24rem;
  text-align: left;
  white-space: pre-line;  // 支持换行符
  // 字号、颜色通过 :style 绑定
}

&__mirror-icons {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.24rem;
}

&__mirror-icon {
  width: 1.2rem;
  height: 1.2rem;
  flex-shrink: 0;
}

配置要点

  • 节点图标(nodes[].icon)可配置为任意图标,不限于特定样式
  • 节点数量(nodes[].quantity)可根据实际需求配置任意数值
  • 每个节点可配置单个或多个红包雨奖励(nodes[].rewards),多个奖励用 OR 连接
  • 左侧镜像区域通过 showMirrorSection 控制是否显示
  • 幸运加倍角标(rewards[].showLuckyBonus)显示在卡片左上角
  • 支持多主题切换(themeType),节点图标、连接线、按钮等颜色随主题变化

参考实现components/custom-red-envelope-rain/(红包雨玩法积木)

4.4 【去送礼】按钮规范(带送礼入口的积木统一遵守)

凡积木需要「去送礼」入口的(如礼盒、盲盒、小礼物爆大礼物等),按钮的布局事件逻辑按以下约定实现,与礼盒/盲盒保持一致。

强制标准:玩法底部【去送礼】按钮样式与结构

带「去送礼」入口的积木必须styles/index.scss 与模板中按下述写法实现,类名与属性不可省略或改写(前缀随积木命名,如 lucky-gift-wrapper__btn-container)。

SCSS 固定样式(styles/index.scss

// 送礼按钮容器
&__btn-container {
  margin: 0.36rem;
}

&__btn {
  width: 3.4rem;
  height: 0.88rem;
  border-radius: 0.44rem;
}

不在 __btn-container / __btn 中写背景、字号、颜色等视觉样式,由 :style="xxxBtnStyle" 动态绑定;水平居中由模板上的 fx-centerfx-c-center 提供。

模板结构(必须一致)

<a-section class="xxx-wrapper__btn-container fx-center">
  <a-section
    class="xxx-wrapper__btn fx-center fx-c-center"
    :style="xxxBtnStyle"
    v-html="textConfig.sendGiftBtnText"
    @a-tap="toSendGift"
  />
</a-section>
  • 结构:按钮区域放在玩法内容与保底说明之后、</ui2-stretch> 之前;外层 __btn-container + 内层 __btn,容器与按钮均带 fx-center,按钮再带 fx-c-center
  • 动态样式:通过 use-meta 导出 xxxBtnStyle(如 goGiftBtnStylesendGiftBtnStyle),来自 themeConfig[themeType].goGiftBtn / sendGiftBtn 或回退到 styles.sendGiftBtn;在 default-styles / themeConfig 中使用标准 CSS 属性 + rem(如 backgroundfontSizecolorfontWeight),禁止简写。
  • 文案:按钮文案来自 textConfig.sendGiftBtnText(或该积木约定的字段如 sendblindBtnText),由 default-ui-meta + genComponentCurRegionText + props.text 合并得到,禁止硬编码「去送礼」等文案。

事件逻辑规范

  • 点击事件:使用 @a-tap="toSendGift"(或语义一致的方法名,如 handleSendGift)。
  • 默认行为:跳转语音房列表,统一使用 ins.$we('jumpDeepLink', 'wespydeeplink://voice_room_list')ins 通过 useInstance() 获取。
  • 可配置扩展:若后续需按活动或 page-center 配置跳转链接,应从 uiMeta.props 或 jsonConfig 读取链接,不在组件内硬编码;未配置时使用上述默认 deeplink。

参考实现:礼盒 components/box/gift-box/goGiftBtnStyletoSendGift)、盲盒 components/box/blind-box/goblindBtnStyletoSendGift)。

5. script setup 编写顺序(与 ui2 一致)

  1. 导入(仅需写非 auto-import 的)
  2. Props(withDefaults(defineProps<Props>(), …))
  3. Emits(defineEmits<Emits>())
  4. Composables(useInstance、useMeta、useLifeCycle 等)
  5. 响应式数据(ref/reactive)
  6. 计算属性(computed)
  7. 监听器(watch)
  8. 方法
  9. 生命周期钩子(onMounted、onAfterInit 等)
  10. defineExpose(如有需要)

6. 在页面中的使用

  • 页面中 const compUiMeta = useCompUiMeta()compUiMeta 的 key 与 page-center 配置的组件 key 一致(如 rankPointsgiftBoxblindBox)。
  • 模板中:<custom-xxx :uiMeta="compUiMeta.xxxKey" />;需在页面或父组件中 import 并注册 该 custom 组件(局部引用即可)。

7. 其他编码与规范

  • 使用 <script setup lang="ts">,禁止 options API 与 mixin;复杂逻辑抽到 composable。
  • 禁止硬编码商品/道具 id、文案、金额;从 jsonConfig 或 uiMeta 读取。
  • 单文件 ≤300 行,SCSS 嵌套 ≤3 层,无 !important;圈复杂度 <10。
  • Vue 2.7 兼容:不使用 defineOptions()defineModel()、Emits 具名元组、Teleport、Suspense 等;Emits 用调用签名形式。

参考实现位置

积木类型路径(相对活动 recharge)
礼盒components/box/gift-box/
盲盒components/box/blind-box/
榜单规则components/rank-points/
榜单礼物components/rank-rewards/
单榜 Top3components/single-top3/
小礼物爆大礼物components/custom-lucky-gift/
红包雨components/custom-red-envelope-rain/

生成新积木时,可复制最相近的积木目录,再按业务改 type、props、布局与 composable。

清单(生成新积木时自检)

结构与类型

  • 目录名 kebab-case,入口文件 custom-<积木名>.vue
  • types/index.ts 定义 UiMeta/Props(及 Emits 若有),与 config/default-ui-meta.ts 结构一致
  • config/ 三文件规范
    • config/default-styles.ts 导出 ThemeStyleConfig 类型 + themeConfig 对象(多主题样式配置)
    • config/default-ui-meta.ts 导出 defaultUiMeta(组件默认配置,图片为空串,theme 引用 default-styles)
    • config/ui-meta.ts 导出 uiMeta(页面配置示例,包含实际图片 URL,有使用说明注释)
    • config/index.ts(可选)导出工具函数和常量
  • config/default-ui-meta.tstype: 'custom-<积木名>'
  • Props 使用 withDefaults(defineProps<Props>(), …),类型来自 types,defaultUiMeta 从 ./config/default-ui-meta 引入,无 any
  • 创建 styles/index.scss 文件,在 Vue 的 <script> 内通过 import './styles/index.scss' 引入(禁止仅用 <style src="...">,否则样式可能不生效)

use-meta 与数据

  • use-meta.ts 接收 props(类型明确),导出 outerStyle、titleStyle、rewardBorder/Bg、stretchConfig、images、textConfig 等
  • 配置读取用可选链 ?.??,文本通过 genComponentCurRegionText + default + props.text 合并

布局与样式(对齐 ui2,固定样式与动态样式分离)

  • 最外层 a-section + ui2-stretch;玩法标题强制标准(4.3.1):SCSS 必须含 __title-row(display flex、居中、padding-top 0.28rem)与 __title(margin 0 0.16rem),模板使用 xxx-wrapper__title-rowxxx-wrapper__title;content 上下 0.4rem、装饰 1.08×0.34rem、explain 与礼盒对齐 0.4rem;奖励用 rewardBorder/Bg + ui-prop-wrapper
  • 【去送礼】按钮(若积木带该入口):强制标准(4.4)SCSS 仅 __btn-container { margin: 0.36rem }__btn { width: 3.4rem; height: 0.88rem; border-radius: 0.44rem },模板为 __btn-container fx-center + __btn fx-center fx-c-center + :style="xxxBtnStyle" + v-html="textConfig.sendGiftBtnText" + @a-tap="toSendGift";点击逻辑默认 ins.$we('jumpDeepLink', 'wespydeeplink://voice_room_list')
  • 礼物/奖励角标 tag强制(4.3.2)角标必须有背景样式,在组件 SCSS 中写死(黄底、padding、border-radius、左上角定位)或通过 styles.rewardTag 传入,禁止仅字色无背景
  • 边框仅包切图强制(4.3.2)奖励/礼物边框容器仅包裹切图+tag,名称与金币在框下展示,结构为 __reward-wrap__reward-item(切图+tag)→ __reward-info(名称+价值)
  • 样式禁止简写:default-styles / themeConfig / 所有 xxxStyle 仅用标准 CSS 属性(top、width、fontSize、color、margin 等)+ rem,禁止 pt/pb/wh/f/pc 等简写;合写已拆分
  • 固定样式写在 SCSS:布局、尺寸、间距、圆角等写在 styles/index.scss,使用 BEM 命名,嵌套 ≤3 层
  • 动态样式用 :style:主题色、渐变等来自 themeConfig 的样式通过 :style 绑定
  • class + :style 结合:模板中通过 class 应用固定样式,通过 :style 绑定动态样式

页面与规范

  • 页面中通过 useCompUiMeta() 取配置,:uiMeta="compUiMeta.xxxKey" 传入;组件局部 import 注册
  • 使用 ins = useInstance() 替代 this;无 mixin;Vue 2.7 兼容(无 defineModel、Emits 具名元组等)

更细的字段说明见 reference.md;Vue/TS/样式/ pageCenter 详细规范见 ui2-upgrade-guide

Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.

<a href="https://skillzs.dev/skills/mercury-api.wepieoa.com/fe-custom-components-skill">View custom-components-skill on skillZs</a>