mirror of
https://gitee.com/ulthon/ulthon_admin.git
synced 2026-10-29 08:58:22 +08:00
feat(view): 名片模板视觉升级,状态开关移至头部
This commit is contained in:
@@ -5,17 +5,20 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 用户管理名片形态(layoutList 自定义 Vue 形态官方示例,任务 19)
|
||||
<!-- 用户管理名片形态(layoutList 自定义 Vue 形态官方示例)
|
||||
硬约束:本节点必须位于页面根容器之外、Vue 渲染容器(#ua-layout-{tableId},
|
||||
由框架在表格容器后动态创建)之外,否则会被 Vue 编译吞掉。
|
||||
上下文契约(admin-table.js buildLayoutContext / 任务 17 定稿):
|
||||
上下文契约(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 事件天然生效 -->
|
||||
- 事件无需自行绑定:操作按钮与 switch 经页面级委托和 form 事件天然生效
|
||||
布局 v2(视觉升级):头部左头像+姓名/账户两行、右状态开关(不再独占
|
||||
一行);主体紧凑小字段(手机/登录次数)+ 备注单行省略;底部上边框分隔,
|
||||
左创建时间右操作按钮组 -->
|
||||
<script type="text/x-template" id="usercardTpl">
|
||||
<div class="usercard-list">
|
||||
<div class="ua-data-card usercard"
|
||||
@@ -24,97 +27,204 @@
|
||||
:data-id="d.id"
|
||||
:data-table-id="list.tableId"
|
||||
:class="{'ua-data-card-checked': d._checked, 'ua-data-card-disabled': d._disabled}">
|
||||
<!-- 头部:圆形头像 + 账户名。head_img 直接取原始行字段绑定 src(渲染器
|
||||
_cells.head_img 的内联缩略图样式不适合名片);无头像时显示内置图标
|
||||
占位(当前库内数据均有头像,此分支为约定兜底) -->
|
||||
<!-- 头部:左 48px 圆形头像(head_img 原始字段直绑,绕开渲染器内联
|
||||
缩略样式;默认头像占位垫底,图片加载失败 @error 隐藏 img 纯
|
||||
DOM 降级,不触碰冻结上下文)+ 姓名/登录账户上下两行;
|
||||
右状态开关悬浮同行:_cells.status 为 switch 渲染器输出的原生
|
||||
checkbox,容器 layui-form 祖先 + 挂载后 form.render 美化,
|
||||
切换经页面级 switch 委托提交 modify 接口,失败自动回滚重渲 -->
|
||||
<div class="usercard-header">
|
||||
<img v-if="d.head_img" :src="d.head_img" :alt="d.username" class="usercard-avatar">
|
||||
<span v-else class="usercard-avatar usercard-avatar-empty" title="未设置头像"><i class="layui-icon layui-icon-username"></i></span>
|
||||
<div class="usercard-name">
|
||||
<span class="usercard-username">{{ d.username }}</span>
|
||||
<span v-if="d.nickname" class="usercard-nickname">{{ d.nickname }}</span>
|
||||
<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>
|
||||
<!-- 状态 switch:_cells.status 为 switch 渲染器输出的原生 checkbox,
|
||||
容器 layui-form 祖先 + 挂载后 form.render 美化;切换经页面级
|
||||
switch 委托提交 modify 接口,失败自动回滚重渲 -->
|
||||
<div class="usercard-status" v-html="d._cells.status"></div>
|
||||
<!-- 创建时间与手机:无渲染器的纯文本列不进 _cells(值为空串),
|
||||
直接取原始行字段插值输出(模型获取器已将 create_time 格式化) -->
|
||||
<div class="usercard-meta">
|
||||
<span class="usercard-time"><i class="layui-icon layui-icon-time"></i> {{ d.create_time }}</span>
|
||||
<span v-if="d.phone" class="usercard-phone"><i class="layui-icon layui-icon-cellphone"></i> {{ d.phone }}</span>
|
||||
</div>
|
||||
<!-- 操作按钮组:_operatHtml 为操作列渲染产物(编辑/设置密码/删除,
|
||||
已按 data-auth-* 裁剪权限),点击行为由框架页面级委托承接 -->
|
||||
<div class="usercard-operat" v-html="d._operatHtml"></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>
|
||||
|
||||
<!-- 名片形态页面级样式(示例边界:网格布局与头像圆形为本页自建,卡片基础
|
||||
观感——边框/白底/hover/选中描边/操作按钮排布——复用 public.css 的
|
||||
.ua-data-card 族,不修改公共样式) -->
|
||||
<!-- 名片形态页面级样式 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(280px, 1fr));
|
||||
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 {
|
||||
.usercard-avatar-box {
|
||||
position: relative;
|
||||
flex: none;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 1px solid #eee;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
.usercard-avatar-empty {
|
||||
.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: 24px;
|
||||
font-size: 22px;
|
||||
}
|
||||
.usercard-avatar-img {
|
||||
object-fit: cover;
|
||||
}
|
||||
.usercard-id {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.usercard-name {
|
||||
min-width: 0;
|
||||
margin-left: 10px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
.usercard-username {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
}
|
||||
.usercard-nickname {
|
||||
.usercard-account {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
line-height: 18px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
.usercard-status {
|
||||
margin-top: 8px;
|
||||
text-align: right;
|
||||
flex: none;
|
||||
margin-left: auto;
|
||||
}
|
||||
.usercard-meta {
|
||||
.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;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
gap: 6px;
|
||||
line-height: 20px;
|
||||
}
|
||||
.usercard-field-label {
|
||||
flex: none;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
.usercard-meta .layui-icon {
|
||||
.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 {
|
||||
|
||||
Reference in New Issue
Block a user