update
This commit is contained in:
parent
8a102fc996
commit
33b6ea9551
494
src/components/ProTable/index.vue
Normal file
494
src/components/ProTable/index.vue
Normal file
@ -0,0 +1,494 @@
|
||||
<template>
|
||||
<div class="page-box">
|
||||
<!-- 搜索选项 -->
|
||||
|
||||
<el-form
|
||||
v-if="!!search"
|
||||
class="search"
|
||||
:model="searchModel"
|
||||
:inline="true"
|
||||
label-position="left"
|
||||
:label-width="search.labelWidth"
|
||||
ref="searchForm"
|
||||
>
|
||||
<el-form-item
|
||||
v-for="item in search.fields"
|
||||
:key="item.name"
|
||||
:label="item.label"
|
||||
:prop="item.name"
|
||||
>
|
||||
<el-select
|
||||
v-if="item.type === 'select'"
|
||||
v-model="searchModel[item.name]"
|
||||
:filterable="!!item.filterable"
|
||||
:multiple="!!item.multiple"
|
||||
clearable
|
||||
:placeholder="`请选择${item.label}`"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
>
|
||||
<el-option
|
||||
v-for="option of item.options"
|
||||
:key="option.value"
|
||||
:label="option.name"
|
||||
:value="option.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-radio-group
|
||||
v-model="searchModel[item.name]"
|
||||
v-else-if="item.type === 'radio'"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
>
|
||||
<el-radio
|
||||
v-for="option of item.options"
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>{{ option.name }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
<el-radio-group
|
||||
v-model="searchModel[item.name]"
|
||||
v-else-if="item.type === 'radio-button'"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
>
|
||||
<el-radio-button
|
||||
v-for="option of item.options"
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>{{ option.name }}</el-radio-button
|
||||
>
|
||||
</el-radio-group>
|
||||
<el-checkbox-group
|
||||
v-model="searchModel[item.name]"
|
||||
v-else-if="item.type === 'checkbox'"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
>
|
||||
<el-checkbox
|
||||
v-for="option of item.options"
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>{{ option.name }}</el-checkbox
|
||||
>
|
||||
</el-checkbox-group>
|
||||
<el-checkbox-group
|
||||
v-model="searchModel[item.name]"
|
||||
v-else-if="item.type === 'checkbox-button'"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
>
|
||||
<el-checkbox-button
|
||||
v-for="option of item.options"
|
||||
:key="option.value"
|
||||
:label="option.value"
|
||||
>{{ option.name }}</el-checkbox-button
|
||||
>
|
||||
</el-checkbox-group>
|
||||
<el-date-picker
|
||||
v-else-if="item.type === 'date'"
|
||||
v-model="searchModel[item.name]"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
clearable
|
||||
@change="handleDateChange($event, item, 'YYYY-MM-DD')"
|
||||
:placeholder="`请选择${item.label}`"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-date-picker
|
||||
v-else-if="item.type === 'datetime'"
|
||||
v-model="searchModel[item.name]"
|
||||
type="datetime"
|
||||
clearable
|
||||
@change="handleDateChange($event, item, 'YYYY-MM-DD HH:mm:ss')"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
:placeholder="`请选择${item.label}`"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-date-picker
|
||||
v-else-if="item.type === 'daterange'"
|
||||
v-model="searchModel[item.name]"
|
||||
type="daterange"
|
||||
format="YYYY-MM-DD"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
clearable
|
||||
@change="handleRangeChange($event, item, 'YYYY-MM-DD')"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-date-picker
|
||||
v-else-if="item.type === 'datetimerange'"
|
||||
v-model="searchModel[item.name]"
|
||||
type="datetimerange"
|
||||
format="YYYY-MM-DD HH:mm:ss"
|
||||
range-separator="-"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
clearable
|
||||
@change="handleRangeChange($event, item, 'YYYY-MM-DD HH:mm:ss')"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-date-picker>
|
||||
<el-input-number
|
||||
v-else-if="item.type === 'number'"
|
||||
v-model="searchModel[item.name]"
|
||||
:placeholder="`请输入${item.label}`"
|
||||
controls-position="right"
|
||||
:min="item.min"
|
||||
:max="item.max"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="item.type === 'textarea'"
|
||||
type="textarea"
|
||||
clearable
|
||||
v-model="searchModel[item.name]"
|
||||
:placeholder="`请输入${item.label}`"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-input>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="searchModel[item.name]"
|
||||
clearable
|
||||
:placeholder="`请输入${item.label}`"
|
||||
:style="{ width: search.inputWidth, ...item.style }"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item class="search-btn">
|
||||
<el-button type="primary" icon="el-icon-search" @click="handleSearch"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button @click="handleReset" icon="el-icon-refresh-right"
|
||||
>重置</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- title 和 工具栏 -->
|
||||
<div class="head" v-if="!hideTitleBar">
|
||||
<slot name="title">
|
||||
<span class="title">{{ title }}</span>
|
||||
</slot>
|
||||
<div class="toolbar">
|
||||
<slot name="toolbar"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<!-- table表格栏 -->
|
||||
<div class="table">
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableData"
|
||||
:row-key="rowKey"
|
||||
tooltip-effect="dark"
|
||||
stripe
|
||||
:border="border"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
v-for="item in columns"
|
||||
:key="item.label"
|
||||
:filter-method="item.filters && filterHandler"
|
||||
show-overflow-tooltip
|
||||
v-bind="item"
|
||||
>
|
||||
<template #header="scope" v-if="!!item.labelSlot">
|
||||
<slot :name="item.labelSlot" v-bind="scope"></slot>
|
||||
</template>
|
||||
<template #default="scope" v-if="!!item.tdSlot">
|
||||
<slot :name="item.tdSlot" v-bind="scope"></slot>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<!-- 分页 -->
|
||||
<el-pagination
|
||||
v-if="paginationConfig.show && total > 0"
|
||||
class="pagination"
|
||||
:style="paginationConfig.style"
|
||||
@size-change="handleSizeChange"
|
||||
v-model:currentPage="pageNum"
|
||||
@current-change="handleCurrentChange"
|
||||
:page-sizes="paginationConfig.pageSizes"
|
||||
v-model:pageSize="pageSize"
|
||||
:layout="paginationConfig.layout"
|
||||
:total="total"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { defineComponent, reactive, toRefs, onBeforeMount } from "vue";
|
||||
const formatDate = (date, format) => {
|
||||
var obj = {
|
||||
"M+": date.getMonth() + 1,
|
||||
"D+": date.getDate(),
|
||||
"H+": date.getHours(),
|
||||
"m+": date.getMinutes(),
|
||||
"s+": date.getSeconds(),
|
||||
"q+": Math.floor((date.getMonth() + 3) / 3),
|
||||
"S+": date.getMilliseconds(),
|
||||
};
|
||||
if (/(y+)/i.test(format)) {
|
||||
format = format.replace(
|
||||
RegExp.$1,
|
||||
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
|
||||
);
|
||||
}
|
||||
for (var k in obj) {
|
||||
if (new RegExp("(" + k + ")").test(format)) {
|
||||
format = format.replace(
|
||||
RegExp.$1,
|
||||
RegExp.$1.length == 1
|
||||
? obj[k]
|
||||
: ("00" + obj[k]).substr(("" + obj[k]).length)
|
||||
);
|
||||
}
|
||||
}
|
||||
return format;
|
||||
};
|
||||
const getSearchModel = (search) => {
|
||||
const searchModel = {};
|
||||
if (search && search.fields) {
|
||||
search.fields.forEach((item) => {
|
||||
switch (item.type) {
|
||||
case "checkbox":
|
||||
case "checkbox-button":
|
||||
searchModel[item.name] = [];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (item.defaultValue !== undefined) {
|
||||
searchModel[item.name] = item.defaultValue;
|
||||
// 日期范围和时间范围真实变量默认值
|
||||
if (
|
||||
(item.type === "daterange" || item.type === "datetimerange") &&
|
||||
!!item.trueNames &&
|
||||
Array.isArray(item.defaultValue)
|
||||
) {
|
||||
item.defaultValue.forEach((val, index) => {
|
||||
searchModel[item.trueNames[index]] = val;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return searchModel;
|
||||
};
|
||||
export default defineComponent({
|
||||
props: {
|
||||
// 请求数据的方法
|
||||
request: {
|
||||
type: Function,
|
||||
},
|
||||
// 表格标题
|
||||
title: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
// 是否隐藏标题栏
|
||||
hideTitleBar: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 搜索表单配置,false表示不显示搜索表单
|
||||
search: {
|
||||
type: [Boolean, Object],
|
||||
default: false,
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 表头配置
|
||||
columns: {
|
||||
type: Array,
|
||||
default: function (params) {
|
||||
return [];
|
||||
},
|
||||
},
|
||||
// 行数据的Key,同elementUI的table组件的row-key
|
||||
rowKey: {
|
||||
type: String,
|
||||
default: "id",
|
||||
},
|
||||
// 分页配置,false表示不显示分页
|
||||
pagination: {
|
||||
type: [Boolean, Object],
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
setup(props, { emit }) {
|
||||
// 优化搜索字段,
|
||||
// 1、如果搜索配置有transform处理函数,执行transform
|
||||
// 2、删除日期范围默认的name字段
|
||||
const optimizeFields = (search) => {
|
||||
const searchModel = JSON.parse(JSON.stringify(state.searchModel));
|
||||
if (search && search.fields) {
|
||||
search.fields.forEach((item) => {
|
||||
if (!searchModel.hasOwnProperty(item.name)) {
|
||||
return;
|
||||
}
|
||||
if (!!item.transform) {
|
||||
searchModel[item.name] = item.transform(searchModel[item.name]);
|
||||
}
|
||||
if (
|
||||
(item.type === "daterange" || item.type === "datetimerange") &&
|
||||
!!item.trueNames
|
||||
) {
|
||||
delete searchModel[item.name];
|
||||
}
|
||||
});
|
||||
}
|
||||
return searchModel;
|
||||
};
|
||||
|
||||
// 请求列表数据
|
||||
const getTableData = async () => {
|
||||
state.loading = true;
|
||||
const searchModel = optimizeFields(props.search);
|
||||
const { data, total } = await props.request({
|
||||
pageNum: state.pageNum,
|
||||
pageSize: state.pageSize,
|
||||
...searchModel,
|
||||
});
|
||||
state.loading = false;
|
||||
state.tableData = data;
|
||||
state.total = total;
|
||||
};
|
||||
|
||||
onBeforeMount(() => {
|
||||
getTableData();
|
||||
});
|
||||
|
||||
const state = reactive({
|
||||
searchModel: getSearchModel(props.search),
|
||||
loading: false,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
pageNum: 1,
|
||||
pageSize: (!!props.pagination && props.pagination.pageSize) || 10,
|
||||
paginationConfig: {
|
||||
show: false,
|
||||
},
|
||||
// 搜索
|
||||
handleSearch() {
|
||||
state.pageNum = 1;
|
||||
getTableData();
|
||||
},
|
||||
// 重置函数
|
||||
handleReset() {
|
||||
if (JSON.stringify(state.searchModel) === "{}") {
|
||||
return;
|
||||
}
|
||||
state.pageNum = 1;
|
||||
state.searchModel = getSearchModel(props.search);
|
||||
getTableData();
|
||||
},
|
||||
// 刷新
|
||||
refresh() {
|
||||
getTableData();
|
||||
},
|
||||
|
||||
// 当前页变化
|
||||
handleCurrentChange(page) {
|
||||
getTableData();
|
||||
},
|
||||
// 改变每页size数量
|
||||
handleSizeChange(value) {
|
||||
state.pageNum = 1;
|
||||
getTableData();
|
||||
},
|
||||
// 全选
|
||||
handleSelectionChange(arr) {
|
||||
emit("selectionChange", arr);
|
||||
},
|
||||
// 过滤方法
|
||||
filterHandler(value, row, column) {
|
||||
const property = column["property"];
|
||||
return row[property] === value;
|
||||
},
|
||||
// 日期范围
|
||||
handleDateChange(date, item, format) {
|
||||
state.searchModel[item.name] = !!date ? formatDate(date, format) : "";
|
||||
},
|
||||
handleRangeChange(date, item, format) {
|
||||
const arr = !!date && date.map((d) => formatDate(d, format));
|
||||
state.searchModel[item.name] = !!arr ? arr : [];
|
||||
|
||||
if (!item.trueNames) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!!arr) {
|
||||
arr.forEach((val, index) => {
|
||||
state.searchModel[item.trueNames[index]] = val;
|
||||
});
|
||||
} else {
|
||||
item.trueNames.forEach((key) => {
|
||||
delete state.searchModel[key];
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
if (typeof props.pagination === "object") {
|
||||
const { layout, pageSizes, style } = props.pagination;
|
||||
state.paginationConfig = {
|
||||
show: true,
|
||||
layout: layout || "total, sizes, prev, pager, next, jumper",
|
||||
pageSizes: pageSizes || [10, 20, 30, 40, 50, 100],
|
||||
style: style || {},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.page-box {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
.search {
|
||||
padding: 20px 20px 0;
|
||||
background: #fff;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.search-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
::v-deep(.el-input-number .el-input__inner) {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 20px 0;
|
||||
background: #fff;
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.table {
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
::v-deep(th) {
|
||||
background: #f6f6f6;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
}
|
||||
.pagination {
|
||||
padding: 0 20px 20px;
|
||||
background: #fff;
|
||||
text-align: right;
|
||||
:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export { default as SvgIcon } from "@/components/SvgIcon/index.vue";
|
||||
export { default as SvgIcon } from "@/components/SvgIcon/index.vue";
|
||||
export { default as ProTable } from "@/components/ProTable/index.vue";
|
||||
@ -28,8 +28,5 @@ Object.entries(Components).forEach(([key, component]) => {
|
||||
app.component(key, component)
|
||||
})
|
||||
|
||||
// 引入vue3-pro-table
|
||||
import Vue3ProTable from "vue3-pro-table";
|
||||
app.use(Vue3ProTable);
|
||||
|
||||
app.use(ElementPlus, { locale }).use(store).use(router).mount('#app')
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<vue3-pro-table
|
||||
<pro-table
|
||||
ref="table"
|
||||
title="列表"
|
||||
:request="getList"
|
||||
@ -28,7 +28,7 @@
|
||||
<el-button size="mini" type="primary">编辑</el-button>
|
||||
<el-button size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</vue3-pro-table>
|
||||
</pro-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user