mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-11-03 19:08:23 +08:00
236 lines
8.8 KiB
PHP
236 lines
8.8 KiB
PHP
<div class="layuimini-container">
|
||
<div class="layuimini-main">
|
||
<table id="currentTable" class="layui-table layui-hide" data-auth-add="{:auth('system.admin/add')}" data-auth-edit="{:auth('system.admin/edit')}" data-auth-delete="{:auth('system.admin/delete')}" data-auth-password="{:auth('system.admin/password')}" data-auth-modify="{:auth('system.admin/modify')}" lay-filter="currentTable">
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 用户管理名片形态(layoutList 自定义 Vue 形态官方示例)
|
||
硬约束:本节点必须位于页面根容器之外、Vue 渲染容器(#ua-layout-{tableId},
|
||
由框架在表格容器后动态创建)之外,否则会被 Vue 编译吞掉。
|
||
上下文契约(admin-table.js buildLayoutContext):
|
||
- rows:行上下文数组,d 与表格行数据同构(含原始字段 + _cells/_operatHtml/_checked/_disabled)
|
||
- list:列表上下文 {tableId, page, count, checkedAll}(Vue 形态不叫 _list)
|
||
- 行根元素携带 data-table-id + data-id 两属性即获得统一行选中委托(点击勾位
|
||
图标切换勾选,按钮/表单控件/layui 美化层自动豁免),选中态经 :class 高亮回显
|
||
- _cells/_operatHtml 含渲染产物 HTML,必须用 v-html 输出;纯文本字段用
|
||
{{ }} 原生插值(自动转义,与 laytpl 的 {{- }} 不同)
|
||
- 事件无需自行绑定:操作按钮与 switch 经页面级委托和 form 事件天然生效
|
||
布局 v2(视觉升级):头部左头像+姓名/账户两行、右状态开关(不再独占
|
||
一行);主体紧凑小字段(手机/登录次数)+ 备注单行省略;底部上边框分隔,
|
||
左创建时间右操作按钮组 -->
|
||
<script type="text/x-template" id="usercardTpl">
|
||
<div class="usercard-list">
|
||
<div class="ua-data-card usercard"
|
||
v-for="d in rows"
|
||
:key="d.id"
|
||
:data-id="d.id"
|
||
:data-table-id="list.tableId"
|
||
:class="{'ua-data-card-checked': d._checked, 'ua-data-card-disabled': d._disabled}">
|
||
<!-- 头部:左 48px 圆形头像(head_img 原始字段直绑,绕开渲染器内联
|
||
缩略样式;默认头像占位垫底,图片加载失败 @error 隐藏 img 纯
|
||
DOM 降级,不触碰冻结上下文)+ 姓名/登录账户上下两行;
|
||
右状态开关悬浮同行:_cells.status 为 switch 渲染器输出的原生
|
||
checkbox,容器 layui-form 祖先 + 挂载后 form.render 美化,
|
||
切换经页面级 switch 委托提交 modify 接口,失败自动回滚重渲 -->
|
||
<div class="usercard-header">
|
||
<span class="usercard-avatar-box">
|
||
<span class="usercard-avatar usercard-avatar-fallback" title="未设置头像"><i class="layui-icon layui-icon-username"></i></span>
|
||
<img v-if="d.head_img" :src="d.head_img" :alt="d.nickname || d.username"
|
||
class="usercard-avatar usercard-avatar-img"
|
||
@error="$event.target.style.display = 'none'">
|
||
</span>
|
||
<div class="usercard-id">
|
||
<span class="usercard-name" :title="d.nickname || d.username">{{ d.nickname || d.username }}</span>
|
||
<span class="usercard-account" :title="d.username">{{ d.username }}</span>
|
||
</div>
|
||
<div class="usercard-status" v-html="d._cells.status"></div>
|
||
<div class="ua-data-card-check"><i class="layui-icon layui-icon-ok-circle"></i></div>
|
||
</div>
|
||
<!-- 主体:紧凑小字段两列排布(手机无值显示 —,登录次数为 pill 徽标),
|
||
备注有值才渲染、单行省略;均为纯文本原始字段插值 -->
|
||
<div class="usercard-body">
|
||
<div class="usercard-fields">
|
||
<div class="usercard-field">
|
||
<span class="usercard-field-label">手机</span>
|
||
<span class="usercard-field-value" :title="d.phone">{{ d.phone || '—' }}</span>
|
||
</div>
|
||
<div class="usercard-field">
|
||
<span class="usercard-field-label">登录次数</span>
|
||
<span class="usercard-badge">{{ d.login_num }}</span>
|
||
</div>
|
||
</div>
|
||
<div v-if="d.remark" class="usercard-remark" :title="d.remark">{{ d.remark }}</div>
|
||
</div>
|
||
<!-- 底部:上边框分隔,左创建时间(模型获取器已格式化)右操作按钮组
|
||
(_operatHtml 为操作列渲染产物:编辑/设置密码/删除,已按
|
||
data-auth-* 裁剪权限),点击行为由框架页面级委托承接 -->
|
||
<div class="usercard-footer">
|
||
<span class="usercard-time">{{ d.create_time }}</span>
|
||
<div class="ua-data-card-operat usercard-operat" v-html="d._operatHtml"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</script>
|
||
|
||
<!-- 名片形态页面级样式 v2:统一设计语言覆写仅作用于本页 .usercard
|
||
(圆角 8px/1px #eee 边框/hover 阴影/选中主题蓝描边+浅蓝底/徽标 pill/
|
||
12px 内边距与 8-10px 区块节奏),公共 .ua-data-card 族不动;
|
||
网格布局与头像圆形为本页自建 -->
|
||
<style>
|
||
.usercard-list {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||
gap: 12px;
|
||
}
|
||
.usercard {
|
||
padding: 12px;
|
||
border: 1px solid #eee;
|
||
border-radius: 8px;
|
||
}
|
||
.usercard:hover {
|
||
border-color: #e0e0e0;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, .08);
|
||
}
|
||
.usercard.ua-data-card-checked {
|
||
border-color: #1e9fff;
|
||
box-shadow: 0 0 0 1px #1e9fff inset;
|
||
background-color: #f2f9ff;
|
||
}
|
||
.usercard.ua-data-card-checked:hover {
|
||
box-shadow: 0 0 0 1px #1e9fff inset, 0 2px 12px rgba(0, 0, 0, .08);
|
||
}
|
||
.usercard-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
}
|
||
.usercard-avatar-box {
|
||
position: relative;
|
||
flex: none;
|
||
width: 48px;
|
||
height: 48px;
|
||
}
|
||
.usercard-avatar {
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
width: 48px;
|
||
height: 48px;
|
||
border-radius: 50%;
|
||
border: 1px solid #eee;
|
||
background-color: #f6f7f9;
|
||
}
|
||
.usercard-avatar-fallback {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #c2c2c2;
|
||
font-size: 22px;
|
||
}
|
||
.usercard-avatar-img {
|
||
object-fit: cover;
|
||
}
|
||
.usercard-id {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.usercard-name {
|
||
display: block;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
line-height: 20px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
}
|
||
.usercard-account {
|
||
display: block;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
line-height: 18px;
|
||
color: #999;
|
||
font-size: 12px;
|
||
}
|
||
.usercard-status {
|
||
flex: none;
|
||
margin-left: auto;
|
||
}
|
||
.usercard-status .layui-form-switch {
|
||
margin: 0;
|
||
}
|
||
.usercard-body {
|
||
margin-top: 10px;
|
||
}
|
||
.usercard-fields {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
.usercard-field {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
line-height: 20px;
|
||
}
|
||
.usercard-field-label {
|
||
flex: none;
|
||
color: #999;
|
||
font-size: 12px;
|
||
}
|
||
.usercard-field-value {
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
color: #333;
|
||
font-size: 13px;
|
||
}
|
||
.usercard-badge {
|
||
display: inline-block;
|
||
padding: 0 8px;
|
||
border-radius: 10px;
|
||
background-color: #eef6ff;
|
||
color: #1e9fff;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
}
|
||
.usercard-remark {
|
||
margin-top: 6px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
color: #999;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
}
|
||
.usercard-footer {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
margin-top: 10px;
|
||
padding-top: 10px;
|
||
border-top: 1px solid #eee;
|
||
}
|
||
.usercard-time {
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
color: #999;
|
||
font-size: 12px;
|
||
}
|
||
.usercard-operat {
|
||
flex: none;
|
||
}
|
||
@media (max-width: 768px) {
|
||
.usercard-list {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|