Vue3中使用element-plus封装表单列表组件,一行代码实现一个页面
1。QhTable子组件
<template>
<div class="qh-base-pages" v-loading="loading">
<div class="qh-table-header" v-if="defaultForm.length > 0">
<div class="l">
<slot name="left"></slot>
</div>
<div class="default-form">
<form>
<div class="cell" v-for="(item, index) in defaultForm" :key="index">
<Input
v-if="item.type == 'input'"
:item="item"
:picon="item.picon == 'search' ? Search : ''"
@returnItem="returnItem"
/>
<SelectList
v-if="item.type == 'select'"
:item="item"
@returnItem="returnItem"
/>
</div>
<div class="cell">
<el-button @click="exportExcel">导出</el-button>
</div>
<div class="cell">
<el-button @click="handleReset">重置</el-button>
</div>
</form>
</div>
</div>
<div class="qh-table">
<div class="qh-table-wrapper">
<el-table
:data="data?.list"
stripe
:height="data.height"
:max-height="data.height || '72vh'"
:show-overflow-tooltip="{ placement: 'left-end' }"
:tooltip-options="{
effect: 'dark',
placement: 'top',
showArrow: true,
}"
:row-class-name="tableRowClassName"
>
<template v-slot:empty>
<div class="qh-no-data">
<el-empty description="暂无数据" />
</div>
</template>
<!-- 循环列表数据 -->
<el-table-column
v-for="(col, index) in data?.cols"
:key="index"
:prop="col.prop"
:label="col.label"
:width="col.width"
:fixed="col.fixed"
:align="col.align || 'center'"
:sortable="col.sortable"
>
<!-- 自定义头部内容 -->
<template #header>
<div v-if="col.headerSlot" class="sub_header_tr_cell">
{{ col.label }}
<div class="sub_header_tr_title">
{{ col.headerSlot.title }}
</div>
</div>
</template>
<template #default="scope">
<!-- // col格子按钮 tools -->
<div v-if="col.tools">
<div v-if="col.tools.name">
<template v-if="col.tools.isProp">
<span
v-if="col.tools.chind[scope.row[col.tools.isProp]] == '-'"
>{{ col.tools.chind[scope.row[col.tools.isProp]] }}</span
>
<span
v-else
class="link"
@click="openRowDialog(col.tools.prop, scope.row)"
>{{ col.tools.chind[scope.row[col.tools.isProp]] }}</span
>
</template>
<span
v-else
class="link"
@click="
openRowDialog(
col.tools.prop,
scope.row[col.tools.prop]
? scope.row[col.tools.prop]
: scope.row
)
"
>{{ col.tools.name }}</span
>
</div>
<span
v-else
class="link"
@click="openRowDialog(scope.row, col.prop)"
>{{ scope.row[col.prop] }}</span
>
</div>
<!-- // 是否开启switch切换按钮 -->
<div v-if="col.switch">
<el-switch
v-model="scope.row[col.prop]"
@change="changeSwitch(scope.row[col.prop], scope.row)"
/>
</div>
<!-- // 展示图片 isPic isVideo -->
<div
v-if="scope.row[col.prop] && (col.isPic || col.isVideo)"
class="isPic"
:class="{ isVideo: 'isVideo' }"
>
<el-icon
v-if="col.isVideo"
@click="openAvatarPopup(scope.row[col.prop], 'video')"
><CaretRight
/></el-icon>
<el-avatar
shape="square"
:size="36"
fit="cover"
@click="openAvatarPopup(scope.row[col.prop])"
:src="scope.row[col.prop]"
/>
</div>
<!-- // 多颜色展示 colors -->
<div v-if="col.colors">
<span
v-for="colorEle in col.colors"
:key="colorEle.value"
:style="{
color:
colorEle.value == scope.row[col.prop] && colorEle.color,
}"
>
{{
colorEle.value === scope.row[col.prop] ? colorEle.name : ""
}}
</span>
</div>
<!-- // 单位 units -->
<div v-if="col.units" class="units">
<template v-if="scope.row[col.prop] > 0">
<span v-if="col.units.dir == 'left'">{{
col.units.name
}}</span
>{{
col.units.value
? (scope.row[col.prop] / col.units.value).toFixed(2)
: scope.row[col.prop]
}}<span v-if="col.units.dir == 'right'">{{
col.units.prop ? scope.row[col.units.prop] : col.units.name
}}</span>
</template>
<template v-else>-</template>
</div>
<!-- // 换算 mat -->
<div class="mat" v-if="col.mat && scope.row[col.prop] > '0'">
<span v-if="col.mat.dir == 'left'">{{ col.mat.name }}</span
>{{ (scope.row[col.prop] / col.mat.value).toFixed(2)
}}<span v-if="col.mat.dir == 'right'">{{ col.mat.name }}</span>
</div>
<!-- // 多字段显示 -->
<div v-if="col.propElse">
<div class="units">
{{ scope.row[col.prop] ? scope.row[col.prop] : "-" }}
</div>
<div
class="mat"
style="color: #273c62"
v-if="scope.row[col.propElse]"
>
{{ scope.row[col.propElse] || "-" }}
</div>
</div>
<!-- // 默认展示 base -->
<span
v-if="
!col.tools &&
!col.isPic &&
!col.isVideo &&
!col.colors &&
!col.units &&
!col.switch &&
!col.propElse
"
>
{{ scope.row[col.prop] === "" ? "-" : scope.row[col.prop] }}
</span>
</template>
</el-table-column>
</el-table>
</div>
<div class="qh-pagination">
<div class="total">
当前共有 <span class="num">{{ props.data.total || "0" }}</span> 条数据
</div>
<el-pagination
v-model:current-page="currentPage"
background
v-model:page-size="pageSize"
:page-sizes="[20, 50, 100, 200]"
layout="sizes, prev, pager, next"
:total="props.data.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<QhPopup
:visible="avatarVisible"
:data="popupData"
@close="closePopup"
></QhPopup>
</div>
</template>
<script setup>
import { ref, reactive } from "vue";
import { post, get } from "@/utils/http";
import table2Excel from "js-table2excel";
import { Input, SelectList, QhPopup } from "@/components";
import { Search, CaretRight } from "@element-plus/icons-vue";
import { ElLoading } from "element-plus";
//给父组件上报状态
const emit = defineEmits([
"updaTable",
"updaDefaultForm",
"openRowDialog",
"updataPostList",
]);
const props = defineProps({
data: {
type: Object,
default: {},
},
loading: {
type: Boolean,
default: false,
},
defaultForm: {
type: Array,
default: [],
},
Sinit: {
type: Object,
default: {},
},
excelInit: {
type: Object,
default: {},
},
});
const Tdata = props?.data;
const Sinit = props?.Sinit;
const excelInit = props?.excelInit;
// 重置表单
const handleReset = () => {
const defaultForm = props?.defaultForm;
for (const key in defaultForm) {
defaultForm[key].value = "";
}
emit("updaDefaultForm", defaultForm);
};
// 点击cel链接
const openRowDialog = (rowVal, prop) => {
emit("openRowDialog", rowVal, prop);
};
// changeSwitch切换
const changeSwitch = (v, row) => {
emit("updataPostList", v, row);
};
// 当前页码
const currentPage = ref(Sinit.page || 1);
const pageSize = ref(Sinit.size || 20);
const returnItem = (data) => {
emit("updaDefaultForm", props?.defaultForm);
};
// 页码改变
const handleSizeChange = (val) => {
Sinit.size = val;
emit("updaTable", Sinit);
};
const handleCurrentChange = (val) => {
Sinit.page = val;
emit("updaTable", Sinit);
};
// 导出exel表格
const exportExcel = () => {
const loading = ElLoading.service({
lock: true,
text: "导出中请稍等...",
background: "rgba(0, 0, 0, 0.6)",
});
post(excelInit.apiUrl, excelInit.data).then((res) => {
if (res.code == 0) {
const lists = res?.data?.list;
let list = JSON.stringify(lists); // 用定义的数据
list = formatExportData(JSON.parse(list));
table2Excel(
excelInit.exportCols,
list,
excelInit.name + new Date().getTime()
);
loading.close();
} else {
loading.close();
}
});
};
const formatExportData = (list) => {
// 处理特殊字段
list.forEach((item) => {
excelInit.formatColumns.forEach((i) => {
item[i.prop] = i.option[item[i.prop]];
});
for (let key in item) {
if (!item[key] && item[key] == null) {
item[key] = "";
}
}
});
return list;
};
</script>
<style lang="scss" scoped>
.c3 {
color: rgba(39, 60, 98, 0.5);
}
.qh-table {
position: relative;
:deep(.el-table th .cell) {
display: flex;
align-items: center;
justify-content: center;
}
.link {
color: #0084ff;
text-decoration-line: underline;
cursor: pointer;
}
}
.default-form form {
display: flex;
justify-content: flex-end;
gap: 10px;
}
:deep(.el-popper.is-dark) {
max-width: 300px;
}
</style>
2。页面组件(list.vue)
<template> <QhTable :defaultForm="defaultFormData" :Sinit="searchInit" :loading="loading" :data="defaultData" :excelInit="excelInit" @updaTable="getUpdaTable" @updaDefaultForm="updaDefaultForm" /> </template> <script setup> import { ref, reactive } from "vue"; import { QhTable } from "@/components"; </script> <style lang="scss" scoped></style>
参数解释:
loading
|
表单加载中 |
const loading = ref(false);
|
|
Sinit
|
搜索字段 (Object) |
const searchInit = ref({
page: 1, // 页码
size: 20, // 数量
w_time: defaultFormData[0].value, //搜索字段1
agent_id: defaultFormData[1].value, //搜索字段2
});
|
|
defaultFormData |
搜索配置 (数组) |
const defaultFormData = reactive([
{
type: "select", //下拉选择
label: "时间周期", //名称
name: "w_time", // 字段
value: "", // 默认值
placeholder: "全部",
options: cofigStore.timePeriod, // 下拉数据配置
},
{
type: "input", // 输入框
placeholder: "请输入用户ID",
name: "user_id", // 字段
value: "",
picon: "search",
width: 210,
},
]);
|
|
defaultData |
表格配置 (对象) |
const defaultData = reactive({
total: 0, // 数据总数
cols: cols, // 表格cols
list: [], // 接口数据列表
});
|
cols =[
{ prop: "regions", label: "国家地区", width: 150 },
]
|
excelInit |
导出excel配置 |
const excelInit = ref({
apiUrl: "/v1/product_data/pay_data", //导出接口api
name: `name-`, // 导出文档名称+时间戳
exportCols: exportColsConfig,
formatColumns: formatColumns,
data: {},
});
|
exportColsConfig=[ //表单配置
{ key: "stats_date", title: "统计日期", type: "text" },
]// 特殊字符处理
formatColumns=[
{
prop: "pay_status",
option: {
1: "支付中",
2: "支付成功",
3: "支付失败",
},
},
|
getUpdaTable |
分页更新 |
const getUpdaTable = (data) => {
searchInit.value = data;
excelInit.value.data = { ...searchInit.value, export: 1 };
getTableList(searchInit.value);
};
|
|
updaDefaultForm |
搜索字符更新 |
// 获取顶部输入框数据
const updaDefaultForm = (data) => {
for (const item of data) {
searchInit.value[item.name] = item.value;
}
excelInit.value.data = { ...searchInit.value, export: 1 };
getTableList(searchInit.value);
};
|

浙公网安备 33010602011771号