开发这样一个页面并不难,提高页面的可复用性从而节约时间成本,这应该是每位同事的必修课。
class为组件预设样式,放在文末展示
页面基础布局:
预设参数
interface State {
tableData: any[]; // 列表数据
loading: boolean; // 列表加载状态
searchParams: { // 筛选参数
keyword: string;
};
page: { // 页码配置
page: number;
size: number;
total: number;
};
}
const state = reactive<State>({
tableData: [],
loading: true,
searchParams: {
keyword: "",
},
page: {
page: 1,
size: 10,
total: 0,
},
});
完善搜索栏
预设方法:
// 搜索/获取列表数据
const searchListByParams = () => {
state.loading = true;
let newParams = JSON.parse(
JSON.stringify({
...state.searchParams,
...state.page,
})
);
delete newParams.total;
// 获取列表数据
// getList(newParams).then((res: any) => {
// });
state.loading = false;
};
// 重置表单
const reset = () => {
state.searchParams.keyword = "";
searchListByParams();
};
// 翻页
const handleCurrentChange = () => {
searchListByParams();
};
// 改变每页显示条数
const handleSizeChange = () => {
searchListByParams();
};
标签页配置:
// tab页
const tabsLabel = [
{ label: "登录日志", name: "0" },
{ label: "请求日志", name: "1" },
{ label: "执行日志", name: "2" },
{ label: "异常日志", name: "3" },
{ label: "数据接入日志", name: "4" },
{ label: "数据交换日志", name: "5" },
];
const logTabIndex = ref<string>("0");
const logTabChange = (tab: any) => {
// 切换tab下标
logTabIndex.value = tab.index;
searchListByParams();
};
标签页填充:
标签页和表格【表头】的关系,应当是一 一对应的,在设计表格时要考虑到这种情况
即:不同的tab页对应着不同的表格
同时要考虑数据回显的方式,后端返回的数据是直接展示的明文,还是需要到字典表查询的键名。
表格【表头】设计:
/**表头配置
* @prop 对应的字段
* @label 显示名称
* @propMap 根据返回值映射名称
* @colorMap 根据返回值映射字体颜色
* */
const columnLabel: any = [
// 登录日志
[
{ prop: 'xxx', label: '姓名' },
{ prop: 'xxx', label: '用户名' },
{ prop: 'xxx', label: '操作类型',
propMap: {
0: '登录',
1: '退出',
},
colorMap: {
0: '#33f',
1: '#3f3',
}
},
{ prop: 'xxx', label: '操作时间' },
{ prop: 'xxx', label: '操作模块' },
{ prop: 'xxx', label: '操作内容' },
{ prop: 'xxx', label: 'IP地址' },
],
// 请求日志
[
{ prop: 'xxx', label: '用户名称' },
{ prop: 'xxx', label: '请求接口' },
{ prop: 'xxx', label: '请求方式' },
{ prop: 'xxx', label: '请求路径' },
{ prop: 'xxx', label: '请求时间' },
{ prop: 'xxx', label: '返回状态' },
{ prop: 'xxx', label: 'IP地址' },
],
// 执行日志
[
],
// 异常日志
[
],
// 数据接入日志
[
],
// 数据交换日志
[
],
]
表头配置与表格组件结合:
<!-- 表格 -->
<el-table
v-loading="state.loading"
class="tb-list mis-margin-top"
:data="state.tableData"
highlight-current-row
height="calc(100% - 56px - 48px)"
>
<el-table-column
type="index"
label="序号"
width="80"
align="center"
/>
<el-table-column
v-for="(item, index) of columnLabel[logTabIndex]"
:key="logTabIndex + index"
:prop="item.prop"
:label="item.label"
align="center"
show-overflow-tooltip
>
<template #default="{ row }">
<span :style="{
color: item.colorMap ? item.colorMap[row[item.prop]] : ''
}">
{{ item.propMap ? item.propMap[row[item.prop]] : row[item.prop] }}
</span>
</template>
</el-table-column>
</el-table>
演示效果:
需要操作项怎么办,加
表格设计:
可以在这个基础上加判断条件,给按钮置灰、禁用等
演示效果:
登录日志有,请求日志无,需要就加,不需要不加
同理,Switch 切换等元素,也可以按需配置
当我们要添加某些元素时,一定要思路清晰。如果是添加一整列,那就是表头相关,这样配置出来的元素都是整列整列的
如果你要配置的元素不包含整列,就不宜放在表头文件中配置
最后加上分页组件,这个模板就开发好了。
我是一个非常喜欢使用循环的人,本文表达的重点在表头配置上,对于一些样式相同的组件,我都会采用表头循环的方式来做。
开发中涉及的数十种设备, 都是同一个组件,用不同的表头文件来回显数据
我们看一段高度组件化的表头代码
getList(newParams).then((res: any) => {
if (!res.data) return;
// 根据专项索引设置表头,在../config/dialogBasic.js 中配置表头
// 专项名称为heat时,自动引入表头 【deviceInfo + 专项名称】,显示供热专项的设备信息
baseData.value = basiLabel["deviceInfo" + props.bizTypeCode]?.map(
(v: any) => {
return {
...v,
val: v.propList
? v.propList.map((itm: any) => (itm = res.data[itm]))
: res.data[v.prop] !== null
? res.data[v.prop]
: "",
};
}
);
这部分就不解释了, 万变不离其宗
这样设计组件有一个好处,方便同事之间交接代码。所有的页面都可以是同一套模板,同事看了你一个页面的代码就明白了你所有的页面,在多人团队中显得尤为重要。
下面是预设css样式,仅供参考:
.mis-pager-contianer {
position: relative;
height: 100%;
.el-main {
--el-main-padding: 0 !important;
padding-left: 16px;
padding-right: 20px;
}
.input-search {
padding-top: 15px;
:deep(.el-input__wrapper) {
border-radius: 15px;
}
}
.playVdeo {
width: 100%;
height: 95%;
align-items: center;
border-radius: 0.15625rem;
box-shadow: var(--el-box-shadow-light);
.v-title {
width: 100%;
z-index: 121;
text-align: center;
background: rgba(0, 0, 0, 0.39);
margin-bottom: 20px;
padding: 0 20px;
display: flex;
justify-content: space-between;
font-size: 16px;
font-family: PingFang SC;
color: #ffffff;
}
.v-box {
width: 100%;
height: 100%;
}
}
.no-video {
width: 100%;
height: 100%;
background: #000;
color: #f00;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
border-radius: 3px;
}
// 树结构筛选
.filter-tree {
padding-top: 17px;
margin-right: 10px;
height: 100%;
font-size: 14px;
font-family: PingFang SC;
font-weight: 500;
color: #16171c;
.flag-def {
width: 38px;
height: 20px;
background: rgba(56, 128, 234, 0.12);
border: 1px solid #3880ea;
border-radius: 2px;
font-size: 12px;
font-family: PingFang SC;
font-weight: 500;
line-height: 20px;
text-align: center;
color: #3880ea;
margin: 0 8px;
}
.font {
position: absolute;
right: 0;
}
// 设置节点高度
:deep(.el-tree-node__content) {
height: 40px;
}
:deep(.el-tree-node__content:hover) {
background-color: #eaf2ff;
}
}
// 四宫格
.four-box {
/* grid是二维布局,行 * 列 的网格 */
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
min-height: 550px;
.four-item {
width: 43%;
height: 270px;
margin: 5px 15px;
border: 1px solid rgb(161, 153, 161);
position: relative;
.item-name {
width: 100%;
height: 30px;
padding: 0 10px;
color: #2d7dff;
position: absolute;
bottom: 0px;
background: rgba(0, 0, 0, 0.39);
display: flex;
justify-content: space-between;
font-size: 16px;
font-family: PingFang SC;
font-weight: bold;
line-height: 30px;
color: #ffffff;
> div {
color: #00b89b;
}
}
}
}
// 九宫格
.nine-box {
/* grid是二维布局,行 * 列 的网格 */
display: flex;
flex-direction: row;
flex-wrap: wrap;
width: 100%;
min-height: 550px;
border: #3880ea 1px solid;
.nine-item {
width: 33%;
height: 230px;
border: 1px solid plum;
}
}
}
如有纰漏欢迎指摘