feat(table): 内置卡片形态与本地化样式

This commit is contained in:
augushong
2026-09-11 01:45:37 +00:00
parent a96137e290
commit 26534aed1c
3 changed files with 396 additions and 3 deletions

View File

@@ -175,8 +175,7 @@ body {
.layuimini-upload-show a img {
height: 80px;
-o-object-fit: cover;
object-fit: cover;
object-fit: cover;
}
.layuimini-upload-show .uploads-delete-tip {
@@ -414,6 +413,8 @@ table样式
}
.layui-layer-easy .layui-layer-moves {
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box;
}
@@ -727,6 +728,138 @@ table样式
border-radius: 0;
}
.ua-layout-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 12px;
}
.ua-layout-empty {
padding: 24px 0;
text-align: center;
color: #999;
}
.ua-layout-loading {
min-height: 60px;
opacity: 0.55;
pointer-events: none;
}
.ua-data-card {
display: flex;
flex-direction: column;
padding: 10px 12px;
border: 1px solid #f2f2f2;
border-radius: 5px;
background-color: #fff;
cursor: pointer;
transition: border-color 0.2s, box-shadow 0.2s;
}
.ua-data-card:hover {
border-color: #d2d2d2;
}
.ua-data-card-checked {
border-color: rgb(22, 183, 119);
box-shadow: 0 0 0 1px rgb(22, 183, 119) inset;
}
.ua-data-card-disabled {
background-color: #fafafa;
cursor: not-allowed;
}
.ua-data-card-disabled .ua-data-card-title,
.ua-data-card-disabled .ua-data-card-item-value,
.ua-data-card-disabled .ua-data-card-time {
color: #c2c2c2;
}
.ua-data-card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 8px;
margin-bottom: 8px;
border-bottom: 1px solid #f2f2f2;
}
.ua-data-card-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bold;
}
.ua-data-card-check {
flex: none;
margin-left: 8px;
color: #d2d2d2;
font-size: 18px;
line-height: 1;
}
.ua-data-card-checked .ua-data-card-check {
color: rgb(22, 183, 119);
}
.ua-data-card-body {
flex: 1;
}
.ua-data-card-item {
display: flex;
margin-bottom: 4px;
line-height: 22px;
}
.ua-data-card-item:last-child {
margin-bottom: 0;
}
.ua-data-card-item-title {
flex: none;
color: #999;
}
.ua-data-card-item-title::after {
content: ":";
}
.ua-data-card-item-value {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ua-data-card-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #f2f2f2;
}
.ua-data-card-time {
color: #999;
font-size: 12px;
}
.ua-data-card-operat {
display: flex;
flex-wrap: wrap;
gap: 4px;
justify-content: flex-end;
}
.ua-data-card-operat a {
margin: 0;
}
@media (max-width: 768px) {
.ua-layout-cards {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.layuimini-form .layui-form-item {
padding-right: 0;
@@ -761,4 +894,4 @@ table样式
.search-tab .layui-tabs-body {
display: none;
}
}

View File

@@ -740,6 +740,148 @@ table样式
}
// 多形态渲染:内置 card 形态(模板管线给容器挂 .ua-layout-cards,卡片本体为
// .ua-data-card 族;admin-table.js 内置 card 模板产出,样式本地化不依赖 CDN)
.ua-layout-cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 12px;
}
.ua-layout-empty {
padding: 24px 0;
text-align: center;
color: #999;
}
// 模板管线 reload 期间的加载态标记(wrapper 在 done 后移除)
.ua-layout-loading {
min-height: 60px;
opacity: .55;
pointer-events: none;
}
.ua-data-card {
display: flex;
flex-direction: column;
padding: 10px 12px;
border: 1px solid #f2f2f2;
border-radius: 5px;
background-color: #fff;
cursor: pointer;
transition: border-color .2s, box-shadow .2s;
&:hover {
border-color: #d2d2d2;
}
}
.ua-data-card-checked {
border-color: rgb(22, 183, 119);
box-shadow: 0 0 0 1px rgb(22, 183, 119) inset;
}
// 禁用行(_disabled):整卡置灰不可勾选(卡内操作按钮仍可用,与引擎 LAY_DISABLED 只锁勾选的语义一致)
.ua-data-card-disabled {
background-color: #fafafa;
cursor: not-allowed;
.ua-data-card-title,
.ua-data-card-item-value,
.ua-data-card-time {
color: #c2c2c2;
}
}
.ua-data-card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 8px;
margin-bottom: 8px;
border-bottom: 1px solid #f2f2f2;
}
.ua-data-card-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: bold;
}
.ua-data-card-check {
flex: none;
margin-left: 8px;
color: #d2d2d2;
font-size: 18px;
line-height: 1;
}
.ua-data-card-checked .ua-data-card-check {
color: rgb(22, 183, 119);
}
.ua-data-card-body {
flex: 1;
}
.ua-data-card-item {
display: flex;
margin-bottom: 4px;
line-height: 22px;
&:last-child {
margin-bottom: 0;
}
}
.ua-data-card-item-title {
flex: none;
color: #999;
&::after {
content: ':';
}
}
.ua-data-card-item-value {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ua-data-card-footer {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #f2f2f2;
}
.ua-data-card-time {
color: #999;
font-size: 12px;
}
.ua-data-card-operat {
display: flex;
flex-wrap: wrap;
gap: 4px;
justify-content: flex-end;
a {
margin: 0;
}
}
@media (max-width: 768px) {
.ua-layout-cards {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.layuimini-form .layui-form-item {
padding-right: 0;

View File

@@ -79,6 +79,106 @@
return result;
};
// ========== 内置 card 形态(name='card' 且无自定义钩子时启用)==========
// 结构参照旧寄生实现的 header/body/footer 骨架(代码全新):header(标题 +
// 勾位图标)、body(键值对字段列表)、footer(create_time + 操作按钮)。
// 数据全部来自渲染上下文(_cells/_operatHtml/_checked/_disabled),列元信息读
// controlledLayouts 的 cols(本闭包内),无任何业务字段硬编码。
//
// 【与 buildLayoutContext 同规则的单元格列筛选】操作列无 field,须先于
// field 判定识别;checkbox/radio 等无字段列跳过(勾选语义由勾位图标承接)
var builtinCardCellCols = function (tableId) {
var cols = (controlledLayouts[tableId] && controlledLayouts[tableId].cols[0]) || [];
var cellCols = [];
$.each(cols, function (i, col) {
if (!col) {
return;
}
if (col.templet === ua.table.tool) {
return;
}
if (!col.field) {
return;
}
cellCols.push(col);
});
return cellCols;
};
// 内置 card 单行模板(tplFunction 调用约定:this/第一参 = 行上下文,第二参 = 列表上下文)
// - 标题列 = 首个字段列(含 checkbox 列的表格即"第二列",无 checkbox 表格即"首字段")
// - 标题列与 create_time 列不重复进 body(分别在 header/footer 展示)
// - create_time 无对应列时 footer 时间留空,不回退原始字段值(避免裸时间戳)
var builtinCardTemplate = function (row, list) {
var cellCols = builtinCardCellCols(list.tableId);
var titleCol = cellCols.length > 0 ? cellCols[0] : null;
var html = '<div class="ua-data-card'
+ (row._checked ? ' ua-data-card-checked' : '')
+ (row._disabled ? ' ua-data-card-disabled' : '')
+ '" data-table-id="' + list.tableId + '" data-id="' + row.id + '">';
html += '<div class="ua-data-card-header">';
html += '<div class="ua-data-card-title">' + (titleCol ? (row._cells[titleCol.field] || '') : '') + '</div>';
html += '<div class="ua-data-card-check"><i class="layui-icon layui-icon-ok-circle"></i></div>';
html += '</div>';
html += '<div class="ua-data-card-body">';
$.each(cellCols, function (i, col) {
if (titleCol && col.field === titleCol.field) {
return;
}
if (col.field === 'create_time') {
return;
}
html += '<div class="ua-data-card-item">'
+ '<span class="ua-data-card-item-title">' + (col.title || col.field) + '</span>'
+ '<span class="ua-data-card-item-value">' + (row._cells[col.field] || '') + '</span>'
+ '</div>';
});
html += '</div>';
html += '<div class="ua-data-card-footer">';
html += '<div class="ua-data-card-time">' + (row._cells['create_time'] || '') + '</div>';
html += '<div class="ua-data-card-operat">' + (row._operatHtml || '') + '</div>';
html += '</div>';
html += '</div>';
return html;
};
// 内置 card 勾选交互:点卡片任意处切换选中(操作按钮/表单控件等交互元素除外),
// 禁用行(_disabled)整卡不可勾选。选择态落 store(与引擎勾选同一事实源,
// 批量删除经 ua.table.checkStatus 分发即取到),随后重渲模板管线回显勾位图标。
// 自定义模板形态的统一行选中委托是任务 18 范围,本委托只认 .ua-data-card。
$(document).on('click', '.ua-data-card', function (e) {
var $card = $(this);
if ($card.hasClass('ua-data-card-disabled')) {
return;
}
if ($(e.target).closest('a,button,input,select,textarea,label,.layui-form-switch,.layui-form-checkbox,.layui-form-radio').length) {
return;
}
var tableId = $card.attr('data-table-id');
var state = window.UA_TABLE_STORE && window.UA_TABLE_STORE.dataProvider.getState(tableId);
if (!state) {
return;
}
var row = null;
var rowId = $card.attr('data-id');
$.each(state.currentPageData || [], function (i, item) {
if (String(item.id) === String(rowId)) {
row = item;
return false;
}
});
if (!row || row.LAY_DISABLED) {
return;
}
window.UA_TABLE_STORE.selection.set(tableId, row);
ua.table.renderLayoutForm(tableId);
});
// (b) 模板引擎优先级解析:tplFunction > tplLay > tplVue > tplLayId > tplVueId,
// 提供一种即生效;五种全无且非 table 条目 → console.warn 并回退 table。
// lay/vue/layId/vueId 的引擎挂载在任务 16(laytpl)/17(Vue),此处只做分发判定。
@@ -86,6 +186,21 @@
if (entry.name === 'table') {
return {kind: 'table', entry: entry};
}
// 内置 card 形态:name='card' 且未提供任何模板钩子时挂内置模板;提供了
// 钩子(tplFunction/tplLay/tplVue/tplLayId/tplVueId 任一)则照常走五级
// 优先级,自定义实现完全覆盖内置形态。builtinCard 标记供模板管线给容器
// 挂网格布局类(.ua-layout-cards)
if (entry.name === 'card') {
var hasHook = typeof entry.tplFunction === 'function'
|| (typeof entry.tplLay === 'string' && entry.tplLay !== '')
|| (typeof entry.tplVue === 'string' && entry.tplVue !== '')
|| (typeof entry.tplLayId === 'string' && entry.tplLayId !== '')
|| (typeof entry.tplVueId === 'string' && entry.tplVueId !== '');
if (!hasHook) {
var builtinEntry = $.extend({}, entry, {tplFunction: builtinCardTemplate, builtinCard: true});
return {kind: 'function', entry: builtinEntry};
}
}
if (typeof entry.tplFunction === 'function') {
return {kind: 'function', entry: entry};
}
@@ -310,6 +425,9 @@
});
}
// 内置 card 形态容器挂网格布局类(样式见 public.scss 的 .ua-layout-cards),
// 自定义模板形态移除该标记,不受卡片网格影响
$container.toggleClass('ua-layout-cards', resolved.entry.builtinCard === true);
$container.html(html);
// 容器恒为 .layui-form(lay-filter 与容器 id 同名),scoped form.render
// 美化模板产出的 switch/checkbox 控件