运营中心 · 小改版设计规范

基于 master 分支,新增「商品」和「计价」两个原型模块(共 9 个页面), 主题色由橙 #FF921C 切换为蓝 #4362FF(与其他蓝色产品对齐), 统一侧栏图标风格(codesign → 恐龙图标库 duotone), 对齐公司设计规范的色彩 Token 体系。本页用于前端交接,记录所有改动点。

9
新增页面
24
恐龙图标
14
侧栏图标替换
5
已有文件改动
Design Token

色彩规范

全局色彩通过 config/theme.ts 注入 AntD ConfigProvider, 以 CSS 变量形式输出(前缀 --alaya-*),全项目共享。

主题色 · Primary

品牌主色 #4362FF,按钮、链接、选中态、菜单激活态。本次改版从橙 #FF921C 切换到蓝色,与其他蓝色产品对齐。

blue-1#F0F5FF
blue-2#E6EDFF
blue-3#BDCEFF
blue-4#94ADFF
blue-5#6B89FF
primary#4362FF
blue-7#2E44D9
blue-8#1D2CB3
blue-9#0F188C
blue-10#0A0D66

中性色 · Gray

10 阶灰色,全部文字色、边框色、填充色、背景色都从此推导。

gray-1#F8FAFC
gray-2#F1F5F9
gray-3#E1E7EF
gray-4#C8D5E5
gray-5#9EACBF
gray-6#65758B
gray-7#48566A
gray-8#344256
gray-9#0F1728
gray-10#090E1A

辅助色 · Orange 原主色 · 保留供 warning 状态色使用

原主色橙色降级为辅助色。状态色(待审核、待支付等)若需暖色调从此选取,不要新增其他色族。

orange-1#FFF7E6
orange-2#FFDFA3
orange-3#FFCC7A
orange-4#FFB752
orange-5#FF9F29
orange-old#FF921C
orange-6#FF8400
orange-7#D96900
orange-8#B35000
orange-9#8C3A00
Design Token

文字层级

文字色严格使用 语义层变量(如 colorTextcolorTextSecondary), 而不是直接引用 gray-10gray-8 等调色板层级。

colorText · gray-10
Alaya 运营中心 · 主标题文本
colorTextSecondary · gray-8
次级标题、卡片副标题
colorTextTertiary · gray-6
辅助说明、表单 placeholder、图标默认色
colorTextQuaternary · gray-5
禁用文本、菜单下拉箭头
colorPrimary · #4362FF
链接、选中态、强调文本
规范要点: 全局文字色使用语义变量,不要直接用 gray-X。当设计要调整时只改语义变量映射,全局生效,避免硬编码导致改色困难。
Design Token

语义变量

边框、填充、背景的语义层级映射,确保改版时能从单一来源调整。

边框 · Border

colorBorder
默认边框(输入框、卡片)
gray-4
colorBorderSecondary
次级边框(表格、分割)
gray-3
colorSplit
分割线、Divider
gray-2

填充 · Fill

colorFill
深填充(强调背景)
gray-4
colorFillSecondary
次级填充
gray-3
colorFillTertiary
三级填充(hover 浅底)
gray-2
colorFillQuaternary
四级填充(最浅底色)
gray-1

背景 · Background

colorBgLayout
页面外层背景
gray-2
colorBgContainer
卡片、Drawer、Modal 容器
#FFFFFF
Design Token

间距 Spacing

全部使用 AntD 标准 token(与 Figma 设计稿一致)。 基础尺寸 sizeUnit: 4 + sizeStep: 4,输出 11 档 Size.Base 阶梯。 Padding / Margin 都是 Base 的别名引用,外加 Content Padding / Control Padding 两组语义专用。 不要自己造 spacing* 自定义 token,组件直接消费 AntD CSS 变量。

Size.Base · 基础阶梯(11 档)

所有 Padding/Margin 都引用此层数值。CSS 变量 --alaya-size-*

sizeXXS
4px
--alaya-size-xxs
sizeXS
8px
--alaya-size-xs
sizeSM
12px
--alaya-size-sm
size / sizeMS
16px
--alaya-size
sizeMD
20px
--alaya-size-md
sizeLG
24px
--alaya-size-lg
sizeXL
32px
--alaya-size-xl
sizeXXL
40px
--alaya-size-xxl
sizeXXXL
48px
--alaya-size-xxxl
sizeXXXXL
80px
--alaya-size-xxxxl
sizeXXXXXL
120px
--alaya-size-xxxxxl

Padding / Margin(Base 别名)

Token引用用途
paddingXXS / marginXXS4sizeXXS极小间距
paddingXS / marginXS8sizeXS按钮 icon-文字、Space size 默认
paddingSM / marginSM12sizeSM表格单元格垂直、小尺寸 padding
padding / margin16size卡片内边距、表单项间距、模块之间
paddingMD / marginMD20sizeMD
paddingLG / marginLG24sizeLG大尺寸内边距
paddingXL / marginXL32sizeXL
paddingXL2 / marginXXL40sizeXXL大块内容间距
paddingXL3 / marginXXXL80sizeXXXXL章节级间距
paddingXL4120sizeXXXXXL

Content Padding(内容专用)

内容区域专用 padding,页面水平内边距用 paddingContentHorizontalLG,标题区垂直用 paddingContentVertical

Token用途
paddingContentHorizontal16内容水平 padding
paddingContentHorizontalLG24页面水平内边距
paddingContentHorizontalSM16内容水平 SM
paddingContentVertical12标题区垂直 padding
paddingContentVerticalLG16内容垂直 LG
paddingContentVerticalSM8内容垂直 SM

Control Padding(控件专用)

Token用途
controlPaddingHorizontal12输入框/按钮内水平 padding
controlPaddingHorizontalSM8小尺寸控件水平

使用示例

// DO:直接用 AntD 标准 CSS 变量(与 Figma 一致)
.my-page {
  padding: 0 var(--alaya-padding-content-horizontal-lg);  /* 24 */
}
.my-section + .my-section {
  margin-top: var(--alaya-margin);  /* 16 */
}
.my-table-cell {
  padding: var(--alaya-padding-sm) var(--alaya-padding);  /* 12 16 */
}

// ❌ 禁止:硬编码像素
.my-page { padding: 0 24px; }

// ❌ 禁止:自造 spacing* 语义 token
// 不要在 theme.ts 里加 spacingPagePaddingX 之类的,AntD 标准命名已经有对应 token
原则: ① 与 Figma 一致 = 用 AntD 标准 token,不要自造命名 ② 页面水平内边距 → paddingContentHorizontalLG(24) ③ 标题区垂直 → paddingContentVertical(12) ④ 模块之间垂直间距 → margin(16) ⑤ 永远不要硬编码像素
Component

DinoIcons · 恐龙图标库

24 个 duotone(双色)风格 SVG 图标,统一替换原 codesign iconfont 实现侧栏图标风格。 通过 { ReactComponent } inline SVG 方式渲染,颜色跟随 currentColor, 默认 colorTextTertiary,菜单选中/hover 跟随 colorPrimary

默认状态 · gray-6

钱包
dino-wallet
收支
dino-invoice-dollar
订单
dino-receipt
合同
dino-file-check
账单
dino-bank-statement
算力包
dino-box
导出记录
dino-file-download
监控
dino-clipboard-list
客户
dino-users
企业
dino-office
合作
dino-handshake
商品
dino-bag-shopping
商品管理
dino-box-check
计价
dino-coins
营销
dino-marketing-target
充值
dino-circle-dollar
优惠券
dino-coupon
礼品
dino-gift
兑换码
dino-discount-code
权限
dino-shield-check
消息
dino-notification
系统设置
dino-gear
标签
dino-hashtag
证照
dino-license

使用方式

// 1. 在 route config 中使用 dino-* 字符串
{
  name: '商品',
  icon: 'dino-bag-shopping',
  path: '/ops/misc/commodity',
}

// 2. app.tsx 的 menuDataRender 自动转换为 React 组件
menuDataRender(menuData) {
  const patchDinoIcons = (items) => items.map((item) => {
    if (typeof item.icon === 'string' && item.icon.startsWith('dino-')) {
      item.icon = React.createElement(dinoIconMap[item.icon]);
    }
    return item;
  });
  return patchDinoIcons(menuData);
}

Button 内图标对齐(全局强约束)

任何 SVG 图标跟文字共存(Button icon prop / flex 容器 / inline span)必须 inline-flex + alignItems center + lineHeight 0,否则 RemixIcon / DinoIcons SVG 默认 baseline 对齐会比中文字偏下 1-2px。

全局 CSS 兜底已写好src/global.ui.css):
.ant-btn .ant-btn-icon {
  display: inline-flex !important;
  align-items: center !important;
  line-height: 0 !important;
}
.ant-btn .ant-btn-icon > svg {
  display: block !important;
}
直接用 <Button icon={<RiXxxLine size={14} />}>文字</Button> 即可,不用每处再 hack
flex 容器手写 icon + 文字场景: 必须三件套:
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
  <span style={{
    display: 'inline-flex',
    alignItems: 'center',
    lineHeight: 0
  }}>
    {icon}
  </span>
  <span>文字</span>
</div>
三件套缺一不可(漏 lineHeight: 0 还是会下沿偏移)。

详见 feedback_inline_icon_alignment.md(项目记忆,强约束 — 写代码时主动应用,不等用户指出再修)。

Specification · 产品 × 前端

Alert 应用规约

列表页标题下「那一行字」到底该不该写、写什么,不只是前端样式问题,更是产品文案规约。 现状是同一种"页面级文案"被两个 API(alertPropsheader.subTitle)混用, 且 PM 写文案时**没有区分模块描述和 Alert 提示的语义**。本节给 PM 一套判定规则,给前端一份待清洗清单。

统一规则:所有页面级 Alert(不论是规则说明 info 还是动态告警 warning)一律走 PageContainer.alertProps渲染到标题白底块内。type 由业务传值,颜色和图标自动跟随。

核心判定:模块描述 vs Alert 提示

维度模块描述Alert 提示
回答的问题 这个页面是什么 用这个页面要知道什么
内容性质 介绍功能(CRUD 同义反复) 注意事项 / 业务规则 / 数据特性 / 警告 / 操作前提
与标题关系 同义反复(标题已说明) 标题不能传达
用户价值 看完 = 没看 影响判断 / 决策
例子 "查看和管理客户账户算力包"
"记录用户在系统中的所有操作过程和结果"
"账单明细数据相对于实际费用消耗会有延迟"
"状态流转:内测 → 公测 → 运营 → 已下线"
处理建议 不写(标题已经够了) PageContainer.alertProps

type · 颜色 · 场景

Alert 不论何种类型,位置统一(标题白底块内)。颜色 / 图标按 type 自动匹配(图标在 pageContainer 里按 type 映射)。

type颜色 token使用场景 / 例子
info(默认) colorInfo(蓝) 静态规则说明、数据特性、操作前提
例:「状态流转:内测 → 公测 → 运营 → 已下线」「分层定价规则」
warning colorWarning(Gold) 动态业务告警 / 风险提醒 / 仅异常时显示
例:「底层资源告警:1 个算力中心已下线」「3 张账单超期未支付」
success colorSuccess(Green) 不在 Alert 范畴;操作成功用 message.success()
error colorError(Red) 不在 Alert 范畴;操作失败用 message.error()Result

代码实现

// 静态 info 规则说明(默认 type)
<PageContainer
  alertProps={{ description: '状态流转:内测 → 公测 → 运营 → 已下线' }}
/>

// 动态 warning 业务告警(条件渲染:异常时才有 description)
<PageContainer
  alertProps={hasResourceAlert ? {
    type: 'warning',
    description: `底层资源告警:${...}`,
    action: <a>查看影响明细</a>,  {/* 右侧操作(可选) */}
  } : undefined}
/>

全项目盘点(15 处)

截至 2026-05-08,所有列表页头部含文案的 15 处实例分类如下(不含商品管理 warning 告警 1 处)。全部走统一 alertProps 留 / 删 / 待 PM 复核 。每条都标注了菜单位置,方便对照页面查看。

菜单位置组件当前 API文案 / 性质动作
A. ALERT — 含信息量,应保留
综合中心 → 计价 → AIDC 定价pricing/AidcListalertProps状态流转:内测 → 公测 → 运营 → 已下线
业务规则,标题不能传达
保留
综合中心 → 商品 → 套餐包管理commodity/BundleListalertPropsDEDUCTION 抵扣包 vs DCU_PACK 算力包…
枚举类型区别
保留
综合中心 → 计价 → 计量项管理pricing/MeterListalertProps分层定价:基准单价 + DCU + 系数
计费逻辑
保留
费用中心 → 账单管理 → 账单明细bill/BillListalertProps账单明细数据相对于实际费用消耗会有延迟
数据延迟提示
保留
综合中心 → 营销管理 → 算力兑换voucher/VoucherListsubTitle新建和管理兑换码…兑换后转为算力包
业务流程
转 alertProps
费用中心 → 账单管理 → 手工账单
(hideInMenu,仅 bsmAdmin)
bill/HandmadeBillalertProps请下载《手工计量》模板…
含交互按钮
保留
B. 待 PM 复核 — 文案语义未审
费用中心 → 账单管理 → 月度账单bill/BillMonthalertProps多段条件渲染(含 i18n)PM 复核
费用中心 → 账单管理 → 月结算单bill/BillSettlementalertPropsi18n 短句PM 复核
费用中心 → 导出记录bill/ExportalertPropsi18n 短句PM 复核
综合中心 → 营销管理 → 算力透支overdraftalertPropsi18n 短句PM 复核
C. 模块描述 — CRUD 同义反复,建议删除
综合中心 → 客户管理 → 企业管理org/OrgListsubTitle本平台支持多客户管理。可以创建多个客户…(60+ 字 CRUD 废话)删除
综合中心 → 系统设置 → 操作日志log/LogListsubTitle记录用户在系统中的所有操作过程和结果删除
费用中心 → 收支管理 → 现金收支account/CashTranssubTitle查看和管理客户账户现金收支明细删除
费用中心 → 收支管理 → 算力收支account/DcuTransListsubTitle查看和管理客户账户DCU收支明细删除
费用中心 → 算力包管理package/ListsubTitle查看和管理客户账户算力包删除

文案标点:句末统一不带句号

不论文案是完整陈述句、祈使句,还是图示型规则——所有 alert 末尾一律不加句号

为什么不加?四条理由:

1. 一致性优先:图示型规则(如 AidcList "状态流转:内测 → 公测 → 运营 → 已下线")末尾加 "。" 视觉别扭。 若用"完整句加 / 图示型不加"的条件规则,相当于让 PM 每写一条都要做一次判断,**规则成本高且容易破例**。 统一不加 → 规则只有一条,机械执行不出错

2. 业界惯例:Material Design / Apple HIG / AntD 官方文档示例中,UI 提示类(alert / tooltip / hint / 表单帮助文字)普遍不带终结标点。 Alert 不是正文段落,本身就是一个有边界的视觉块(icon + 蓝底),不需要标点收口。

3. 视觉重量:Alert 高度只有 30 px,单行短句加句号会让句尾"压"住一个小黑点,与轻量提示气质不符。

4. i18n 友好:英文环境下 "..." 和 "...." 句末标点逻辑与中文不一致。统一不加,多语言切换时不需要为标点再做适配。

下一步动作

本分支已完成:
· 视觉方案确定 —— Alert 移入 PageHeader.footer(与标题同处白底块)+ 去除描边。本节展示的就是新视觉。
· 组件层规则收口 —— type 硬编码 info,不暴露给业务(避免 PM 滥用 warning/error)。

后续小迭代待办(需 PM 协同):
1. PM 侧 —— 拿上面【全项目盘点】这张表逐条 review,确认每条是删除、保留、还是补新的。
2. 前端侧 —— 删除 5 条「模块描述」性质的 subTitle;把 1 条 subTitle(VoucherList)转成 alertProps
3. 文案标点统一 —— 把 9 条现存 alert 末尾的"。"全部去掉(PM 可以一次性提 issue 给翻译/i18n 团队)。
4. 组件侧(可选) —— 评估是否在 pageContainer 里彻底**移除 subTitle API 入口**,让 PM 想填废话也没地方填,靠组件强制收口语义。

FAQ

Q: 那"操作成功"、"操作失败"提示用什么?
都不用 PageContainer.alertProps
· 成功 → message.success() toast
· 失败 → message.error()Result 组件
· 风险确认(删除不可恢复等)→ Modal.confirm
· 表单字段校验 → Form 自带 errorMessage
PageContainer.alertProps 只承载页面级、静态、规则性的提示。

Q: 我的 alert 文案超过 2 行了能写吗?
说明这条提示已经超出"一句话注意事项"的范畴,应该:
· 拆分成多条短句的列表
· 或改成「使用说明」按钮,点开 Drawer/Modal 看详细规则
不要在 alert 里堆长文。

Q: PageContainer.alertProps 的 type 可以传哪些值?
'info'(默认)/ 'warning'
· info → 静态规则说明(蓝)
· warning → 动态业务告警(黄),通常配合条件渲染(仅异常时传 description)
success / error 不在 Alert 范畴 —— 用 message.success() / Modal.error() / Result

Q: 业务告警(如"3 张账单超期")也走 alertProps 吗?
。统一规则:所有页面级 Alert 都走 alertProps,传 type: 'warning' 即可。条件渲染 `description` 即可控制"仅异常时显示"。

Q: 告警 Alert 右侧能放"查看明细"按钮吗?
可以,传 actionalertProps
alertProps={{ type: 'warning', description: '...', action: <a>查看影响明细</a> }}
AntD Alert 的 action 槽位会渲染在右侧。
Component

Tag 标签

统一使用 AntD Tag 组件的 color 预设值(success/error/warning/processing), 不要直接用 color="green"color="red" 等颜色名,避免与主题脱节。

语义预设

通用语义池,业务方按"成功/失败/警告/进行中/默认/品牌"语义选择,文案自填。

color效果语义场景常见文案示例
success Success 成功 / 启用 / 正常 销售中、已启用、运行中、已通过、健康、在线…
error Error 失败 / 异常 / 终止 已停售、失败、已下线、异常、已拒绝、离线…
warning Warning 警告 / 待处理 / 提醒 待审核、待支付、即将到期、风险、待确认…
processing Processing 进行中 / 加载 / 同步 处理中、同步中、构建中、上传中、计算中…
default Default 中性 / 草稿 / 已结束 草稿、已结束、未开始、归档、—
品牌强调 Brand 品牌强调 / 推荐 / 重要 VIP、推荐、新品、官方、Pro

使用示例

// DO:用语义 color 预设,文案随业务自填
<Tag color="success">销售中</Tag>
<Tag color="success">运行中</Tag>
<Tag color="warning">待审核</Tag>
<Tag color="processing">同步中</Tag>

// DON'T:硬编码颜色名 / 色值
<Tag color="green">销售中</Tag>
<Tag color="#52c41a">销售中</Tag>
选用原则: 新增状态时优先映射到现有语义池(success/error/warning/processing/default)。 不要为每个新业务状态新增专属色,避免色彩失控。

生命周期 4 态映射(强约束)

运营中心所有"生命周期阶段"状态(内测/公测/运营/已下架)必须按下表映射,跨页面统一。

状态 key显示文案Tag color视觉业务含义
internal内测default内测仅内部账号可见 · 无 SLA · 不计费
beta公测warning公测对指定客户群开放 · 有限 SLA · 可按公测价计费
active运营success运营对所有客户开放 · 完整 SLA · 正式计费
offline已下架default已下架不再接受新订单 · 存量资源走迁移 / 退款
· 用 Tag 不用 Badge:生命周期是"分类阶段"语义(彩色块),不是"实时动态状态"(圆点)
· 禁 processing 蓝:跟主题主色冲突,状态语义不清
· key 用 active 不用 operating:跨页面统一 key 命名(AidcList 已统一)
· 商品视角文案例外:CommodityList offline 显示为「停售」(PM 原文案保留)

分类 Tag vs 状态 Tag — 视觉重量必须拉开

同卡片/同行里"分类"(产品线/类型)+ "状态"(生命周期)共存时,必须视觉区分,否则用户分不清。

用途写法视觉重量
状态(生命周期)<Tag color="success">运营</Tag>彩色背景(突出)
分类(产品线 / 类型)推荐<div className=styles.cardLine>计算</div> 文字描述
或:<Tag>计算</Tag> plain(无 color)
灰字 / 灰边框(低调)
禁用: 分类 Tag 用 hue color(blue / cyan / gold / purple 等),跟状态 Tag 视觉同款会让用户混淆。详见 feedback_tag_plain_classification.md
Component

表格列规范

所有 ProTable / Table 共用一套操作列宽度档位数值列对齐规则, 避免每个页面各算一套尺寸。新页面强约束,存量页面随 bug fix / 改版逐步迁移。

操作列宽度档位

档位 = 最小可读宽度。视觉舒适宽度 ≈ 档位 × 1.25(觉得局促时按 1.25 加宽)。

// 公式
列宽 = 单元格左右 padding (32px)
     + Σ 操作文字宽度
     + (操作数 - 1) × 8px 间距
     + 16px 安全余量

// 参数依据
AntD Table padding-inline   : 16px (默认)
OperationRender / Space gap : 8px (默认)
中文 14px 正文              : 2 字 ≈ 28px · 3 字 ≈ 42px
操作数量典型示例最小宽度舒适宽度说明
1 个 编辑 / 查看 80px 100px 单操作页常用
2 个 短词(2 字) 编辑 + 删除 120px 140px 最常见
2 个 含长词(≥3 字) 编辑 + SKU 管理 160px 180px 长操作 ≥3 字
3 个 短词 编辑 + 删除 + 启用 180px 200px
3 个 含长词 SKU 管理 + 编辑 + 停售 200px 220px
4+ 个 编辑 + 删除 + 复制 + 更多▾ 220px 第 4 个起收进 Dropdown「更多 ▾」
间距 / 分隔: 统一用 OperationRender space(AntD <Space> 默认 8px)。 不要| / <Divider /> 分隔操作 — 项目里已统一靠间距区分。

删除操作的 friction

删除走 Modal.confirm 二次确认即可(已是项目惯例)— 这就是 friction,不要再嵌进 Dropdown 隐藏, 否则用户找不到。隐藏式的「更多 ▾」只用于操作数量 ≥ 4 时压缩视觉。

数值列右对齐

价格、金额、单价、数量、配额、百分比等数值类列必须 align: 'right', 小数点视觉自然对齐,扫读时易做大小比较。配 D-DIN 数字字体(global.css 已注册)。

列类型对齐示例
价格 / 金额 / 单价align: 'right'¥35.20/h · ¥1,000.00 · ¥0.50
数量 / 计数 / 配额align: 'right'128 · 3 · 1,024
百分比 / 比率align: 'right'92.5% · 0.85x
文本(名称 / 描述)left(默认)商品名称 / 介绍
时间戳left(默认)2026-05-09 18:40:20
状态 Tagcenterleft● 运营 / Tag 胶囊
操作left(与首列一致)编辑 删除

代码示例

// ✅ 价格列:右对齐 + D-DIN 字体 span 包裹
{
  title: '价格',
  dataIndex: 'price',
  width: 140,
  align: 'right',
  render: (_, r) => <span className={styles.numericValue}>{r.price}</span>,
}

// ✅ 操作列:固定宽度 + OperationRender
{
  title: '操作',
  valueType: 'option',
  width: 120, // 2 个短词最小档
  render: (_, r) => (
    <OperationRender space>
      <Typography.Link onClick={() => openEdit(r)}>编辑</Typography.Link>
      <Typography.Link type="danger" onClick={() => handleDelete(r)}>删除</Typography.Link>
    </OperationRender>
  ),
}

// .ui.less 里数字字体(global.css 已注册 D-DIN @font-face)
.numericValue {
  font-family: 'D-DIN', sans-serif;
  font-weight: 500;
  letter-spacing: 0.2px;
}

反例

❌ 错✅ 对
width: 'auto' 让 AntD 自动算 — 行宽不一致显式按档位定 width
同表两行操作数量不同导致换行取最长那行的档位
| / <Divider /> 分隔<Space> 8px 间距
价格列默认左对齐align: 'right' + D-DIN 字体
把删除按钮藏进 Dropdown「更多」保留可见 + Modal.confirm 即可
详细规约: 见仓库 ui/doc/tableColumnSpec.md。新页面落实,存量页跟着 bug fix 迁移,不一次性扫。

编码字段(code / key / id)展示规约

运营中心所有编码字段(商品编码 / 套餐 code / 产品 code / 区域 code 等)统一展示样式。
核心规则:主题色 = 可下钻。所有蓝色 Typography.Link 必须有真实跳转/Modal,不能是死链接。

场景写法视觉
卡片内(标题旁紧挨) <span className={styles.cardCode}>{code}</span>
+ .cardCode { font-size:12px; color:var(--alaya-color-text-quaternary); }
灰色 12px 小字,inline 紧挨标题
表格独立列 有详情页 <Typography.Link onClick={跳详情}>{code}</Typography.Link> 蓝色链接(点击跳详情/弹 Modal)
表格独立列 无详情页 <Typography.Text>{code}</Typography.Text> 普通黑字(不要用主题色,避免"死链接")
禁用: <Typography.Text code>(白底 + 灰边框 + 等宽 SF Mono)—— 这套是"嵌入代码片段"样式,不适合业务编码。
<Tag> 包编码 —— Tag 是"分类/状态标签"语义,不是 ID 展示。
主题色无跳转 = 死链接 —— 跟"主题色 = 可下钻"规约冲突,用户会困惑"为什么点击没反应"。
详见 feedback_code_field_display.md
Component

表单选择器组件

Form 互斥单选 / 多选场景下我们有 6 种组件可选:AntD Segmented、AntD Radio.Group(button)、3 个自定义 Alaya* 卡片组件(AlayaTabs / AlayaCheckboxCards / AlayaRadioStackCards)、Select 下拉。每种视觉重量 / 适用场景不同,避免随意混用,按下方决策树选。

决策树

                                     ┌─ 视图 / 模式切换(table↔card · tab 导航)? ──→ Segmented(滑块)
                                     │
                                     │                                  ┌─ 想要 均分铺满? ─→ + .alaya-radio-block
                                     │── Form 短词 ladder / 等价单选 ──┤
                                     │                                  └─ 紧凑跟随内容? ─→ 默认 outline
                                     │                                  (以上两种都用 Radio.Group(button))
   单选 ── 2-4 项 ───────────────────┤
                                     │── 重要决策 · 含图标 · 仅标题 ────────────────→ <AlayaTabs>
                                     │
                                     └── 重要决策 · 含 标题 + 描述 ──────────────────→ <AlayaRadioStackCards>

   多选 ── 2-N 项 · 需 status 配色(运营/公测/内测/下线)─────────────→ <AlayaCheckboxCards>

   ≥ 5 项(单选)─────────────────────────────────────────────────→ Select
   ≥ 8 项 + 长选项 ──────────────────────────────────────────────→ Select showSearch

6 种组件对比

组件视觉特征数据语义典型场景
Segmented 共享灰底容器 + 滑块指示器 视图 / 模式切换 List/Kanban 切换、列表 toolbar 视图切换、tab 式导航
Radio.Group(button) + .alaya-radio-block 连接的按钮(共享边框)· .alaya-radio-block 加上后均分宽度 Form 字段:ladder 进度 / 短词等价单选 新建商品 Drawer:状态(内测/公测/运营)、区域筛选、period 选择
<AlayaTabs> 自定义组件 水平独立卡片 + 8px gap + 蓝边/浅蓝底选中 Form 单选:重要决策(仅标题,含图标) 新建商品 Drawer:销售渠道 / 计费形态 / 计价策略
<AlayaRadioStackCards> 自定义组件 垂直堆叠卡片 + 圆形 radio + icon + 标题 + 描述 Form 单选:重要决策(含 1~2 行描述帮助理解) 新建商品 Drawer Step 2:默认可见人群(公开 / 限定客户群)
<AlayaCheckboxCards> 自定义组件 水平独立卡片 + 5 态配色(active/beta/internal/offline/unsupported)+ 右侧 tag 小标 Form 多选:需要 status 区分语义的多选场景 新建商品 Drawer Step 2:可售区域(蓝运营 / 黄公测 / 浅灰内测 / 灰底划线已下线)
Select 下拉框 选项数多 / 节省垂直空间 / 需搜索 所属产品(8 项)、城市选择、用户列表

代码示例

// ✅ Segmented(视图/模式切换 — table↔card 类)
<Segmented
  options=[
    { label: '表格', value: 'table', icon: <RiTableLine /> },
    { label: '卡片', value: 'card', icon: <RiLayoutGridLine /> },
  ]
/>

// ✅ Radio.Group(button) + alaya-radio-block(Form ladder · 均分铺满)
<Radio.Group
  optionType="button"
  className="alaya-radio-block"
  options=[
    { label: '内测', value: 'BETA' },
    { label: '公测', value: 'PUBLIC_BETA' },
    { label: '运营', value: 'ACTIVE' },
  ]
/>

// ✅ Radio.Group(button) 默认(短词等价 · 紧凑跟随内容)
<Radio.Group optionType="button">
  <Radio.Button value="hangzhou">杭州</Radio.Button>
  <Radio.Button value="shanghai">上海</Radio.Button>
  <Radio.Button value="beijing">北京</Radio.Button>
</Radio.Group>

// ✅ AlayaTabs(水平卡片单选 · 仅标题 + 图标)
<AlayaTabs
  options=[
    { label: '线上', value: 'online', icon: <RiGlobalLine size=20 /> },
    { label: '线下', value: 'offline', icon: <RiFileTextLine size=20 /> },
  ]
/>

// ✅ AlayaRadioStackCards(垂直堆叠单选 · 含描述)
<AlayaRadioStackCards
  options=[
    {
      value: 'public',
      title: '公开',
      description: '全部客户可见 · 控制台自助下单',
      icon: <RiGlobalLine size=16 />,
    },
    {
      value: 'group',
      title: '限定客户群',
      description: '仅勾选的客户群可见 · 多选取并集',
      icon: <RiTeamLine size=16 />,
    },
  ]
/>

// ✅ AlayaCheckboxCards(多选 · status 5 态配色)
<AlayaCheckboxCards
  options=[
    { value: 'cn-hangzhou', label: '华东 1(杭州)', status: 'active' },
    { value: 'cn-shenzhen-gpu', label: '深圳 GPU', status: 'beta', tag: '公测' },
    { value: 'ap-singapore', label: '新加坡', status: 'internal', tag: '内测' },
    { value: 'cn-qingdao', label: '青岛', status: 'offline', tag: '已下线' },
  ]
/>

// ✅ Select(≥ 5 项)
<Select
  options=[
    { label: '弹性容器集群 VKS', value: 'VKS' },
    { label: '云容器实例 CCI-INSTANCE', value: 'CCI_INSTANCE' },
    { label: '带宽 BANDWIDTH', value: 'BANDWIDTH' },
    { label: '弹性公网 IP EIP', value: 'EIP' },
    { label: '大模型 API LLM', value: 'LLM' },
    { label: '对象存储 OSS', value: 'OSS' },
    { label: '文件存储 NAS', value: 'NAS' },
    { label: '块存储 EBS', value: 'EBS' },
  ]
/>

<AlayaTabs> 规格(自定义组件)

Figma 设计稿 27148:18292 / 26978:48187 对齐。源码 src/components/AlayaTabs/。每个 item 独立边框 + 圆角 + 8px gap + 三态主题色,自适应宽度跟随内容。

维度Token
高度(default)32px1+4+22+4+1 = 32(项目默认 control 高)
高度(large)40pxsize="large" · 1+8+22+8+1 = 40
圆角6px--alaya-border-radius
边框1px solid默认 --alaya-color-border · 选中 --alaya-color-primary
Padding (default)4px 24px4 / --alaya-padding-lg
Padding (large)8px 24px--alaya-padding-xs / --alaya-padding-lg
icon ↔ text gap8px--alaya-padding-xs
item 之间 gap8px--alaya-padding-xs
图标尺寸16px / 20pxRemixIcon size 由调用方传
选中态 bgblue1 浅蓝--alaya-color-primary-bg
右上角 ✓ 角标opt-inshowBadge 默认 false,需要再开

<AlayaCheckboxCards> 规格(多选 · 5 态配色)

源码 src/components/AlayaCheckboxCards/。AlayaTabs 的多选扩展,每项含内置 16×16 勾选框 + label + 可选 tag 角标。status 决定卡片配色与可勾选性。

status语义配色可勾选
active运营蓝边 + 浅蓝底(--alaya-color-primary / -primary-bg是(默认勾选目标)
beta公测黄边 + 浅黄底(--alaya-color-warning / -warning-bg)+ 右侧黄 tag
internal内测浅灰文字 + 灰底(--alaya-color-text-secondary)+ 灰 tag是(语义降权但仍可勾)
offline已下线灰底删除线(--alaya-color-fill-quaternary + line-through(组件自动 disabled)
unsupported当前规格族不支持同 offline(组件自动 disabled)
维度Token
高度(default / large)32px / 40px同 AlayaTabs
勾选框16×16 圆角 3px,选中态填 status 主色内置,无需外部传图标
item gap8px--alaya-padding-xs
tag 小标11px / radius 4px / 跟随 status 配色

<AlayaRadioStackCards> 规格(垂直堆叠单选 · 含描述)

源码 src/components/AlayaRadioStackCards/。每项占满父容器宽度,左侧圆形 radio + 右侧 icon + 标题 + 描述。适用于「重要决策且需要 1~2 行描述帮助用户理解」的场景,AlayaTabs 撑不下描述时用这个。

维度Token
布局垂直堆叠 · 占满父宽flex-direction: column
Padding12px 16px--alaya-padding-sm / --alaya-padding
左侧 radio16×16 圆形 · 选中态填主色 + 内圆白--alaya-color-primary
选中态卡片蓝边 + 浅蓝底--alaya-color-primary / -primary-bg
icon ↔ title 间距8px--alaya-padding-xs
title14px / 22 line-height · font-weight 500
description12px / 20 line-height · tertiary 灰--alaya-color-text-tertiary
item 间距8px--alaya-padding-xs

.alaya-radio-block 规格(Radio.Group 均分修饰)

AntD 5.18 的 Radio.Group 还没有原生 block prop(5.21+ 才加)。给 Radio.GroupclassName="alaya-radio-block",每个按钮 flex:1 平分容器宽度。源码 src/global.ui.css

用法说明
<Radio.Group className="alaya-radio-block" />占满父容器宽度,每个 button 等分
不加该 class跟随内容宽度(紧凑)

反例

❌ 错✅ 对
2 个等价短词用 Select 下拉用 Radio.Group(button)
状态 ladder 用 AlayaTabs(独立卡)用 Radio.Group(button) + alaya-radio-block(短词均分更克制)
表头 table↔card 切换用 Radio.Group(button)用 Segmented(视图切换语义更准)
选项 label 用 emoji 🌐 线上用 RemixIcon 走 icon 字段
所属产品 8 项摆一排做 AlayaTabs≥5 项一律 Select
<Segmented className="alaya-tabs">(旧用法)统一用 <AlayaTabs> 组件,旧 className hack 已废弃
需要描述的单选用 AlayaTabs(只放标题撑不下)<AlayaRadioStackCards>(垂直堆叠 + 描述)
多选 + 需要 status 配色用 AntD Checkbox.Group 自定义 render<AlayaCheckboxCards>(status 5 态内置)
已下线/不支持的 region 还放成可勾的项option 加 status: 'offline''unsupported',组件自动 disabled + 划线
关键约束: 水平选择类组件(Segmented / Radio.Group(button) / AlayaTabs / AlayaCheckboxCards)只要标题,不带描述。详细解释走 Form.Item.tooltip 或字段级 label 副标题。需要描述的场景请改用 <AlayaRadioStackCards>(垂直堆叠 + 描述是它的核心定位)。
Pattern

列表展现模式选型(运营后台)

运营后台列表按性质二选一不做视图切换业务对象类用卡片(卡片 12/页 + 翻页器),流水 / 数据类用表格。哪种用哪种由列表性质决定。

两类列表分工

类型特征展示模式代表页
业务对象类 每条有"产品形象"(图标 + 名称 + 关键指标 + 价格区间) 纯卡片 · 12/页 + 翻页器 商品 / 产品 / 套餐 / 计费模式
流水 / 数据类 每条以数据为主(金额 / 日期 / 状态 / ID) 纯表格 · 默认 20/页 账单 / 月结 / 订单 / 客户管理 / 客户群成员 / 计量项 / AIDC 定价

规约要点

  • 不做视图切换器 — 哪种用哪种由列表性质决定,给业务对象强加表格 / 给数据类强加卡片都是反例。"切换器"曾被考虑但最终撤回(用户原话:「商品管理这个模块我不是说没有表格视图了吗」)
  • 卡片视图:12/页 + 翻页器 — 不做无限滚动。运营场景是「翻到第 N 页定位某条 → 跳详情 → 回来」,URL 友好分页比无限滚动顺手;24 太多 / 6 太少,12 = 3 列 × 4 行刚好一屏
  • 表格视图:20/页起步,密度高,AntD 内置排序 / 筛选 / 行选直接用
  • URL 带分页位置?page=N&pageSize=12(或 20),刷新 / 跳详情回来都不丢位置,可分享链接
  • 判断技巧 — 看每条数据是否有"图标 + 名称 + 关键指标"的"形象感"。有 → 卡片;只是金额/日期/状态/ID → 表格

代码示例

// 业务对象类:纯卡片 + 12/页 + 翻页器
<Row gutter={[16, 16]}>
  {items.slice((page - 1) * 12, page * 12).map(renderCard)}
</Row>
{items.length > 12 && (
  <Pagination
    current={page}
    pageSize={12}
    total={items.length}
    showSizeChanger={false}
    onChange={setPage}
  />
)}

// 流水/数据类:纯表格
<Table
  columns={columns}
  dataSource={items}
  pagination={{ pageSize: 20, showSizeChanger: true }}
/>

反例

❌ 错✅ 对
商品 / 产品 / 套餐 做表格视图(或给卡片加切换器)纯卡片 · 商品本身就是"产品目录"形象
账单 / 月结 / 订单 做卡片网格纯表格 · 数据扫读密度高
卡片视图做无限滚动分页器 12/页 · URL 友好 / 跳详情回来不丢位置
给卡片加 Segmented 切表格不做切换 · 哪种用哪种由列表性质决定,给用户选择反而徒增决策成本
Component · Specification

Drawer 统一规范

所有运营中心新建/编辑 Drawer 一律按本节规约重做。规约沉淀自 CommodityList 4 步 Steps 向导 Drawer,2026-05-11 同步到 ProductList / BundleList / AidcList / MeterList / BillingMode 五个 Drawer。

硬性规约表

Drawer 宽度
800 全部统一(原 520 / 560 / 600 / 640 一律收口)
Footer 布局
footer={<div flex space-between>} · 取消左 / 主操作右(详见 Drawer footer 操作按钮
表单布局
单列垂直 · 一行一个 Form.Item禁止 Row+Col 两列;Form.List 行内字段例外)
可售区域 / 多选卡
<AlayaCheckboxCards> 共享组件
类型 / 模式 3-4 选 1
<AlayaRadioStackCards direction="horizontal"> 共享组件
状态颜色
4 态映射 · 内测灰 / 公测橙 / 运营绿 / 已下架灰 · Badge status 用 default/warning/success,禁用 processing 蓝

硬性禁止(4 条)

· 不做 dev-spec / 📐 字段规则灰底块:原型里那种列正则/长度/唯一性的灰底说明是给开发看的,校验逻辑落到 Form.Item rules,UI 只保留 placeholder + 必要的 extra
· 不做 🔒 LockTag「发布后锁定」备注:disabled 输入框的灰色态本身就能传达不可编辑
· 不做概念解释 Alert:DCU/RESERVED/Region 等业务概念说明、类型对比说明 不放进 Drawer
· 不用 emoji 图标:🌐 🎯 ⚡ 🔋 🔒 等一律用 RemixIcon / DinoIcons 替代

2026-05-11 落地清单(5 Drawer)

页面原宽新宽关键改动
commodity/ProductList5208004 态升级 / AlayaCheckboxCards 可售区域 / 删 dev-spec + LockTag + Alert + emoji
commodity/BundleList560800新增 RESERVED 类型 / AlayaRadioStackCards 3 选 / 保留 LockOutlined 作为 RESERVED 功能图标(不是备注 LockTag)
pricing/AidcList600800单列垂直 / footer / 4 态颜色
pricing/MeterList640800meter 编辑 + 价格编辑 两个 Drawer / footer / 单列
pricing/BillingMode640800单列垂直 / footer / 4 态颜色

代码模板

import { Drawer, Form, Button, Space } from 'antd';
import AlayaCheckboxCards from '@/components/AlayaCheckboxCards';

<Drawer
  title="新建商品"
  width={800}                              // 统一 800
  open={open}
  onClose={onClose}
  footer={
    <div style={{ display: 'flex', justifyContent: 'space-between' }}>
      <Button onClick={onClose}>取消</Button>
      <Button type="primary" onClick={onSubmit}>提交</Button>
    </div>
  }
>
  <Form layout="vertical">                // 单列垂直,禁止 Row+Col 两列
    <Form.Item name="code" label="商品编码" rules={[...]} />
    <Form.Item name="name" label="商品名称" rules={[...]} />
    <Form.Item name="regions" label="可售区域">
      <AlayaCheckboxCards options={regionOptions} />
    </Form.Item>
  </Form>
</Drawer>
Delivery

新增路由

所有页面均使用 mock 数据,前端接入时需替换为真实 API。

商品模块 · /ops/misc/commodity

路由页面组件说明
/ops/misc/commodity一级菜单,icon: dino-bag-shopping
/ops/misc/commodity/productcommodity/ProductList产品管理列表(Drawer 600px 新建/编辑)
/ops/misc/commodity/listcommodity/CommodityList商品管理列表(卡片+表格双视图)
/ops/misc/commodity/list/createcommodity/CommodityCreate新建商品(4 步 Steps 独立页面)
/ops/misc/commodity/list/:action/:idcommodity/CommodityCreate编辑商品
/ops/misc/commodity/bundlecommodity/BundleList套餐包管理(Drawer 640px 新建/编辑)

计价模块 · /ops/misc/pricing

路由页面组件说明
/ops/misc/pricing一级菜单,icon: dino-coins
/ops/misc/pricing/aidcpricing/AidcListAIDC 定价(Drawer 520px)
/ops/misc/pricing/meterpricing/MeterList计量项管理(Drawer 640px 编辑 + 定价)
/ops/misc/pricing/billingpricing/BillingMode计费模式(Drawer 560px + 绑定商品 Modal)

侧栏图标替换 · 14 项

菜单旧 icon (codesign)新 icon (恐龙)
账户管理icon-yy-zhanghuguanlidino-wallet
收支管理icon-yy-DCUshouzhimingxidino-invoice-dollar
订单管理icon-yy-dingdanguanlidino-receipt
合同管理icon-yy-hetongguanlidino-file-check
账单管理icon-yy-zhangdanguanlidino-bank-statement
算力包管理icon-yy-suanlibaoguanlidino-box
导出记录icon-yy-daochujiludino-file-download
会话监控icon-yy-mingchengziyuanjianqiangdino-clipboard-list
客户管理icon-yy-yonghuguanlidino-users
商品管理icon-yy-shangpinguanlidino-box-check
营销管理icon-yy-duihuanmaguanlidino-marketing-target
权限管理icon-yy-renyuanquanxianguanlidino-shield-check
消息管理icon-yy-gonggaotongzhidino-notification
系统设置icon-yy-biaoqiandino-gear
后续规划: 恐龙图标后期需上传 CoDesign 生成 iconfont,届时改回 icon-yy-xxx 格式并删除 DinoIcons 组件 + SVG 文件。
Delivery

改动文件清单

合并前请重点 review 以下文件。

新增文件

文件路径说明
src/pages/commodity/ProductList/index.tsx产品管理列表
src/pages/commodity/CommodityList/index.tsx商品管理列表(双视图)
src/pages/commodity/CommodityCreate/index.tsx新建/编辑商品
src/pages/commodity/CommodityCreate/index.less商品创建页样式
src/pages/commodity/BundleList/index.tsx套餐包管理
src/pages/pricing/AidcList/index.tsxAIDC 定价
src/pages/pricing/MeterList/index.tsx计量项管理
src/pages/pricing/BillingMode/index.tsx计费模式
src/components/DinoIcons/index.tsx恐龙图标组件(24 个)
src/components/DinoIcons/index.global.less图标颜色 + 菜单状态样式
src/assets/icons/icon*.svg × 24duotone 风格 SVG 资源

已有文件修改

文件改动说明
config/route/platformRoutes/misc.ts新增商品、计价模块路由 + 14 项图标替换
config/route/platformRoutes/cost.ts费用中心 8 项图标替换
src/app.tsx导入 dinoIconMap 并在 menuDataRender 递归替换;底部折叠按钮(menuFooterRender);隐藏默认折叠按钮;顶部 logo 高度 48px → 40px(对齐 Figma),logo 容器加 alignItems: 'center' 让图片和文字水平居中
config/theme.ts主题色切换:colorPrimary #FF921C 橙 → #4362FF 蓝(与其他蓝色产品对齐);菜单选中背景 secondaryOrange1blue1;对齐公司设计规范:gray/blue/orange 色板、文本色、边框色、填充色、背景色
src/components/DinoIcons/index.global.less图标默认色 colorTextTertiary;选中/hover 跟随主色;下拉箭头 colorTextQuaternary;菜单去过渡 + 子菜单展开动画 0.15s
对接提醒: 所有列表页使用 mock 数据,前端接入时需:① 替换 mock 为真实 API ② 对接 CRUD 接口 ③ 确认字段名与后端一致。

已知遗留问题(不在本次改版范围)

主题色已通过 config/theme.ts 切换到蓝色,AntD 内置组件 + 用 var(--alaya-*) 的自定义样式都自动跟随。 但项目中仍有 十几处硬编码 #FF921C 橙色,未走主题系统:

文件用途建议
public/scripts/loading.js加载页背景色改为 --alaya-color-primary
src/constants/order.ts订单状态文字色改用 colorWarning 语义 token
src/constants/notice.ts"待发布" 状态文字色改用 colorWarning
src/constants/payOrder.ts支付状态文字色改用 colorWarning
src/components/ChangeLan/index.less语言切换器 hover 色改为 --alaya-color-primary
src/components/CustomPayModal/index.tsx弹窗强调色改为 --alaya-color-primary
建议:这些都是前端早期遗留,建议后续单独起一个分支批量替换为 var(--alaya-color-primary) 或对应语义 token,不在本次小改版范围。
Modal · 下架 / 停售 / 状态变更

下架 / 停售 Modal 统一规范

覆盖项目里 6 个破坏性操作 Modal — 结构、尺寸、组件用法、文案全部对齐,让运营 / PM / 测试在不同页面看到的"下架 / 停售"操作有完全一致的体验。

1. 宽度统一 width=560

所有 stop / takedown / 状态变更类 Modal:width={560}
例外:含复杂表格 / Form.List 多列(如 SKU 价格合成追溯)可放大到 760

2. 标题规范

场景标题
产品下架下架产品「{record.name}」
商品状态变更商品状态变更
套餐包下架下架套餐包「{record.name}」
计量项下架计量项下架
SKU 停售SKU 停售
SKU 停售停续停售停续「{record.code}」

❌ 标题禁带 emoji(⏸ / 🔄 / 🛑 等)
✅ 涉及目标对象时带「目标名」让运营确认操作哪一条

3. 内容区结构

统一 3 段式(自上而下):
橙色 / 红色 Alert — 说明此操作的影响("新客户买不到、老客户可续费"等)
影响范围 div / Alert — 用 · N 个 X 列出受影响的关联数据(可选,仅复杂破坏性操作显示)
Form 收集原因 — 必填 textarea + 可选「下架后处理」Select

禁止:"彻底删除"FAQ 提示 / 6 态流转图等开发文档式内容(这些是 ui/doc 范畴)

3.1. 影响范围数字 — 中性黑色 strong,不要橙/红

规则:影响范围里的数字用 plain <strong>(中性黑色),不要color: warning/error/orange
原因:警示信号集中在顶部 Alert(橙/红背景)+ 底部 danger 按钮(红色)。影响范围是「陈述事实」不是「警告」,加颜色反而稀释顶部 Alert 的警示意图,让弹窗颜色过载。
❌ 错:<strong style={{ color: 'var(--alaya-color-warning)' }}>1 个在售商品</strong>
✅ 对:<strong>1 个在售商品</strong>

4. Form 字段规范

字段组件规则
下架原因 / 变更原因Input.TextArea rows=3 maxLength=200 showCount必填 · placeholder 用 / 分隔示例
下架后处理Select + getPopupContainer={() => document.body}避免 dropdown 被 Modal overflow 裁剪
目标状态(仅商品状态变更)Radio.Group 全宽分块3 选项卡片式,每项含主标题+二级描述
客户群多选(仅"客户群专属在售")Checkbox.Group grid 2 列条件渲染 — Radio.Group 外层用 shouldUpdate 控制

5. Footer 按钮规范

统一 AntD 默认 footer 渲染:okText / okButtonProps / cancelText / onOk / onCancel
❌ 不要自定义 footer={ReactNode} JSX(除非有特殊间距需求且全局 CSS 无法解决)
okButtonProps={{ danger: true }} 让确认按钮红色(破坏性操作)
okText 用「确认下架」/「确认停售」/「确认变更」/「确认彻底下线」明确动作

6. 全局 Modal 间距 — 跟 stop modal 共享

已在 src/global.ui.css 全局调整(影响所有 Modal):

.ant-modal-root .ant-modal .ant-modal-content > .ant-modal-header {
  padding-bottom: 24px !important;
  margin-bottom: 0 !important;
}
.ant-modal-root .ant-modal .ant-modal-content > .ant-modal-footer {
  padding-top: 24px !important;
  margin-top: 0 !important;
}
.ant-modal-root .ant-modal .ant-modal-content {
  overflow: visible !important;  /* 让 Select dropdown 不被裁 */
}

7. 6 个 stop Modal 实现一览

页面触发状态
ProductList列表行「下架」link · danger✅ 对齐
CommodityList列表行「停售」link · danger✅ 对齐(6 态变更 Modal)
BundleList列表行「下架」link · danger✅ 对齐
MeterList列表行「下架」link · danger✅ 对齐
CommoditySkuSKU 表「停售」link · danger✅ 对齐
CommoditySkuSKU 表「停售停续」link · danger(红 Alert + 必勾 checkbox)✅ 对齐
Placeholder · 提示词 / 微文案规范

文字提示词规范

覆盖 Input / TextArea / Select placeholder、Label 后缀(如「(必填)」)、Alert / Modal 内部文案。统一才能让运营 / PM / 测试一眼读懂,不被工程师术语劝退。

1. Placeholder 示例分隔符 — 统一 / (带空格)

对:placeholder="例:被新版商品替代 / 销售持续下滑 / 业务策略调整 / 合规清退"
错:placeholder="例:被新版商品替代 · 销售持续下滑 · 业务策略调整"(用了 · 中点)
错:placeholder="例:替代 ; 下滑 ; 调整"(用了 ; 分号)

为什么 / 比 · 好:斜杠在中文 placeholder 里更易识别为"或者"的语义;中点 · 容易跟正文里的中点冲突,视觉上是"和并列"而不是"或多选"。

2. 词汇选择 — 运营 / 业务语言,禁工程师术语

❌ 工程师术语✅ 运营友好说明
硬件 EOL业务线下线 / 合规清退EOL = End of Life,运营人员不知道
被 g8 替代被新版商品替代g8 是 GPU 代号,普通运营不熟
API 重构计费模型重构API 是后端实现术语
line item 拆分账单条目拆分line item 是英文行业黑话
规格族 EOL规格族迭代EOL → 迭代/替代更清楚

3. Label 后缀 — 禁冗余"(必填)"

AntD Form.Item 配 rules=[{ required: true }] 时,label 自动渲染红色 * 星号。
错:label="变更原因(必填 · 留痕用)" — "必填" 跟 * 重复,"留痕" 是后端实现说明
对:label="变更原因" — 简洁

4. Modal / Drawer 内文案 — 禁"开发文档式"

错:"💡 想彻底删除(清空目录条目)?需要先下架,等所有下属订单到期、所有引用清理后,再走数据治理流程申请删除。常规运营场景下架就够了。"
→ 这是开发说明 / FAQ,不是操作流程,运营点不到,是文档
对:直接在 Alert / 标题里说清"会发生什么"+ 二次确认即可,FAQ 单独写文档

5. 5 个下架 / 停售 Modal placeholder 现状

页面统一后的 placeholder
ProductList(产品下架)例:产品迭代替代 / 业务线下线 / 合规清退 / 客户反馈整改
CommodityList(商品状态变更)例:被新版商品替代 / 销售持续下滑 / 业务策略调整 / 合规清退
BundleList(套餐包下架)例:被新版包替代 / 业务线下线 / 优惠期结束 / 销售下滑
MeterList(计量项下架)例:被新计量项替代 / 计费模型重构 / 业务线下线
CommoditySku(SKU 停售)例:被新档位替代 / 业务线下线 / 优惠期结束 / 销售下滑

6. 通用模板

下架 / 停售类原因:
例:被新版 XX 替代 / 业务线下线 / 销售下滑 / 合规清退

新建 / 编辑类描述:
placeholder="一句话说明 XX 用法"(不要 "客户感知"、"留给前端" 之类的工程师说明)
2026-05-20 · 拟定

表格分页规约

解决"小数据表格挂废分页器 1/1 页"的视觉噪音问题。按表格性质分三档,不一刀切「全部分页」也不一刀切「全部不分页」。

表格性质 分页规约 典型场景
业务对象列表 必须分页
pageSize: 10 + showSizeChanger
ProductList / CommodityList / BundleList / CommoditySku / OrderList / OrgList / MeterList / BillingMode / AidcList
详情页关联资源 数据 ≥ 10 条 才分页;
< 10 直接全列展示
ProductDetail 计量项 / AidcDetail 计费模型·产品·商品 / 引用范围 SKU / 关联套餐
固定枚举列表 永不分页
pagination={false}
MeterList Drawer 算力中心系数 8 行 / 计费模式枚举 4 行 / 状态枚举 / 货币 / 时区

三个判断维度

1. 数据规模会不会增长?
会(业务对象类)→ 必须分页 · 不会(固定枚举)→ 永不分页 · 会但天花板低(关联资源)→ 看当前条数

2. 用户主要任务是浏览还是聚焦?
浏览全集(列表页)→ 分页帮助分批消化 · 看完所有项的关联(详情页)→ 全列展示更高效

3. 视觉噪音容忍度?
「1/1 页」「共 3 条」对所有数据 < 10 的表格都是浪费

业务对象列表 — 必须分页

数据天花板高 / 用户主要任务是筛选 + 浏览 + 操作。

<AlayaProTable
  pagination={{
    pageSize: 10,            // 默认 10/页
    showSizeChanger: true,   // 允许切 20 / 50 / 100
    showQuickJumper: true,   // 大数据集允许跳页
    showTotal: (total) => `共 ${total} 条`,
  }}
/>

详情页关联资源 — 条件分页

数据天花板低(一般 1-30) / 用户已经聚焦某个主对象 / 需要看到所有关联。

// 数据 < 10:不分页
<Table pagination={false} ... />

// 数据 >= 10:分页(同业务列表但 showSizeChanger 可关)
<Table
  pagination={{
    pageSize: 10,
    showSizeChanger: false,  // 详情页关联资源不需要 100/页
    showTotal: (total) => `共 ${total} 条`,
  }}
/>
判断时机:写组件时按 mock 数据 / API 返回长度判断;如果业务上限不确定,默认走分页(保守)

固定枚举 — 永不分页

数据规模业务上固定,加分页 = 反模式,违背"固定数据"语义。

<Table pagination={false} ... />

边界场景 FAQ

Q1: 详情页里 8 条数据,刚好低于 10 → 不分页?
A: 是。规约是 ≥ 10 才分页。8 条全列展示 (~ 320px 表高) 没问题。

Q2: 当前 6 条但 API 上限 100?
A: 走分页(保守)。规约判断"业务天花板"优先于"当前数量"。

Q3: SKU 列表算业务对象还是关联资源?
A: 看页面性质。CommoditySku 详情页里的 SKU = 关联资源(按条件分页);单独 SKU 列表页 = 业务对象(必须分页)。

Q4: 表格在 Modal / Drawer 里要分页吗?
A: 看数据性质,跟容器无关。Modal 里的引用范围表(一般 < 10)不分页;Modal 里的批量选 SKU(可能上百)必分页。

Q5: 已经有「展开 / 收起」交互的怎么办?
A: 走「折叠」交互 + 不分页。例如:可售区域勾选了 8 个里的 6 个 → 展示成 "杭州 / 上海 / + 4 个" 折叠 + 点击展开。

落地清单

写表格时按这个清单走:
1. 这个表格的数据是 业务对象 / 关联资源 / 固定枚举?
2. 业务对象 → pagination={{ pageSize: 10, showSizeChanger: true, showTotal: ... }}
3. 关联资源 → 看数据数:< 10 → pagination={false};≥ 10 → pagination={{ pageSize: 10, showSizeChanger: false }}
4. 固定枚举 → pagination={false}
5. 不确定?默认走分页(保守)

详细规约文档:ui/doc/tablePagination.md

2026-05-20 · 拟定

状态 vs 类型 — 双轴视觉规约

跨列表 / 详情页统一两类语义的视觉表达。状态用小圆点(Badge dot),类型用色块标签(Tag color),跨页面用户秒识别"圆点 = 状态属性 / 色块 = 分类身份"。

语义 形态 AntD 组件 例子
状态 小圆点 + 文字 <Badge status="..." color="..." /> 在售 / 已下架 / 内测 / 公测 / 运营中 / 应用中 / 已启用
类型 边框 + 底色块 <Tag color="..." /> PAYG / SUBSCRIPTION / SPOT / TIERED / 计算 / 网络 / 存储

Badge dot 颜色规约(状态用)

状态 key Badge prop 视觉
internal 内测color="gray"灰点
beta 公测color="orange"橙点
active 运营中 / 在售 / 应用中 / 已启用status="success"绿点
offline 已下架 / 已下线status="default"灰点

Tag color 规约(类型用)

类型 Tag 加颜色区分,对齐原型设计。

类型Tag color来源
PAYG(按量付费)blue原型 计价.html tag-blue
SUBSCRIPTION(订阅)green原型 tag-green
SPOT(抢占式)orange原型 自定义 #fed7aa
TIERED(阶梯)purple原型 tag-purple
产品线(计算 / 网络 / 存储 / AI)processing按业务约定
历史背景:2026-05-12 曾把 Badge 改 Tag(task #68),当时没有区分"状态 / 类型"两轴;2026-05-20 重新拆开 Badge dot 状态 / Tag color 类型,跨页面语义更准确。不是反复横跳,是认知升级。

详细规约:ui/doc/statusVsTypeDisplay.md

2026-05-20 · 拟定

列表页 Tab 切换规约

业务对象类列表都用 PageContainer.tabList 状态切换。不加 tab 仅 2 类例外:已用段头分类(如 CommodityList IaaS / LLM)/ 不存在状态字段。

页面 tab 项 态数
ProductList全部 / 在售 / 已下架2 态
MeterList全部 / 应用中 / 已下架2 态
BundleList全部 / 内测 / 公测 / 运营中 / 已下架4 态
AidcList全部 / 内测 / 公测 / 运营中 / 已下线4 态
BillingMode全部 / 内测 / 已启用2 态(原型只 2 态)
CommodityList(段头分 IaaS / LLM,不加 tab)例外

主轴:状态而非类型

曾尝试 BillingMode 用「类型 tab」(按量 / 订阅 / 抢占 / 阶梯)—— 撤回,理由:
① 业务上"按状态找模式"频率 > "按类型找模式"
② 类型信号通过 KPI 卡 + 表格类型列已表达
③ 跨页面 tab 都按状态切换,用户认知模型稳定

数据分布不均的处理

某态当前 mock 数据为 0 时(如 BillingMode 默认全 active),补 1-2 条 mock 让 tab 不空,业务上真实环境会有这些态。

连带改动:删除 subTitle

加 tabList 时顺手删开发文档式 subTitle(如「基础设施单元 · 所有原价和计费模式的'地理坐标'」)。PM 视角的描述应该放在帮助 / 文档,不挂页面顶部。

详细规约:ui/doc/listFilterTab.md

2026-05-20 · 拟定

active 状态文案规约

status: 'active' 状态文案带「」字表达"进行时"语义,避免名词歧义("运营"会被理解为名词"运营部门")。

页面 active 文案 业务语义
ProductList在售商品对客户开放销售中
MeterList应用中计量项被 SKU / 计费模式引用使用中
BillingMode已启用计费模式启用中(原型 计价.html line 537)
AidcList运营中算力中心机房运营中
BundleList运营中套餐包销售运营中
CommodityList运营中IaaS / LLM 商品运营中
CommoditySku 详情运营中商品在售状态

命名风格

风格适用例子
X 中持续进行的状态应用中 / 运营中
已 X已完成的状态变更已启用 / 已下架 / 已下线
业务专属已含"进行态"语义在售(在 + 售 已含进行)
反例:「运营」「应用」缺「中」字,容易理解为名词或动作;「上线」是动词,状态应是「已上线」/「已启用」。
同步 toast 文案:列表显示「已启用」时,操作 toast 也用「已启用 xxx」,不要漂移成「已上线 xxx」。

详细规约:ui/doc/activeStatusWording.md

2026-05-20 · PM 拍板

业务模型 4 层 · 产品 / 商品 / 规格族 / SKU + 计量项

Alaya 运营中心标准业务模型层级 · 跨页面认知基础。注意正确顺序是「产品 → 商品 → 规格族 → SKU」,商品在规格族上面(之前曾讲反过)。

产品(Product · 品类)
  └── 商品(Commodity · 售卖配置)
        └── 规格族(SPU · Standard Product Unit · 配置族)
              └── SKU(Stock Keeping Unit · 最终售卖单元)
                    × 计量项(meter · 横切计费字段)
                    × 算力中心系数
                    × 计费模式系数
                    = 客户账单

各层定义(以云容器实例为例)

定义例子
产品业务品类目录云容器实例 / VKS / 带宽 / EIP / LLM / OSS / NAS / EBS
商品按计费形态 / 资源类型切的售卖单元云容器实例-GPU / 云容器实例-CPU
规格族同商品内的硬件 / 配置变种NVIDIA-H800A-NV-80G / NVIDIA-L40S-PCIE-48G
SKU最终带价格的具体配置gpu:1 cpu:2 mem:4 disk:50
计量项(横切)所有 SKU 共享的"按啥算钱"字段cci.gpu.hours / cci.cpu.hours / cci.memory.gb.hours

直观记法

  • 产品 = 卖什么类型(业务目录视角)
  • 商品 = 怎么切售卖(销售视角,按计费形态 + 资源类型切)
  • 规格族 = 同商品下的硬件变种(工程视角,卡型 / 档位族)
  • SKU = 最终具体配置(财务视角,挂价格的最小单元)
  • 计量项 = 计费的最小度量字段(数据视角,量纲)
⚠️ 容易讲反:曾讲成「产品 → 规格族 → 商品 → SKU」是错的。规格族是商品内部的硬件变种,商品才是产品的直接子层。

价格计算链路

客户实际用量(如 100 GPU·h + 200 CPU·h)
   × 计量项基准单价(gpu.hours = ¥4.40 / cpu.hours = ¥0.12)
   × 算力中心系数(杭州 ×1.0 / 香港 ×1.42)
   × 计费模式系数(按量 ×1.0 / 包年 ×0.85)
   = 账单金额

详细规约:ui/doc/billingHierarchy.md

2026-05-20 · 拟定

已下架行不整体置灰

表格里 status: 'offline' 的行不要 opacity-60 整行透明、不要单元格文字灰。状态信号交给状态 Badge + 副信息(下架日期),整行置灰会让"启用 / 下架详情"等操作链接看起来不可点。

禁止做的

  • rowClassName={(r) => r.status === 'offline' ? 'opacity-60' : ''} — 整行透明
  • <span className={r.status === 'offline' ? styles.textOffline : ''}> — 单元格灰
  • 价格 / 数字列根据 status 切换灰色

正确做法

  • 状态列:<Badge status="default" text="已下架" /> 灰圆点 + 文字
  • 状态列下方副信息:小灰字下架日期(如 "2026-02-15")
  • 其他列:保持默认色,不区分 status
  • 操作列:Typography.Link 保持默认蓝色(运营仍可点"下架详情 / 启用")
类比:邮箱"已读"邮件标记成灰色信号,但邮件本身仍能点开。我们这里同理 —— 已下架的 SKU / 计量项仍能"查看下架详情 / 重新启用"。

详细规约:memory feedback_offline_row_no_dim.md

2026-05-13 拟定 · 2026-05-20 补

详情页 4 Tab 规范

所有详情页统一形态:PageContainer + ProCard「基本信息」+ ProCard「关联资源 Tab」。对齐 Figma node 27615:41659。

基本信息卡(顶部)

  • 88×88 大图标(产品 icon / AIDC icon)
  • 右标题:名称 18px / 500 + 状态 Badge dot
  • Descriptions column={3} colon size="small" — 必含新建/编辑表单的所有用户编辑字段(标题视觉重复不是删字段的理由)
  • 备注 / 描述独立一行:<span 灰 label:><span 默认色 value>,不加灰底块

关联资源 Tab(下方)

  • 用 AntD Tabs + HTML table(不用 antd Table,性能更轻)
  • tab 数 ≤ 4 个:基础 / 计费模型 / 产品 / 商品 之类
  • 每个 tab 数据 < 10 条不分页(按 表格分页规约

告警走 alertProps(不挂 body)

详情页告警(如「底层计量项已下架影响计费」)走 PageContainer.alertProps 放标题栏区域,不散装到 body 顶部,避免抢主内容视觉焦点。

header.extra 操作按钮

  • 主操作右:编辑(primary + RiEditLine icon)
  • 次操作左:商品 selector / 查看商品 / 导出 等
  • onBack:history.back()

详细规约:ui/doc/detailPageStandard.md + ui/doc/detailPageBasicInfo.md

运营中心规约 · 强约束

Drawer 表单单列垂直

运营中心所有 Drawer 表单 Form.Item 一行一个字段,禁止用 Row + Col 做两列布局。Form.List 行内字段(如 SKU 表格、阶梯表格)例外

禁止做的:
<Row gutter={12}><Col span={12}><Form.Item ... /></Col><Col span={12}><Form.Item ... /></Col></Row>

正确做法

<Form layout="vertical">
  <Form.Item name="code" label="编码" rules={[...]}><Input /></Form.Item>
  <Form.Item name="name" label="名称" rules={[...]}><Input /></Form.Item>
  <Form.Item name="line" label="产品线"><Select ... /></Form.Item>
</Form>

为什么

  • Drawer 一般 800-960 宽,两列字段挤变形(label 截断 / placeholder 没空间)
  • 单列上下扫读符合表单填写心理流
  • 跨 Drawer 统一形态,用户认知模型稳定

原型 2 列 grid 怎么办

原型 HTML 经常用 grid-cols-2 写两列字段,不要按原型布局照抄,强约束改单列。原型是 PM 视角的"字段都列了"草图,不是布局规约。

详细规约:memory feedback_drawer_form_vertical.md

协作规约 · 强约束

不擅自改 PM 产品文案 + 业务模型

UI Developer 的边界是「样式 + 布局 + 交互」,不包括字面文字 + 不包括"加几个状态枚举"。即便发现规约不一致也要先问用户拍板。

不许擅自改的

具体内容
文案类defaultMessage 内容 / Modal title / 字段 label / placeholder / extra / 按钮文字("停售"/"下架"/"保存"/"发布" 等)
业务模型类状态枚举数量 / 状态枚举 key 值 / 字段必填校验 / 工作流步骤数 / 数据 schema 字段

允许动的(UI 规约层)

  • 视觉组件选择(如 Tag → Badge 切换形态,字面值不变)
  • 颜色映射 / 主题 token
  • 布局 / 间距 / 字号
  • 交互形态(卡片 vs 表格 / Drawer vs 独立页面)

发现页面间文案不一致怎么办

  1. 不要"统一"
  2. 列差异给用户看("ProductList 用 X / CommodityList 用 Y")
  3. 问用户:A 保留各自 / B 统一到 X / C 统一到 Y
  4. 等用户拍板再改
典型踩坑
❌ 把原型 HTML 里英文 Code 当成 PM 文案搬到表头 → 应中文化「编码」
❌ MeterList 把「保存」改成「发布」(凭语义合理性自换)
❌ ProductList 自加 4 态 internal/beta(PM 实际只 2 态)
❌ 详情页 Descriptions 漏字段(觉得跟标题重复就删 — 不行,字段是表单值展示,标题是视觉锚点)

详细规约:memory feedback_no_product_copy_edit.md

工程规约

列表 / 详情 mock 必须同步

同一对象在列表页和详情页的 mock 数据(名称 / 编码 / 状态 / 字段)必须保持一致,改一边要同步另一边。

典型 List ↔ Detail 对

  • ProductList ↔ ProductDetail
  • CommodityList ↔ CommoditySku
  • AidcList ↔ AidcDetail
  • BillingMode ↔ BillingModeDetail(如有)
  • BundleList ↔ BundleDetail(如有)

改 mock 的 checklist

1. 改名称:列表 mockData + 详情 mock 库 + Select 选项里产品名(详情头部 selector)
2. 改状态:列表 status 字段 + 详情 status 字段 + 4 态颜色映射
3. 改字段值(goodsSku 数 / regions 数):列表字段 + 详情 Descriptions 同字段 + 关联资源 Tab 条数要对得上

反例

ProductList: name: '云容器实例(固定规格)'
ProductDetail: name: '云容器实例'
→ 用户点进详情看到名字不一样,怀疑系统错误

详细规约:memory feedback_list_detail_mock_sync.md

规约

编码字段统一展示

编码字段(code / spuCode / meterCode 等)跨场景统一形态。禁止 <Typography.Text code> 带边框等宽

场景形态原因
列表表格编码列(有详情页)<Typography.Link onClick=> history.push(detail)> 蓝色点击下钻语义
列表表格编码列(无详情页)<Typography.Text> 默认黑色普通信息
详情页 Descriptions 编码值纯字符串,不加任何样式跟其他字段视觉重量一致
卡片副信息编码(产品名后)灰色小字 <span className="cardCode">次要标识

禁止

  • <Typography.Text code>VKS</Typography.Text>(自带灰底框 + 等宽字体 → 抢视觉)
  • 自加 mono 字体 className 让编码"看起来更技术"(除非原型明确要求 font-mono,且只用于专业开发场景如计量项详情)

详细规约:memory feedback_code_field_display.md

原型解读规约 · 强约束

原型规范三合一

Alaya 原型 HTML(/Users/dang/Desktop/计费-v1.7/*.html)是 PM 规约源头。3 条解读规则:

① 可点击都要落地

原型里所有 onclick 标记的元素(蓝色 link / 按钮 / 卡片 / Tag)都要有真实交互,不能做死按钮 / 死链接。点击 → 跳转 / 打开 Drawer / 弹 Modal / showToast 都行,但不能没反应。

② dev-spec / 字段规则块 不上 UI

原型里 <div class="dev-spec"> 灰底块 + 「📐 字段规则 / 正则 / 长度 / 锁定」这类是开发文档给前端看的,不做到 UI 上。校验落到 rules,UI 保留 placeholder + 关键 LockTag 就够了。

③ 原型既是布局也是内容规约

文字 / 数值 / 占位顺序全是规约。动手前先复制原型对应段到对话逐字段核对;不写 fallback 写 catalog(产品名 / 状态名 / 字段名都按原型来)。

3 条规则的边界

- 「可点击落地」解决:原型给了交互入口,前端不能跳过
- 「dev-spec 不上 UI」解决:开发草稿别误读成 UI 规约
- 「布局即内容规约」解决:原型字面值就是 PM 拍板的文案,不要按"语义合理性"自换
三条一起用,少犯 90% 的原型误读

英文字段名是开发草稿

原型 HTML 里 <th>Code</th> / <th>Status</th> 这种英文是开发草稿记号,不是 PM 拍板的产品文案。运营控制台所有表头 / Tab / 按钮 / 字段 label 都要中文化。

详细规约:memory feedback_prototype_clickables.md + feedback_prototype_dev_spec.md + feedback_prototype_content_spec.md

规约 · 按选项数选组件

Drawer 单选 / 多选组件选择

Drawer 表单单选 / 多选不能"看着像 Select 就用 Select",要按选项数和视觉权重选组件。

类型选项数组件
单选2-5 项<Radio.Group className="alaya-radio-block"> + flex 全宽分块
单选6+ 项<Select /> 下拉
多选少量 + 平展<AlayaCheckboxCards /> 卡片网格
多选大量 / 需搜索<Select mode="multiple" /> 下拉

为什么

  • 2-5 项 Radio 全展开扫读最快,省一次点击
  • 6+ 项 Radio 会撑爆 Drawer 横向空间
  • 多选需要"看到全部已选" → 卡片网格 / Select tags 两种

详细规约:memory feedback_drawer_singleselect.md

列表性质规约 · 强约束

运营后台列表 — 卡片 vs 表格 二选一

运营后台所有列表页按数据性质二选一不做视图切换器

数据性质视图例子
业务对象类纯卡片网格 12 / 页带翻页器CommodityList 平台商品 / LLM 推理服务(产品形象强,价格 / 计费模式可视化好)
流水 / 数据类纯表格ProductList / MeterList / BundleList / AidcList / BillingMode(数字 + 文本扫读为主)

判断口诀

"是否需要让用户对这个对象产生视觉记忆 / 品牌印象?"
- 是 → 卡片(图标 + 配色 + 重点价格醒目展示)
- 否 → 表格(多列扫读 + 排序 + 筛选效率优先)

详细规约:memory feedback_ops_table_first.md + ui/doc/listViewMode.md

协作规约 · 强约束

视图切换需事先审核

任何页面加 Segmented / Tabs 切换不同呈现前必须先问用户,不限于运营后台。

需要审核的场景

  • 加「卡片 / 表格」切换器(Segmented)
  • 加「按状态 / 按类型」切换器
  • 加 Tabs 让一个页面承担多种数据呈现
  • 详情页加 4 Tab 以外的非标准切换

不需要审核的场景

  • PageContainer.tabList(标题栏带 Tab)的状态筛选 — 已是全局规约
  • 详情页的 4 Tab 关联资源 — 已是详情页规范
  • Drawer 内 view 切换(如 SPU 子视图)— 已写过 ui/doc/drawerViewSwitch.md

为什么

视图切换会让用户认知模型不稳定,是重设计决策,应该 PM 拍板而不是开发凭"我觉得这样更好"加。

详细规约:memory feedback_view_toggle_approval.md

工程规约

PageContainer 标题栏带 Tab 是固定形态

用 ProLayout 原生 tabList + tabActiveKey + onTabChange 实现标题栏 Tab,已有 4 条 :has CSS 覆盖把布局对齐到总高 90px。

标准实现

<PageContainer
  backIcon={false}
  tabList={[
    { tab: '全部', key: 'all' },
    { tab: '在售', key: 'active' },
    { tab: '已下架', key: 'offline' },
  ]}
  tabActiveKey={statusTab}
  onTabChange={(key) => setStatusTab(key)}
>
  <AlayaProTable ... />
</PageContainer>

不要这样做

  • ❌ 在 body 里自加 <Tabs> 模拟标题栏 Tab — 会破坏 PageContainer 总高布局
  • ❌ 用 Segmented 替代 tabList — Segmented 是 mini 切换器,不是页面级筛选

详细规约:memory feedback_pagecontainer_tabs.md + ui/doc/pageContainerWithTabs.md

CSS 规约 · 老踩坑

inline icon 跟文字水平居中

SVG icon + 文字共存时必须 inline-flex + alignItems: 'center' + lineHeight: 0 三件套,否则视觉总会差几像素。

标准写法

<span style={{
  display: 'inline-flex',
  alignItems: 'center',
  gap: 4,
  lineHeight: 0,
}}>
  <RiCheckLine size={14} />
  <span style={{ lineHeight: 1.5 }}>运营中</span>
</span>

为什么

  • SVG 默认 vertical-align: baseline,跟文字基线对齐而非中线
  • line-height 影响 SVG 容器高度,造成"图标被文字行高拉低"
  • 三件套:父级 inline-flex + alignItems center 强制中线对齐 + 父级 lineHeight 0 消除文字行高影响 + 子级 lineHeight 恢复

老踩坑

每次都"看上去差一点点"调半天,最后回归三件套。已经被坑过 N 次,记到 memory 了。

详细规约:memory feedback_inline_icon_alignment.md

CSS 规约

切图比例不准擅自 stretch

切图(设计师给的 4x PNG 资源)background-size: 100% 100%。用 background-size: auto 100%(或 contain),或调容器尺寸 = 切图原比例。

为什么

  • 切图里有端头装饰 / 曲率 / 高光等按原比例画的视觉细节
  • 100% 100% 强制拉伸 → 圆角变椭圆 / 曲线变形 / 高光位置漂移
  • 设计师切图时已经按真实容器尺寸做了像素级调整,前端只需配合不要破坏

正确做法

/* 容器尺寸保持切图原比例 */
.banner {
  width: 1280px;     /* 切图原始宽 ÷ scale */
  height: 320px;     /* 切图原始高 ÷ scale */
  background-image: url(/banner.png);
  background-size: auto 100%;   /* 高占满,宽按比例 */
  background-repeat: no-repeat;
}

老踩坑

长江小学安防大屏 / 大巴山中药材驾驶舱多个 demo 项目都踩过这个坑 — 切图拉伸后设计师不认。

详细规约:memory feedback_preserve_cut_aspect.md

交互规约

Drawer view 内部切换(不叠层)

Drawer 内出现"子流程"(如新建商品流程里要新建规格族)时,用同 Drawer 内 view 切换,不要叠层第二个 Drawer。

实现思路

  • 父 Drawer 一直 open,加 spuViewOpen 等 state 控制内部 view
  • 父 Drawer 的 title / footer / body / closeIcon 都按 view state 条件渲染
  • 子 view 的 closeIcon 改成 <RiArrowLeftLine /> 返回箭头
  • onClose:子 view 状态 → 返回父 view,不是关 Drawer

为什么不叠层

  • 叠层 Drawer 视觉层级混乱(哪个是当前?)
  • 背景遮罩重叠像素加深问题
  • 用户关闭子 Drawer 容易误关父 Drawer

已实现案例

CommodityList 新建商品 Drawer → 「+ 新建规格族」子 view(task #103)

详细规约:ui/doc/drawerViewSwitch.md

工程规约 · demo 模式

Snapshot demo 模式

ops-center-web 的 demo 模式(部署到 alaya-ops-snapshot.pages.dev)通过 window.__SNAPSHOT__ 全局标记激活。

关键能力

  • Mock 拦截:window.fetch 重写,所有 /api/ 请求返回 mockData 不真打后端
  • access 全开:access Proxy 任何 key 返回 true,所有菜单可见
  • 构建信息注入window.__SNAPSHOT_BUILD__ = { hash, time } 供 UI 版本 tag 显示
  • UI 版本标识:右上角橙色 tag「UI 版本」+ tooltip 显示 commit hash + build 时间
  • 原型对比 FAB:右下角浮动按钮,点击叠层显示原型 HTML iframe,URL hash 联动子页

代码标记

所有 demo 模式相关代码用 @ui-change-snapshot 注释标记,方便合代码时整段删除恢复生产模式。

部署流程

# Node 20 切换
PATH="/Users/dang/.local/share/fnm/node-versions/v20.20.2/installation/bin:$PATH"

# 构建(输出到 dist/)
npm run build:snapshot

# 重命名
rm -rf dist-snapshot && mv dist dist-snapshot

# 部署到 CF Pages
npx wrangler pages deploy dist-snapshot \
  --project-name alaya-ops-snapshot \
  --branch main \
  --commit-dirty=true \
  --commit-message "<英文消息>"
注意:commit-message 必须用英文(CF Pages 中文报 8000111 Invalid commit message);Node 必须 20(v25 缺 http_parser)。

详细规约:ui/doc/snapshotDemoMode.md + ui/doc/prototypeCompareFab.md

Changelog · design-guo-minor 分支

改动记录

本节按日期顺序记录 design-guo-minor 分支所有改动,**给前端同事 / PM 接手时快速对照"改了哪、为什么改、有什么风险"**。 每条改动包含:背景 / 涉及文件 / 风险点。

2026-05-01 ~ 02 · 接入 mfCloud 云端共享主题

前端架构师告知:Alaya 系产品(bp + ops + 其他)的主题切换标准做法是接入云端共享 ThemeProvider,CDN 加载,多产品同源。本地 config/theme.ts 改为 fallback。

文件改动
src/app.tsx引入 ThemeProvider, { themeConfig } from 'mfCloud/ThemeProvider'memo.theme = themeConfig(远程加载);rootContainer<ThemeProvider> 包裹;本地保留 colorBgMenuItemSelected: blue1 一项 override 兜底
config/theme.ts改为构建期 fallback:gray 色板 / 文本色 / 边框色 / 填充色 / 背景色全部对齐公司设计规范;主色 #FF921C 橙 → #4362FF 蓝;间距 spacing 统一走 AntD 标准 token,不自造 spacing*
config/env.jsenvName: 'dev02''unite'(dev02 环境无法连接,统一走 unite 测试环境 https://catalog.unite.zetyun.cn
风险点: mfCloud/ThemeProvider 是远程模块,TS 类型缺失,需 @ts-ignore。云端 mfCloud 暂未配置 colorBgMenuItemSelected,本地 override 兜底;云端补齐后删。

2026-05-02 · 顶部导航栏全部走 AntD token

src/app.tsx 顶部导航栏原有大量硬编码(logo 高度 48 / 标题色 #333 / marginLeft 10 / fontWeight 450 等非 AntD scale),全部 token 化。

字段改前改后
logo 高度48 硬编码var(--alaya-control-height-lg) = 40
标题字号字面量var(--alaya-font-size-lg) = 16
标题颜色#333var(--alaya-color-text-heading)
logo 与标题间距10px(非 AntD scale)var(--alaya-margin-xs) = 8
标题 font-weight450(非标准)500
logo 容器对齐默认display: flex; alignItems: center 让图片与文字水平居中

2026-05-02 ~ 03 · 侧栏图标全局替换(codesign iconfont → 恐龙图标库)

原侧栏菜单用 codesign 上传的 iconfont(icon-yy-xxx)。前端要求改用恐龙图标库(duotone 风格),通过本地 SVG + DinoIcons 组件承载。后期再统一上传 codesign 生成 iconfont 替换回。

文件改动
新建 src/components/DinoIcons/index.tsx组件本体:根据 name prop 渲染对应 SVG(用 <use href="#i-xxx"/> 引用 sprite)
新建 src/components/DinoIcons/index.global.less默认色 colorTextTertiary(gray6);选中/hover 跟随菜单文字色;下拉箭头色 colorTextQuaternary(gray5)
新建 src/assets/icons/ 30+ SVG 切图恐龙库 duotone 风格本地 SVG(iconWallet / iconBox / iconCoins / iconBagShopping 等),全部用 currentColor,受组件层主题色控制
替换 src/assets/icons/iconNotification.svg用恐龙库 communication/duotone/envelope-notification.svg 替换原图,并把 #fff 全替换为 currentColor
config/route/platformRoutes/cost.ts费用中心 8 个菜单 icon 字段全替(账户/收支/订单/合同/账单/算力包/导出记录/会话监控)
config/route/platformRoutes/misc.ts综合中心 8 个菜单 icon 字段全替(客户/商品/营销/权限/消息/系统设置 + 新增商品/计价)
src/app.tsx导入 dinoIconMapmenuDataRender 中递归把 dino-* 字符串替换为 <DinoIcon /> 组件
后期还原计划:src/assets/icons/ 下 SVG 上传 CoDesign 生成新 iconfont,届时改回 icon-yy-xxx 格式并删除 DinoIcons 组件 + 本地 SVG。

2026-05-03 ~ 05 · 商品 / 计价模块原型页面(4 步 Steps + 路由改造)

综合中心新增「商品」「计价」两个一级菜单,需要原型页面给业务方走查。商品管理是 4 步向导(15+ 字段 + 条件联动),Drawer 承载不下,改为独立页面 + Steps。

新增路由

路由组件说明
商品(icon: dino-bag-shopping)
/ops/misc/commodity/productcommodity/ProductList产品管理列表
/ops/misc/commodity/listcommodity/CommodityList商品管理(卡片+表格双视图)
/ops/misc/commodity/list/createcommodity/CommodityCreate新建(4 步 Steps 独立页)
/ops/misc/commodity/list/:action/:idcommodity/CommodityCreate编辑商品
/ops/misc/commodity/bundlecommodity/BundleList套餐包管理
计价(icon: dino-coins)
/ops/misc/pricing/aidcpricing/AidcListAIDC 定价列表
/ops/misc/pricing/meterpricing/MeterList计量项管理
/ops/misc/pricing/billingpricing/BillingMode计费模式

CRUD 模式

  • ProductList · Drawer(600px) 新建/编辑 + Modal 下线
  • CommodityList · 卡片/表格双视图 + 跳转独立页新建/编辑 + Modal 停售;含 Tier 2 资源告警 banner(详见下文 Alert 应用规约)
  • CommodityCreate · 4 步 Steps 独立页面(ProForm + ProCard 水平布局)
  • BundleList · Drawer(640px) 新建/编辑 + Modal 下线
  • AidcList · Drawer(520px) 新建/编辑
  • MeterList · Drawer(640px) 编辑 + 定价 Drawer + Modal 下线
  • BillingMode · Drawer(560px) 新建/编辑 + Modal 绑定商品
数据状态: 所有列表页全部 mock 数据,未对接真实 API。前端接入时需 ① 替换 mock 数据为真实 API 请求;② 对接实际增删改查接口;③ 确认字段名与后端接口一致。

2026-05-06 ~ 08 · PageContainer 多次迭代(核心改动)

PageContainer 是全局列表页容器,原有 3 处内联常量 + 硬编码像素,且 mfCloud 主题接入后外层白底块没了背景色(标题与下方 layout 灰底糊一起),需要重构 + 修复。

最终状态(建议前端只看这部分)

改动点具体内容
新增文件src/components/pageContainer/index.less 承载非内联样式
Alert 渲染位置renderAlert() 改到 header.footer(与标题同处白底块内);与标题间距 16px 走 token
Alert 样式去掉 marginBottom(白底块自身 padding 已撑开下方)+ 去 border(白底块本身已与下方灰底有视觉边界)
Alert 其他保持 master:图标 14/22/mr4 内联,padding 8 12 内联,type="info" 硬编码(业务侧不暴露 type 写口)
主标题字号var(--alaya-font-size-lg) = 16,行高 24,weight 500(覆盖 AntD PageHeader 默认 fontSizeHeading4=24 偏大)
外层白底兜底:global(.ant-pro-page-container-warp-page-header) 强制 background: var(--alaya-color-bg-container) + margin-block-end: var(--alaya-margin) (16)
返回按钮var(--alaya-control-height-sm) = 24×24;font-size 12 字面量保留(AntD 没暴露 fontSizeSM=12 token)

兼容性(9 个使用 alertProps 的页面)

BillMonth / HandmadeBill / BillSettlement / BillList / Export / overdraft / BundleList / AidcList / MeterList —— 业务渲染逻辑零变化,只是渲染位置从 children 区移入白底块 footer。

  • HandmadeBill 内含 <DownloadBlobBtn> —— 测试通过,按钮在白底块内可正常点击下载
  • BillMonth 多段条件渲染 —— 实际只渲染一行短句,无溢出风险

2026-05-07 ~ 08 · 设计规范页 design-system.html(本页)

前端 + 设计 + PM 缺一份共同对齐的"组件视觉规范 + Token 字典"。把所有 token、颜色、间距、组件状态、视觉规约整合成一个独立 HTML 页面,部署 CF Pages 给团队访问。

条目位置 / 链接
源码(单文件)/Users/dang/ops-center-web/design-system.html
部署地址https://alaya-design-spec.pages.dev/
CF Pages 项目alaya-design-spec
部署命令npx wrangler pages deploy .deploy-spec --project-name alaya-design-spec --branch main --commit-dirty=true(先把 design-system.html 复制到 .deploy-spec/index.html
Inspect 模式(Figma-like):
· 右侧浮层「Inspect 模式 ON/OFF」按钮
· 开启后 hover 任意 [data-spec] 元素 → 显示 spec tooltip(黑底)
· 点击锚定(蓝色 outline)→ 按住 Alt + hover 另一个元素 → 画虚线测距(支持相邻和包含两种关系)
· 无锚定时 Alt + hover → 跟随光标显示 width × height

2026-05-08 · Alert 应用规约(产品 × 前端共同对齐)

Alert 在项目里有多种语义被混用:① PM 写文案时不区分"模块描述"和"alert 提示";② 同一个文案位(PageContainer)被两个 API 混用;③ 商品管理的业务告警一开始我用了独立 <Alert type="warning">,跟其他页面不统一。统一收口:所有页面级 Alert 都走 PageContainer.alertProps,type 由业务传值(info / warning),位置统一进白底块。

详细规约请见上方《Alert 应用规约》整章。本节只列产出物:
· 新增 section "Alert 应用规约"(核心判定 + type/颜色/场景 + 全项目盘点 + 文案标点 + FAQ)
· 盘点 15 处 info 现有文案,分 A 留 / B 待 PM 复核 / C 删 三类,含菜单位置
· 统一规则:所有 alert 走 alertProps,type 可传 info | warning,组件内按 type 自动选图标和颜色
· 商品管理 warning 告警迁移:从独立 <Alert banner>alertProps={{ type: 'warning', description: ... }},渲染到白底块内,支持右侧 action 链接("查看影响明细")
· 文案标点统一不带句号,含 4 条 WHY

本节配套产出

文档受众 / 用途
本设计规范页(design-system.html)全团队(前端 / 设计 / PM)—— 视觉规范 + 组件状态 + Token 字典
CHANGELOG-design-guo-minor.md(项目根)前端 —— 开发视角详细记录,git 提交时会跟代码一起留底
Obsidian 笔记(个人)个人 —— 跨产品对比 / 决策回溯 / 跟其他 Alaya 产品的横向参考

2026-05-09 · 商品 / SKU 详情页 + 全局规范扩充

商品管理列表 / LLM 推理服务 / SKU 详情页迭代过程中沉淀的全局规则,全部进 src/global.ui.css + ui/doc/ 留底。

本轮新增 / 调整:
· PageContainer 主标题字号 16 → 18px(行高 24 → 28px),主副标题 flex 居中 — 见上方《内容区标题 + Alert》节
· 表格列规范新增独立章节 — 操作列宽度 6 档位 / 数值列右对齐 / D-DIN 数字字体 / 删除 friction 规则
· D-DIN 字体启用 — global.css 早就 @font-face 注册了但项目没用,本次首次落地到商品卡指标 / 价格 / SKU 列表
· 操作列档位"最小可读 vs 视觉舒适"区分 — 之前只给最小档位被反映"局促",文档补充舒适宽度 ≈ 档位 × 1.25

本轮配套产出

文档主题
ui/doc/pageHeaderTitleSize.mdPageContainer 主标题 18px + flex 居中
ui/doc/tableColumnSpec.md表格操作列宽度档位 + 价格数值列右对齐
ui/doc/customSegmentedTabs.mdalaya-tabs 自定义选项卡(独立卡片样式 Segmented)
ui/doc/noEmojiIcons.md禁 emoji 作为图标(用 RemixIcon / DinoIcons)

2026-05-09 · 表单选择器规范(4 种组件取舍)

迭代过程中反复在 Segmented / Radio.Group(button) / 自定义 alaya-tabs / Select 之间切换,沉淀决策树。

规则:
· 2-4 选 + 渐进/视图切换 → AntD Segmented 默认
· 2-4 选 + 含图标 + 重要决策 → 自定义 alaya-tabs(独立卡片)
· 2-4 选 + 短词无图标 → AntD Radio.Group(button)
· ≥ 5 选Select(≥ 8 加 showSearch
· 选项卡只要标题 — 不带描述,详细解释走 Form.Item.tooltip

详见上方《表单选择器组件》整章。

2026-05-11 · ProTable 表格操作区 Toolbar 右侧 padding 对齐

修正 .ant-pro-table-list-toolbar-container 与下方表格单元格右沿错位 16px 的视觉 bug。

规则:表格操作区 toolbar 的 padding-inline 必须与表格单元格 cellPaddingInline 对齐,不能再叠加上层容器的 padding
· 层级累积.ant-pro-card-body(24) + .ant-pro-table-list-toolbar-container(原 24) = 右侧 48px
· 单元格.ant-pro-card-body(24) + cellPaddingInline(8) = 右侧 32px
· 两者相差 16px,导致 toolbar 右侧按钮(齿轮/刷新)凭空浮在表格列右沿之外
· 修法:把 toolbar 的 padding-inline 改成跟 cellPaddingInline 一样(8px),让 toolbar 内容右沿与表格内容右沿对齐

修改文件:src/global.ui.css(!important 规则 24 → 8)+ src/components/AlayaProTable/index.global.less(注释同步)+ ui/doc/toolbarPaddingFix.md(文档)

通用启示:覆盖 AntD ProTable 组件样式时,必须先确认有几层嵌套容器都加了 padding——任何"右侧多出一截"的视觉感往往来自两层 padding 叠加,不是 AntD 默认偏大。

2026-05-11 · 5 个 Drawer 统一规范重做

把 ProductList / BundleList / AidcList / MeterList / BillingMode 5 个新建/编辑 Drawer 全部对齐 CommodityList 4 步 Steps 抽屉的规范。新增 2 个共享卡片组件,统一 4 态生命周期颜色映射。

· 宽度 800(原 520/560/600/640 一律收口)
· Footerfooter prop 取消左 / 主操作右(不再用 extra
· 表单单列垂直(删 Row+Col 两列,Form.List 行内字段例外)
· 新建共享组件src/components/AlayaCheckboxCards/(多选卡)+ src/components/AlayaRadioStackCards/(3-4 选 1 卡)
· BundleList 新增 RESERVED 包月预留(DEDUCTION / DCU_PACK / RESERVED 三类)
· 4 态颜色统一:内测灰 / 公测橙 / 运营绿 / 已下架灰(Badge status 用 default/warning/success,禁用 processing 蓝)
· 全部删除:📐 字段规则灰底块 / 🔒 LockTag「发布后锁定」/ DCU 概念 Alert / RESERVED 警告 Alert / 所有 emoji 图标

详见 Drawer 统一规范 节、ui/doc/drawerStandard.mdCHANGELOG-design-guo-minor.md「2026-05-11 · 5 个 Drawer 统一规范重做」

风险点: BundleList 保留的 LockOutlined 是 RESERVED 类型功能图标 + Form.List 内 row-level 锁定状态指示,不是「发布后锁定」备注 LockTag,接手时不要误删。

2026-05-12 · 卡片视图规约定型 + 状态/分类 Tag 统一 + Loading 改造

围绕"业务对象类列表卡片"的视觉规约定型,覆盖状态显示、分类显示、操作位置、卡片样式、首屏 loading 五大维度。

· 生命周期状态用 Tag(不用 Badge):「内测/公测/运营/已下架」改用 Tag color="success/warning/default",禁 processing 蓝
· 分类用文字描述(不用 Tag color):产品线 / 类型 等分类信息改 plain 灰字描述,跟状态 Tag 视觉拉开
· 卡片操作位置规约:危险操作左下(danger 红 + 物理分离防误触),主操作右下(Z 型扫读落点),操作行 margin-top: auto 锁底跨卡水平对齐
· 全局 Card box-shadow 压平:默认 none,hoverable hover 时 0 4px 16px rgba(15,23,42,.08) 干净微投影
· 全局 Button 内图标对齐.ant-btn .ant-btn-icon 强制 inline-flex 居中,防 RemixIcon baseline 偏下
· 首屏 Loading 改造:旧 AntD 4 方块橙 → 云朵 logo 双层描边循环 + 内联骨架屏接管(详见 Loading 规范
· 编码字段统一展示:禁用 <Typography.Text code> 带边框等宽样式,卡片用灰色小字 span,表格用 Typography.Link
· 不擅自改 PM 文案 + 业务模型:CommodityList 2 态 active/offline = 运营/停售 PM 原文案保留,不擅自升级 4 态

详见 卡片列表规约 + 首屏 Loading 规范 节,ui/doc/cardListStandard.md + ui/doc/loadingSpinner.mdCHANGELOG-design-guo-minor.md「2026-05-12」

2026-05-12 收口 · 列表视图模式最终规约 + 下架按钮颜色统一 + 编码 Link 规约

当天晚些时候用户三次拍板后的最终规约。

· 列表视图模式收口:撤回 ProductList + BundleList 的卡片视图切换器,全运营中心只有 CommodityList 走纯卡片,其他列表(ProductList / BundleList / MeterList / AidcList / BillingMode / 账单 / 月结 / 客户管理 等)一律纯表格。详见 列表展现模式
· 下架按钮颜色统一红色:表格行 <Typography.Link type="warning">(橘色) → <Typography.Link type="danger">(红色),跟卡片 Button danger 视觉一致。下架是破坏性操作(改变发布状态),用 danger 红 跟"删除"同语义,warning 橘是"待处理/风险提示"不适合。
· 主题色 = 可下钻规约:表格 code 列只在有详情页时用 <Typography.Link> 蓝色(+ onClick 跳详情),无详情页必须用 <Typography.Text> 普通黑字。死链接(蓝色但无跳转)跟"主题色 = 可下钻"潜规约冲突,用户会困惑。

详见 表格列规范 · 编码字段展示 节。

2026-05-13 · BillingMode / MeterList 对齐原型 + 6 模块 toast 文案统一 + 价格颜色规约

围绕计价模块两张表单页 + 全局 toast 文案 + 数值色规约的三大整改。

· BillingMode Drawer 完整重做:宽 800→960,4 section(基础信息 → 计费大类 → 模式系数 → 计价参数),字段顺序对齐原型 modeNew ① 行 2065(编码→名称→启用状态→描述);启用状态项数差异化(新建 2 项 / 编辑 3 项)
· 计费大类 AlayaRadioStackCards:5 卡片 3+2 grid,emoji 全删用 RemixIcon(PAYG=Timer / TIERED=BarChart2 / SUBSCRIPTION=Calendar / SPOT=Flashlight / QUEUE=OrderPlay),description 3 行(title + 主描述 + 二级描述)
· 模式系数特征盒子:灰底盒子「全局系数 = [InputNumber] × 基准单价」+ 蓝 Alert 业务说明 + 演示行(vks.cpu.hours / cci.disk.gb.hours 实时演算)— 严格按原型 modeNew/modeEditPAYG ②
· PAYG 计价参数 4 字段:付款时机 / 计量粒度 / 计费粒度 / 金额取整,全 2 项 Radio,选项数 + 文案严格按原型 modeNew 行 2379-2382(不擅自增加项)
· MeterList Drawer 5 section:基础信息单列 + 「高级 · LLM 父子关系」Collapse 仅 LLM 出现 + 基准定价(紫 Alert)+ DCU 速率(黄 Alert)+ 算力中心系数(蓝 Alert,表 6 列含实际DCU/说明 + 杭州行紫底「基准」Tag)+ 生效时间(蓝 Alert 版本规则)
· MeterList 列表表格:删「产品」列;单价/DCU 与单位并排展示(inline-flex; align-items: baseline),不堆两行;列宽加宽
· 价格色规约--alaya-color-warning 橘是"告警"语义,价格不用。统一改 --alaya-color-text 默认黑 + font-weight: 600;已下架走 text-quaternary 灰。详见 价格 / 数值类显示规约
· 6 模块 toast 文案严格按原型 showToast 原文:禁用 operateSuccess 泛文案,下架类用 message.warning 不用 success。详见 Toast 文案规约
· 启用状态字段 label:用 pages.pricing.mode.status id 避免被全局 status 翻译表覆盖成「状态」
· 绑定 Modal 居中centered prop + 标题对齐原型「绑定适用商品」+ 外框 320px 高纵向滚动 + webkit-scrollbar 主题 token
· BillingMode KPI 卡 bordered={false}:跟内容区背景无缝过渡("内容区大背景不带边框"惯例)
· TOU 分时计价折叠删除:本期不做,恢复参考 git history 5/13 之前

详见 Toast 文案规约 + 价格 / 数值类显示规约ui/doc/toastWording.md + ui/doc/priceDisplayRule.mdCHANGELOG-design-guo-minor.md「2026-05-13」

Convention · Wording

Toast 文案规约

2026-05-13 由 PM 反馈定型:"6 模块新建/编辑/下架的 toast 全部用『操作成功』,原型已经写好了专属文案,为什么不照着用?"

硬规

· 禁用 intl.formatMessage({ id: 'operateSuccess', defaultMessage: '操作成功' }) 泛文案
· 必须查原型 HTML 里对应操作的 showToast(...) 字符串照抄(~/Desktop/v2/商品.html / 计价.html
· toast 类型:创建/编辑/启用 → message.success;下架/停售/状态变更 → message.warning(destructive 不是 success);删除/失败 → message.error

6 模块落地清单

模块新建编辑下架(warning)
ProductList产品已发布产品配置已保存产品已下架 · 切换列表「已下架」筛选可查看记录
CommodityCreate商品已创建 · 批量生成 ${n} 个 SKU商品配置已保存
BundleList套餐包已发布套餐包配置已保存套餐包已下架 · 已购余量到期清零
AidcList算力中心已发布算力中心配置已保存
MeterList计量项已发布已保存 · 价格变化生成新版本 · SKU 价格已重算计量项已下架 · 切换列表「已下架」筛选可查看记录
BillingMode新模式已创建并启用(内测)配置已保存
BillingMode 绑定 Modal已绑定到选定商品
详细规约:ui/doc/toastWording.md。同主旨规约:feedback_no_product_copy_edit.md
Color · Semantic

价格 / 数值类显示规约

2026-05-13 PM 反馈:"计量项列表的单价为什么用橘色?我们没这种用法。" 起源 MeterList 旧版用 var(--alaya-color-warning) 给单价上橘色 — warning 是"告警"语义,价格不是告警。

硬规

· 价格 / DCU / 系数 / 配额 等数值默认用 var(--alaya-color-text) 黑色 + font-weight: 600 bold 给视觉权重
· 已下架 / disabled 行var(--alaya-color-text-quaternary)
· 价格 + 单位并排inline-flex + align-items: baseline + gap: 4px),不堆两行。单位 12px text-quaternary
· 例外:演示行 / 公式结果可用 --alaya-color-primary 蓝高亮(如 BillingMode 系数演算);大屏 KPI 数字趋势色(success 绿 / danger 红)与计费单价场景脱钩

颜色语义对照

Token设计意图用在哪
--alaya-color-text默认黑价格、DCU、系数、数量、文字
--alaya-color-text-quaternary浅灰单位、说明、已下架、placeholder
--alaya-color-primary主蓝链接、操作、Tag processing、公式结果
--alaya-color-success绿状态 Tag「运营」、成功 Alert
--alaya-color-warning状态 Tag「公测」、告警 Alert ⚠️ 不要用在价格
--alaya-color-error危险操作、destructive Tag

代码模板

// MeterList 标杆 (.less)
.priceInline {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
}
.priceMain {
  font-weight: 600;
  color: var(--alaya-color-text);        /* ✅ 默认黑 */
}
.priceOffline {
  font-weight: 600;
  color: var(--alaya-color-text-quaternary); /* 已下架灰 */
}

// .tsx 渲染
<span className={styles.priceInline}>
  <span className={record.status === 'offline' ? styles.priceOffline : styles.priceMain}>
    {record.basePrice}
  </span>
  <span className={styles.priceUnit}>{record.basePriceUnit}</span>
</span>
详细规约:ui/doc/priceDisplayRule.md
Component · Specification

业务对象类列表卡片规约

所有"业务对象类"列表卡片(ProductList / CommodityList / BundleList 等)按本规约定型。涵盖容器 / 卡片头 / 状态显示 / 分类显示 / 操作位置 / 视图切换 6 个维度。

硬性规约

卡片容器
height: 100% + ant-card-body display: flex; flex-direction: column
box-shadow
默认 none(扁平)/ hover 0 4px 16px rgba(15,23,42,.08)(干净微投影)
网格布局
3 列 × 4 行 = 12/页Row gutter [16, 16] + Col span={8}
cardHead
左 icon 64x64 / 中 标题+code+分类描述 / 右 状态 Tag
编码 cardCode
灰色 12px span( <Typography.Text code>
分类 cardLine
文字描述 12px / text-secondary( 彩色 Tag)
状态显示
Tag color 4 态:内测灰 / 公测橙 / 运营绿 / 已下架灰
操作行 actionRow
margin-top: auto 锁底 / 危险左 / 主操作右 / type="link"

卡片操作位置规则(核心)

危险左 / 主操作右,物理分离防误触 + 视线落点对齐 Z 型扫读:

┌──────────────────────────────┐
│ 卡片信息...                   │
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─  │
│ [危险]   ··    [次] [主]      │
│  左               右          │
└──────────────────────────────┘
按钮规约:全部 <Button type="link" size="small">,危险操作加 danger不加图标(danger 红色已区分破坏性,加图标视觉重量超过主操作)。

状态 Tag vs 分类 Tag — 视觉重量必须拉开

维度状态(生命周期)分类(产品线/类型)
组件<Tag color="success"> 彩色背景<div className=styles.cardLine> 文字描述(推荐)/ 或 <Tag> plain 灰边框
视觉重量突出(彩色块),运营关心低调(灰字),辅助信息
颜色4 态规约:success 绿 / warning 橙 / default 灰无 color,灰字或灰边框
禁用processing 蓝(跟主题色冲突)hue color(blue/cyan/gold/purple)— 跟状态 Tag 同款视觉

代码模板

// cardHead:图标 + 标题块 + 状态 Tag
<div className={styles.cardHead}>
  <img src={iconMap[item.code]} width=64 height=64 />
  <div className={styles.cardTitleWrap}>
    <h3 className={styles.cardTitle}>
      {item.name}
      <span className={styles.cardCode}>{item.code}</span>
    </h3>
    {/* 分类用文字描述 */}
    <div className={styles.cardLine}>{item.line}</div>
  </div>
  {/* 状态用彩色 Tag */}
  <Tag color={statusTagColor[item.status]}>{statusLabelMap[item.status]}</Tag>
</div>

// actionRow:危险左 / 主操作右 / 锁底
<div className={styles.actionRow}>
  {item.status !== 'offline' && (
    <Button type="link" size="small" danger onClick={handleOffline}>下架</Button>
  )}
  <div className={styles.actionSpacer} />
  <Button type="link" size="small" onClick={handleEdit}>编辑</Button>
  <Button type="link" size="small" onClick={handleView}>查看商品</Button>
</div>
详细规约:ui/doc/cardListStandard.md。memory 沉淀: feedback_card_actions_position.md / feedback_card_action_lock_bottom.md / feedback_status_4state_colors.md / feedback_tag_plain_classification.md / feedback_code_field_display.md / feedback_no_product_copy_edit.md
Component · Static Asset

首屏 Loading 规范

首屏 loading 由三层视觉接力,HTML 静态层做,不依赖 React。文件位置:public/scripts/loading.js(首屏预加载脚本)+ src/components/loading-ui/(应用内 React 组件库)。

三层接力

Stage 1 · LogoSpinner
Alaya 云朵 logo 双层 stroke 描边循环(灰底 + 彩色叠加),4s 一轮,错开 delay 0/0.25/0.5/0.75s
Stage 2 · 内联骨架屏
PageContainer + 筛选 + 表格灰色 shimmer 占位,z-index 底层始终 opacity:1,logo (z=2) 上层覆盖
Stage 3 · 真实页面
React removeChild(#globalLoading) 瞬间暴露骨架屏 / 等浏览器 idle 后整体 fade out 300ms 移除

LogoSpinner 关键技术点

· 纯描边循环:用户拍板"渐变填充很蠢",去掉 fill 上色,只保留 stroke 描边的两层叠加(灰底 #CBD5E1 + 彩色 #FF8400/#4362FF)
· SVG pathLength="100" 属性:必须写在 path 元素上(不是 CSS),让 stroke-dasharray: 100 标准化所有 path 描边时长一致
· stroke-linecap: butt:端点截断(不圆头),起笔/终笔轨迹更明显
· 瞬间复位避免呼吸闪烁:keyframes 85%→86% 间 stroke-dashoffset: 0 → 100 瞬间跳变(1% 时长),CSS animation 不插值,视觉上是"消失重画"而非"渐隐"

内联骨架屏(关键设计决策)

React registerLoaded 函数(src/app.tsx)会物理 removeChild(#globalLoading) 删 logo DOM 节点,CSS opacity transition 无效。所以骨架屏必须从启动就在 DOM 底层 opacity:1,logo 上层覆盖 — React 删 logo 瞬间骨架屏自动暴露,零间隙。

反例:路由级骨架屏放 React 层(src/loading.tsx + Suspense fallback)— 会引入延迟:骨架屏组件本身是 React chunk 需要加载,反而拖慢首屏。
正例:骨架屏 HTML 内联到 public/scripts/loading.js 里,跟 logo 同 DOM,0 chunk 加载延迟。

应用内 Loading 组件库

组件视觉用法
<CometSpinner /> 彗星拖尾环形 spinner(conic-gradient + radial mask) <CometSpinner className="size-12" />
<TextDots /> 文字 + 跳动省略号(3 dot opacity 错开波浪) <TextDots>Thinking</TextDots>
<TextShimmer /> 文字扫光(亮带从左到右掠过) <TextShimmer>Processing</TextShimmer>

已知局限(不再尝试解决)

首屏白屏问题绕不过去 — 测试过多个方案后确认根源在 React app 业务层(PageContainer 渲染好但 ProTable 数据还在请求,AntD Spin 接管)。loading.js 是 HTML 静态层兜底,已经做到极限。

真正能根治的方案需要 React app 自己做:ProLayout loading={!initialState} 配置 / PageContainer Suspense fallback / ProTable 数据加载 Skeleton — 属业务层范畴,UI Developer 不动 src/app.tsx

详细规约 + 关键代码 + 移除时机逻辑 + 反例清单,见 ui/doc/loadingSpinner.md

Inspect OFF