diff --git a/public/static/admin/css/public.css b/public/static/admin/css/public.css index 33a23ae..c4d1725 100644 --- a/public/static/admin/css/public.css +++ b/public/static/admin/css/public.css @@ -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; -} \ No newline at end of file +} diff --git a/public/static/admin/css/public.scss b/public/static/admin/css/public.scss index bfa8629..b1e6e08 100644 --- a/public/static/admin/css/public.scss +++ b/public/static/admin/css/public.scss @@ -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; diff --git a/public/static/plugs/ulthon-admin/admin-table.js b/public/static/plugs/ulthon-admin/admin-table.js index 2e22d4e..e21822a 100644 --- a/public/static/plugs/ulthon-admin/admin-table.js +++ b/public/static/plugs/ulthon-admin/admin-table.js @@ -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 = '