iter(g350): 数据表格补 caption 可访问名
This commit is contained in:
+6
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T18:28:42.413Z",
|
"generatedAt": "2026-10-26T18:35:06.426Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -2176,6 +2176,11 @@
|
|||||||
"t": "(row: T) => unknown",
|
"t": "(row: T) => unknown",
|
||||||
"k": 1,
|
"k": 1,
|
||||||
"d": "传了才多一列展开钮:点开在该行下铺一条详情行(内容由这里给,展开态按 rowKey 记,筛选翻页不丢)"
|
"d": "传了才多一列展开钮:点开在该行下铺一条详情行(内容由这里给,展开态按 rowKey 记,筛选翻页不丢)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "caption",
|
||||||
|
"t": "string",
|
||||||
|
"d": "表格可访问名:渲染成视觉隐藏的 <caption>,读屏进表时靠它说清是哪一张表(纯图形上看不见,也不占位)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
|
|||||||
+2
-1
@@ -1483,9 +1483,10 @@ const content = h(
|
|||||||
section(
|
section(
|
||||||
'sec-table',
|
'sec-table',
|
||||||
'数据表格',
|
'数据表格',
|
||||||
'粘性表头、斑马纹、行选择(全选三态 + 已选计数,选中行低视觉比重)、点表头或 Tab 聚焦后按 Enter/空格 排序(按住 Shift 点第二个表头即多列叠加,标记带优先级 ↑1 / ↓2)、定宽列表头右缘可拖拽或 ←→ 调列宽(双击复位)、工具栏筛选(0 行时按「有没有筛选词」分别提示「暂无数据」或「没有符合…的行」并给一键清除)、底部分页;工具栏「列」按钮勾选即可隐藏/恢复列(至少留一列,隐藏列不占表头也不进导出)、「导出 CSV」导的是筛选 + 排序后的全量行(不含勾选,与当前页无关)。',
|
'粘性表头、斑马纹、行选择(全选三态 + 已选计数,选中行低视觉比重)、点表头或 Tab 聚焦后按 Enter/空格 排序(按住 Shift 点第二个表头即多列叠加,标记带优先级 ↑1 / ↓2)、定宽列表头右缘可拖拽或 ←→ 调列宽(双击复位)、工具栏筛选(0 行时按「有没有筛选词」分别提示「暂无数据」或「没有符合…的行」并给一键清除)、底部分页;工具栏「列」按钮勾选即可隐藏/恢复列(至少留一列,隐藏列不占表头也不进导出)、「导出 CSV」导的是筛选 + 排序后的全量行(不含勾选,与当前页无关);caption 是表格的可访问名,视觉隐藏但读屏进表先念它。',
|
||||||
card({ title: '用户表' }, dataTable({
|
card({ title: '用户表' }, dataTable({
|
||||||
data: users,
|
data: users,
|
||||||
|
caption: '用户表:姓名 / 年龄 / 城市三列,可按列排序与筛选', // 可访问名:读屏进表先念这句
|
||||||
selectable: true,
|
selectable: true,
|
||||||
exportable: true,
|
exportable: true,
|
||||||
columnToggle: true,
|
columnToggle: true,
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T18:28:42.413Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T18:35:06.426Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -1046,6 +1046,7 @@ const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label:
|
|||||||
- columnToggle(默认 false):boolean —— 工具栏显示「列」按钮:弹层内勾选控制列显隐,至少保留一列(最后一列的勾选框锁死),缺省 false
|
- columnToggle(默认 false):boolean —— 工具栏显示「列」按钮:弹层内勾选控制列显隐,至少保留一列(最后一列的勾选框锁死),缺省 false
|
||||||
- hiddenColumns:Signal<string[]> —— 受控隐藏列键集:与「列」面板双向同步,外部可据此持久化用户偏好;隐藏列不渲染、不进导出
|
- hiddenColumns:Signal<string[]> —— 受控隐藏列键集:与「列」面板双向同步,外部可据此持久化用户偏好;隐藏列不渲染、不进导出
|
||||||
- renderDetail(回调):(row: T) => unknown —— 传了才多一列展开钮:点开在该行下铺一条详情行(内容由这里给,展开态按 rowKey 记,筛选翻页不丢)
|
- renderDetail(回调):(row: T) => unknown —— 传了才多一列展开钮:点开在该行下铺一条详情行(内容由这里给,展开态按 rowKey 记,筛选翻页不丢)
|
||||||
|
- caption:string —— 表格可访问名:渲染成视觉隐藏的 <caption>,读屏进表时靠它说清是哪一张表(纯图形上看不见,也不占位)
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
|||||||
props.columnToggle ? columnPicker({ columns: props.columns, hidden, disabled: busy }) : null, // 列显隐面板:勾选即重绘表头与单元格
|
props.columnToggle ? columnPicker({ columns: props.columns, hidden, disabled: busy }) : null, // 列显隐面板:勾选即重绘表头与单元格
|
||||||
props.exportable ? exportButton(table, () => shown(), props.exportName ?? 'table.csv') : null, // 导出筛选+排序后的全量行,列跟随当前可见列
|
props.exportable ? exportButton(table, () => shown(), props.exportName ?? 'table.csv') : null, // 导出筛选+排序后的全量行,列跟随当前可见列
|
||||||
),
|
),
|
||||||
h('div', { class: 'ua-table-scroll' }, h('table', null, colgroup, thead, tbody)),
|
h('div', { class: 'ua-table-scroll' }, h('table', null, props.caption ? h('caption', { class: 'ua-visually-hidden' }, props.caption) : null, colgroup, thead, tbody)), // caption 视觉隐藏仍进无障碍树:表格才有名字
|
||||||
tablePager({
|
tablePager({
|
||||||
info,
|
info,
|
||||||
total: () => visible().total,
|
total: () => visible().total,
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export interface DataTableProps<T> {
|
|||||||
columnToggle?: boolean; // 工具栏显示「列」按钮:弹层内勾选控制列显隐,至少保留一列;缺省 false
|
columnToggle?: boolean; // 工具栏显示「列」按钮:弹层内勾选控制列显隐,至少保留一列;缺省 false
|
||||||
hiddenColumns?: Signal<string[]>; // 受控隐藏列键集:与「列」面板双向同步,供调用方记住用户偏好
|
hiddenColumns?: Signal<string[]>; // 受控隐藏列键集:与「列」面板双向同步,供调用方记住用户偏好
|
||||||
onRowClick?: (row: T) => void; // 整行点击 / Enter / 空格的回调;传了行才可聚焦(未传时行不可交互)
|
onRowClick?: (row: T) => void; // 整行点击 / Enter / 空格的回调;传了行才可聚焦(未传时行不可交互)
|
||||||
|
caption?: string; // 表格可访问名(渲染成视觉隐藏的 <caption>):读屏进表只念「表格」时,靠它说清是哪一张
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 选项条目:select / radioGroup / tabs / segmented 共用的选项形状 ***/
|
/*** 选项条目:select / radioGroup / tabs / segmented 共用的选项形状 ***/
|
||||||
|
|||||||
@@ -314,13 +314,12 @@
|
|||||||
"n": "password",
|
"n": "password",
|
||||||
"t": "boolean",
|
"t": "boolean",
|
||||||
"d": "密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本"
|
"d": "密码框:原生打点渲染,内置「显示/隐藏」切换按钮(aria-label 同步);缺省普通文本"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "ariaLabel",
|
||||||
|
"t": "string",
|
||||||
|
"d": "无可见 label 时的可访问名(如「收件人」);缺省「文本输入」"
|
||||||
}
|
}
|
||||||
,
|
|
||||||
{
|
|
||||||
"n": "ariaLabel",
|
|
||||||
"t": "string",
|
|
||||||
"d": "无可见 label 时的可访问名(如「收件人」);缺省「文本输入」"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
|
"example": "const name = signal(''); const el = textField({ value: name, placeholder: '输入名字…', onInput: (v) => console.log(v) })",
|
||||||
"steps": [
|
"steps": [
|
||||||
@@ -388,13 +387,12 @@
|
|||||||
"n": "size",
|
"n": "size",
|
||||||
"t": "'sm' | 'md' | 'lg'",
|
"t": "'sm' | 'md' | 'lg'",
|
||||||
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
"d": "高度档位:sm 紧凑 / md 默认 / lg 大触控目标,走 --ua-control-h-* 三档;缺省 md"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "ariaLabel",
|
||||||
|
"t": "string",
|
||||||
|
"d": "无可见 label 时的可访问名(如「数量」);缺省「数字输入」"
|
||||||
}
|
}
|
||||||
,
|
|
||||||
{
|
|
||||||
"n": "ariaLabel",
|
|
||||||
"t": "string",
|
|
||||||
"d": "无可见 label 时的可访问名(如「数量」);缺省「数字输入」"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
|
"example": "const qty = signal(1); const el = inputNumber({ value: qty, min: 1, max: 99, onChange: (v) => console.log(v) })",
|
||||||
"steps": [
|
"steps": [
|
||||||
@@ -456,13 +454,12 @@
|
|||||||
"n": "autosize",
|
"n": "autosize",
|
||||||
"t": "boolean | { minRows?, maxRows? }",
|
"t": "boolean | { minRows?, maxRows? }",
|
||||||
"d": "自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数"
|
"d": "自适应内容高度:true 跟随内容涨落,对象态钳最小/最大行数,超出 maxRows 内部滚动;缺省固定行数"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "ariaLabel",
|
||||||
|
"t": "string",
|
||||||
|
"d": "无可见 label 时的可访问名(如「备注」);缺省「多行文本」"
|
||||||
}
|
}
|
||||||
,
|
|
||||||
{
|
|
||||||
"n": "ariaLabel",
|
|
||||||
"t": "string",
|
|
||||||
"d": "无可见 label 时的可访问名(如「备注」);缺省「多行文本」"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })",
|
"example": "const bio = signal(''); textarea({ value: bio, rows: 4, placeholder: '一句话介绍…' })",
|
||||||
"steps": [
|
"steps": [
|
||||||
@@ -539,13 +536,12 @@
|
|||||||
"t": "(value: number) => void",
|
"t": "(value: number) => void",
|
||||||
"k": 1,
|
"k": 1,
|
||||||
"d": "拖动回调"
|
"d": "拖动回调"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "ariaLabel",
|
||||||
|
"t": "string",
|
||||||
|
"d": "无可见 label 时的可访问名(如「音量」);缺省「数值」,区间模式自动带「下限 / 上限」"
|
||||||
}
|
}
|
||||||
,
|
|
||||||
{
|
|
||||||
"n": "ariaLabel",
|
|
||||||
"t": "string",
|
|
||||||
"d": "无可见 label 时的可访问名(如「音量」);缺省「数值」,区间模式自动带「下限 / 上限」"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })",
|
"example": "const vol = signal(40); slider({ value: vol, min: 0, max: 100, step: 5, onChange: (v) => console.log(v) })",
|
||||||
"steps": [
|
"steps": [
|
||||||
@@ -2050,6 +2046,11 @@
|
|||||||
"t": "(row: T) => unknown",
|
"t": "(row: T) => unknown",
|
||||||
"k": 1,
|
"k": 1,
|
||||||
"d": "传了才多一列展开钮:点开在该行下铺一条详情行(内容由这里给,展开态按 rowKey 记,筛选翻页不丢)"
|
"d": "传了才多一列展开钮:点开在该行下铺一条详情行(内容由这里给,展开态按 rowKey 记,筛选翻页不丢)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "caption",
|
||||||
|
"t": "string",
|
||||||
|
"d": "表格可访问名:渲染成视觉隐藏的 <caption>,读屏进表时靠它说清是哪一张表(纯图形上看不见,也不占位)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
@@ -4674,13 +4675,46 @@
|
|||||||
"title": "行内编辑 Editable",
|
"title": "行内编辑 Editable",
|
||||||
"demo": "sec-editable",
|
"demo": "sec-editable",
|
||||||
"props": [
|
"props": [
|
||||||
{ "n": "value", "t": "Signal<string>", "r": 1, "d": "展示值,双向绑定:只有提交才写回,Esc 取消绝不落值" },
|
{
|
||||||
{ "n": "placeholder", "t": "string", "df": "'点击填写'", "d": "空值时的占位文案,颜色走辅助文字档" },
|
"n": "value",
|
||||||
{ "n": "label", "t": "string", "df": "'编辑'", "d": "读屏用的字段名,作静止态按钮 aria-label 的前缀(如「标题:季度目标」)" },
|
"t": "Signal<string>",
|
||||||
{ "n": "multiline", "t": "boolean", "d": "多行模式(textarea):Enter 换行,Ctrl / Cmd + Enter 才提交;缺省单行" },
|
"r": 1,
|
||||||
{ "n": "disabled", "t": "boolean | Signal<boolean>", "d": "禁用只读,布尔与信号双口径;点了也不进编辑态" },
|
"d": "展示值,双向绑定:只有提交才写回,Esc 取消绝不落值"
|
||||||
{ "n": "validate", "t": "(v: string) => string | null", "k": 1, "d": "提交校验:返回文案即拒绝提交、保持编辑态并标红(aria-invalid + role=alert),返回 null 表示通过" },
|
},
|
||||||
{ "n": "onCommit", "t": "(v: string) => void", "k": 1, "d": "提交回调,值已写回信号之后触发;取消不触发" }
|
{
|
||||||
|
"n": "placeholder",
|
||||||
|
"t": "string",
|
||||||
|
"df": "'点击填写'",
|
||||||
|
"d": "空值时的占位文案,颜色走辅助文字档"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "label",
|
||||||
|
"t": "string",
|
||||||
|
"df": "'编辑'",
|
||||||
|
"d": "读屏用的字段名,作静止态按钮 aria-label 的前缀(如「标题:季度目标」)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "multiline",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "多行模式(textarea):Enter 换行,Ctrl / Cmd + Enter 才提交;缺省单行"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "disabled",
|
||||||
|
"t": "boolean | Signal<boolean>",
|
||||||
|
"d": "禁用只读,布尔与信号双口径;点了也不进编辑态"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "validate",
|
||||||
|
"t": "(v: string) => string | null",
|
||||||
|
"k": 1,
|
||||||
|
"d": "提交校验:返回文案即拒绝提交、保持编辑态并标红(aria-invalid + role=alert),返回 null 表示通过"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "onCommit",
|
||||||
|
"t": "(v: string) => void",
|
||||||
|
"k": 1,
|
||||||
|
"d": "提交回调,值已写回信号之后触发;取消不触发"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"example": "const title = signal('季度目标'); editable({ value: title, label: '标题', validate: (v) => (v ? null : '标题不能为空'), onCommit: (v) => toast.success('已保存:' + v) })",
|
"example": "const title = signal('季度目标'); editable({ value: title, label: '标题', validate: (v) => (v ? null : '标题不能为空'), onCommit: (v) => toast.success('已保存:' + v) })",
|
||||||
"steps": [
|
"steps": [
|
||||||
@@ -4694,4 +4728,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1797,6 +1797,20 @@ function checkRoleNames(pkgs) {
|
|||||||
return issues;
|
return issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** 校验表格可访问名:没有 caption 的表格读屏只念「表格」,几张表同屏时分不清哪张是哪张 ***/
|
||||||
|
function checkTableCaption(pkgs) {
|
||||||
|
const issues = [];
|
||||||
|
for (const pkg of pkgs) {
|
||||||
|
if (pkg.type !== 'skin-dom') continue;
|
||||||
|
const tablePath = path.join(ROOT, pkg.dir, 'src', 'dataTable.ts');
|
||||||
|
if (!fs.existsSync(tablePath)) continue;
|
||||||
|
const src = fs.readFileSync(tablePath, 'utf8');
|
||||||
|
if (!/props\.caption \? h\('caption'/.test(src)) issues.push(`${pkg.name}/src/dataTable.ts 没渲染 caption:表格缺可访问名,读屏只说「表格」`);
|
||||||
|
if (!/caption\?: string/.test(fs.readFileSync(path.join(ROOT, pkg.dir, 'src', 'types.ts'), 'utf8'))) issues.push(`${pkg.name}/src/types.ts 的 DataTableProps 缺 caption:没入口就永远命名不了`);
|
||||||
|
}
|
||||||
|
return issues;
|
||||||
|
}
|
||||||
|
|
||||||
/*** 校验同义词表:目标 id 必须真实存在(改名后残留会让回退指到空气),同一个词不得归两组(歧义会让结果随表序漂移) ***/
|
/*** 校验同义词表:目标 id 必须真实存在(改名后残留会让回退指到空气),同一个词不得归两组(歧义会让结果随表序漂移) ***/
|
||||||
function checkSynonyms(ids) {
|
function checkSynonyms(ids) {
|
||||||
const issues = [];
|
const issues = [];
|
||||||
@@ -1886,6 +1900,7 @@ function cmdVerify() {
|
|||||||
issues.push(...checkFormBusy(pkgs)); // 提交忙碌态必须有可见反馈
|
issues.push(...checkFormBusy(pkgs)); // 提交忙碌态必须有可见反馈
|
||||||
issues.push(...checkFormLabel(pkgs)); // 标签必须真正归属控件
|
issues.push(...checkFormLabel(pkgs)); // 标签必须真正归属控件
|
||||||
issues.push(...checkRoleNames(pkgs)); // listbox / radiogroup 必须有名字
|
issues.push(...checkRoleNames(pkgs)); // listbox / radiogroup 必须有名字
|
||||||
|
issues.push(...checkTableCaption(pkgs)); // 表格必须有可访问名
|
||||||
issues.push(...checkSynonyms(new Set(pkgs.flatMap((p) => (p.docs?.components ?? []).map((c) => c.id))))); // 同义词表指向的组件必须真实存在
|
issues.push(...checkSynonyms(new Set(pkgs.flatMap((p) => (p.docs?.components ?? []).map((c) => c.id))))); // 同义词表指向的组件必须真实存在
|
||||||
checkDarkTokens(issues);
|
checkDarkTokens(issues);
|
||||||
checkTokenParity(issues);
|
checkTokenParity(issues);
|
||||||
@@ -1898,7 +1913,7 @@ function cmdVerify() {
|
|||||||
console.error(`${issues.length} violation(s)`);
|
console.error(`${issues.length} violation(s)`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全、tokens.json ↔ tokens.css 名字与值双源一致、@keyframes 无复制粘贴的重复帧、滚动容器全在细条规则内,README 组件清单与分组同步,backlog ↔ 留痕双向一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00,每个 iter 提交都有留痕),组合配方结构完整,MCP 工具清单三处一致,搜索同义词表目标存在且无歧义,combobox / slider / range 控件与 listbox / radiogroup 容器都有可访问名,toast 有 live 区域与 alert 抢占播报,表单错误文案被 aria-describedby 关联、标签归属控件(for / aria-labelledby)、重置真复位、错误环只在聚焦时亮、汇总条随校验重算、提交按钮有忙碌态,视觉回归抽查清单十九组件 × 五维度齐全)');
|
console.log('verify OK (manifest ↔ docs ↔ demo 一致,文档 props ↔ 入参接口双向一致,皮肤无硬编码颜色与未定义令牌、暗色令牌齐全、tokens.json ↔ tokens.css 名字与值双源一致、@keyframes 无复制粘贴的重复帧、滚动容器全在细条规则内,README 组件清单与分组同步,backlog ↔ 留痕双向一致(编号严格递增、无重复 id、留痕时间线单调且带 +08:00,每个 iter 提交都有留痕),组合配方结构完整,MCP 工具清单三处一致,搜索同义词表目标存在且无歧义,combobox / slider / range 控件与 listbox / radiogroup 容器都有可访问名、表格有 caption,toast 有 live 区域与 alert 抢占播报,表单错误文案被 aria-describedby 关联、标签归属控件(for / aria-labelledby)、重置真复位、错误环只在聚焦时亮、汇总条随校验重算、提交按钮有忙碌态,视觉回归抽查清单十九组件 × 五维度齐全)');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/
|
/*** 校验 git 提交信息是否符合 COMMITTING.md 规范(参数为 message 文件路径或直接 message 文本),违规退出码 1 ***/
|
||||||
|
|||||||
Reference in New Issue
Block a user