二次封装table组件有两种封装途径
1.先定义好位置,用插槽解决
<template>
<div class="maincontent">
<!-- 筛选条件 -->
<div class="main-topSearch">
<slot name="search"></slot>
</div>
<div class="main-center">
<!-- 常用按钮 -->
<div class="btns">
<slot name="btns"></slot>
</div>
<!-- 正文 -->
<div class="maincenter-table">
<slot name="tabs"></slot>
<slot name="table"></slot>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
@import "./index.scss"
</style>
样式
$main-color: #07BFB4;
// ---------------------------------------------只写关于通用页面的样式-----------------------------------------
.maincontent {
.main-topSearch {
display: block;
background: #fff;
padding: 20px 17px 0px;
}
// 表格
::v-deep .el-tabs--border-card {
-webkit-box-shadow: none;
box-shadow: none
}
.main-center {
background-color: white;
border-radius: 2px;
margin-top: 10px;
padding: 0px 20px 10px 20px;
.maincenter-top {
display: flex;
justify-content: space-between;
padding-right: 0px;
padding-left: 20px;
padding-top: 5px;
padding-bottom: 3px;
.title {
font-size: 14px;
color: #303133;
line-height: 20px;
}
.rightbutton {
width: 80px;
height: 40px
}
.sortOne {
padding: 0px 0px 7px 0px;
border-bottom: none;
color: #999;
cursor: pointer; //鼠标手
font-size: 14px;
font-weight: 500;
}
.sortOneSelected {
padding: 0px 0px 7px 0px;
border-bottom: 3px solid $main-color;
color: #333;
cursor: pointer;
font-size: 14px;
font-weight: 500;
}
.sortTwo {
padding: 0px 0px 7px 0px;
margin-left: 30px;
color: #999;
border-bottom: none;
cursor: pointer;
font-size: 14px;
font-weight: 500;
}
.sortTwoSelected {
padding: 0px 0px 7px 0px;
margin-left: 30px;
color: #333;
border-bottom: 3px solid $main-color;
cursor: pointer;
font-size: 14px;
font-weight: 500;
}
}
.btns,
.filters {
display: flex ;
justify-content: space-between;
align-items: center;
padding: 20px 0px !important;
margin-bottom: 0px !important;
background: #fff;
align-items: center;
.btn{
font-weight: 400;
color: #07BFB4;
border: 1px solid #07BFB4;
border-radius: 2px;
::v-deep span{
display: flex;
align-items: center;
justify-content: center;
.icon{
width: 14px;
margin-right: 5px;
}
}
}
}
.maincenter-table {
::v-deep .el-tabs--border-card>.el-tabs__content {
padding: 20px;
}
::v-deep .el-tabs__header {
margin: 0 0 20px;
}
::v-deep .el-table--small {
border: 1px solid #DEE2E7;
}
::v-deep .el-tabs--card>.el-tabs__header .el-tabs__item.is-active {
border-bottom-color: white !important;
/* z-index: 99; */
background: white;
color: #07BFB4;
}
::v-deep .el-tabs--card>.el-tabs__header .el-tabs__item {
background: #F4F4F4;
color: #777777;
border-bottom: 1px solid white;
// border-left: 1px solid #E4E7ED;
}
//分页设置
.maincenter-bottom {
background-color: white;
height: 52px;
display: flex;
justify-content: space-between;
align-items: center;
.bottom_title {
margin-left: 20px;
color: #606266;
font-size: 13px;
line-height: 32px;
}
.bottonm-page {
margin-top: 20px;
margin-right: 20px;
margin-bottom: 10px;
}
}
}
.blank_tips {
height: 140px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.el-table--enable-row-hover .el-table__body tr:hover>td {
background-color: #f5f7fa !important;
}
}
}
页面使用
<template>
<div class="main_content">
<TablePage>
<template #search>
<el-form
label-position="right"
ref="form"
:model="formSearch"
label-width="100px"
>
<el-row :gutter="20" type="flex">
<el-col :span="20">
<el-row :gutter="20" type="flex">
<el-col :span="8">
<el-form-item label="用户名" class="mb0">
<el-input v-model="formSearch.nickname" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="注册成功时间" class="mb0">
<el-date-picker
type="daterange"
v-model="time"
style="width: 100%"
value-format="yyyy-MM-dd"
start-placeholder="开始日期"
end-placeholder="结束日期"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="项目地址:" class="mb0">
<div style="display: flex; width: 100%">
<el-select
v-model="formSearch.cityId"
placeholder="市/州"
class="formItem1"
clearable
@change="selectCity"
>
<el-option
v-for="item in locationCityCodeOptions"
:key="item.deptId"
:label="item.name"
:value="item.deptId"
/>
</el-select>
<div style="width: 10px"></div>
<el-select
v-model="formSearch.countyId"
placeholder="区/县"
class="formItem1"
clearable
>
<el-option
v-for="item in locationAreaCodeOptions"
:key="item.deptId"
:label="item.name"
:value="item.deptId"
/>
</el-select>
</div>
</el-form-item>
</el-col>
</el-row>
</el-col>
<el-col :span="4">
<el-button
type="primary"
@click="queryData(1)"
icon="el-icon-search"
>
筛选
</el-button>
<el-button @click="clearData" icon="el-icon-delete">
清空
</el-button>
</el-col>
</el-row>
</el-form>
</template>
<template #table>
<el-table
:data="tableData"
style="border: 1px solid #f4f4f4"
:header-cell-style="{ background: '#F4F4F4', color: '#000' }"
v-loading="loading"
>
<el-table-column
prop="nickname"
header-align="center"
align="center"
label="用户名称"
/>
<el-table-column
prop="cityName"
header-align="center"
align="center"
label="注册所在市/州"
/>
<el-table-column
prop="countyName"
header-align="center"
align="center"
label="注册所在区/县"
/>
<el-table-column
prop="registerTime"
header-align="center"
align="center"
label="注册成功时间"
/>
<el-table-column
prop="loginLastTime"
header-align="center"
align="center"
label="最后登录时间"
/>
<el-table-column
prop="productCount"
header-align="center"
align="center"
label="项目数量"
>
<template slot-scope="scope">
<el-popover placement="top-start" width="120" trigger="hover">
<div>
<p class="mt0"><b>项目数量(个)</b></p>
<p style="border-bottom: 1px solid black"></p>
<p class="flex-p">
<span>储备项目</span>
<span>{{ scope.row.reserveCount }}</span>
</p>
<p class="flex-p">
<span>在建项目</span>
<span>{{ scope.row.constructionCount }}</span>
</p>
<p class="flex-p mb0">
<span>运行项目</span>
<span>{{ scope.row.connectionCount }}</span>
</p>
</div>
<span style="cursor: pointer" slot="reference">{{
scope.row.productCount
}}</span>
</el-popover>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="scope">
<el-popconfirm title="确定重置密码吗?" style="margin-left: 10px" @confirm="handlePsWord(scope.row, scope.index)">
<el-button type="text" slot="reference"
class="tableButtonEdit">重置密码</el-button>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<div class="avue-crud__pagination">
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pagination.currentPage"
:page-sizes="pageSizes"
:page-size="pagination.size"
:total="total"
background
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</template>
</TablePage>
</div>
</template>
<script>
import TablePage from '@/components/TabePage/page.vue';
// import { queryCitiesAndAreas } from '@/api/project/submit/submit';
// import { getEnterpriseUserPage } from '@/api/admin/user';
// import {resetPassword } from '@/api/admin/user'
// import axios from '@/router/axios';
const pageSizes = [10, 20, 50, 100];
export default {
data() {
return {
formSearch: {
nickname: '',
cityId: '',
countyId: '',
registerEndTime: '',
registerStartTime: ''
},
time: '',
locationCityCodeOptions: [],
locationAreaCodeOptions: [],
pageSizes,
pagination: {
current: 1,
size: pageSizes[0],
},
tableData: [],
total: 0,
loading: false
};
},
components: {
TablePage
},
mounted() {
// console.log(axios.defaults.baseURL,'--------axios.defaults.baseURL')
// queryCitiesAndAreas()
// .then((res) => {
// const data = res.data.data;
// this.locationCityCodeOptions = data.filter((x) => x.level === 3);
// this.locationAreaCodeOptionsAll = data.filter((x) => x.level === 4);
// this.queryData();
// })
// .catch(() => {});
},
methods: {
handlePsWord(row, index) {
console.log('-----------row')
console.log(row)
// resetPassword({
// userId:row.userId
// })
// .then(() => {
// // this.role = []
// // this.getList(this.page)
// // done()
// this.$notify.success('修改成功')
// })
// .catch(() => {
// // loading()
// })
},
selectCity() {
const locationCityCode = this.formSearch.cityId;
if (locationCityCode) {
this.locationAreaCodeOptions = this.locationAreaCodeOptionsAll.filter(
(x) =>
x.parentId ===
this.locationCityCodeOptions.find(
(x) => x.deptId === locationCityCode
).deptId
);
this.formSearch.countyId = null;
} else {
this.locationAreaCodeOptions = [...this.locationAreaCodeOptionsAll];
}
},
queryData(current) {
this.loading = true;
current = current || this.pagination.current;
if (this.time && this.time.length !== 0) {
this.formSearch.registerStartTime = this.time[0];
this.formSearch.registerEndTime = this.time[1];
}
// getEnterpriseUserPage({
// ...this.formSearch,
// ...this.pagination,
// current
// }).then((res) => {
// const { records, total } = res.data.data;
// this.tableData = records;
// this.total = total;
// this.loading = false;
// });
},
sizeChangeHandle(val) {
this.pagination.size = val;
this.queryData();
},
currentChangeHandle(val) {
this.pagination.current = val;
this.queryData();
},
clearData() {
this.$refs.form.resetFields();
this.formSearch = {
nickname: '',
cityId: '',
countyId: '',
registerEndTime: '',
registerStartTime: ''
};
this.time = '';
this.queryData();
}
}
};
</script>
<style>
.flex-p {
display: flex;
justify-content: space-between;
}
</style>
展示效果
优缺点: 封装简单,页面利用插槽直接写入对应代码 每个页面都需要重复定义,比如分页
2. 全部封装起来,包括请求 分页器等
TableColumns.vue
<template>
<el-table-column
v-if="!curColumn.type"
:key="curColumn.prop || curColumn.label || curColumn.type"
:label="curColumn.label"
:prop="curColumn.prop"
:width="curColumn.width"
:min-width="curColumn.minWidth"
:column-key="curColumn.prop"
:fixed="curColumn.fixed"
:sortable="curColumn.sortable"
:sort-method="curColumn.sortMethod"
:sort-by="curColumn.sortBy"
:resizable="curColumn.resizable"
:show-overflow-tooltip="curColumn.showOverflowTooltip"
:align="curColumn.align"
:filters="curColumn.filter ? filters : undefined"
:filter-method="curColumn.filter ? doFilter : undefined"
:class-name="curColumn.className"
class="c--table-columns"
>
<!-- header -->
<template
v-if="curColumn.formItem"
slot="header"
>
<el-input
v-if="curColumn.formItem.type === 'input'"
v-model="filterProp"
:placeholder="curColumn.formItem.placeholder"
/>
<el-select
v-if="curColumn.formItem.type === 'selector'"
v-model="filterProp"
:placeholder="curColumn.formItem.placeholder"
>
<el-option
v-for="(option, idx) in curColumn.formItem.options"
:key="idx"
:value="option.value"
:label="option.label"
/>
</el-select>
<el-date-picker
v-if="curColumn.formItem.type === 'datePicker'"
v-model="filterProp"
:placeholder="curColumn.formItem.placeholder"
type="datetime"
/>
</template>
<!-- body -->
<template slot-scope="scope">
<!-- input 输入框 -->
<div v-if="curColumn.canEdit && scope.row.isEditing">
<el-input type="number" v-model="scope.row[scope.column.property]" ></el-input>
</div>
<!-- input 文本域 -->
<div v-else-if="curColumn.viewEdit ">
<el-input type="textarea" maxlength="40" show-word-limit v-model="scope.row[scope.column.property]"></el-input>
</div>
<!-- select 选择框 自评价-->
<div v-else-if="curColumn.selfEdit && scope.row.isEditing">
<el-select v-model="scope.row[scope.column.property]" @change="tableSelect(scope.row)" placeholder="">
<el-option
v-for="item in selFoptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<!-- 时间选择器 -->
<div v-else-if="curColumn.timeEdit && scope.row.isEditing">
<el-date-picker
v-model="scope.row[scope.column.property]"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期">
</el-date-picker>
</div>
<div
v-else-if="curColumn.buttons && curColumn.buttons.length > 0"
class="column-buttons-container"
>
<template v-for="(btn, index) in curColumn.buttons">
<div
:key="index"
:class="btn.wrapperClass"
class="column-btn-wrapper"
>
<el-button
v-if="typeof btn.name === 'function' ? btn.name(scope.row) : btn.name"
:class="typeof btn.class === 'function' ? btn.class(scope.row) : (btn.class || '')"
:size="btn.size || 'mini'"
:type="typeof btn.type === 'function' ? btn.type(scope.row) : (btn.type || 'default')"
:disabled="typeof btn.disabled === 'function' ? (btn.dishandler ? btnrowActions(scope.row, btn.dishandler) : btn.disabled(scope.row)) : (btn.disabled || false)"
class="column-btn-action"
@click.stop="rowActions(scope.row, btn.handler)"
>
{{ typeof btn.name === 'function' ? btn.name(scope.row) : btn.name }}
</el-button>
</div>
</template>
</div>
<div
v-else-if="curColumn.template"
v-html="renderTemplate(scope.row, scope.column, scope.$index)"
>
</div>
<div v-else>
{{ renderTemplate(scope.row, scope.column, scope.$index) }}
</div>
</template>
<TableColumns
v-for="(col, idx) in curColumn.children || []"
:key="idx"
:column="col"
@link="link"
/>
</el-table-column>
<el-table-column
v-else
:type="curColumn.type"
:label="curColumn.label"
:width="curColumn.width"
:min-width="curColumn.minWidth"
:column-key="curColumn.prop"
:fixed="curColumn.fixed"
:resizable="curColumn.resizable"
:show-overflow-tooltip="curColumn.showOverflowTooltip"
:align="curColumn.align"
:class-name="curColumn.className"
:reserve-selection="true"
ref = "cloums"
/>
<!-- row-key="selected" -->
</template>
<script>
import TableColumns from './TableColumns'
export default {
name: 'TableColumns',
components: {
TableColumns
},
props: {
column: {
type: Object,
default: () => {
return {}
}
}
},
data () {
return {
defaultColumn: {
label: undefined,
prop: undefined,
type: undefined,
width: undefined,
minWidth: undefined,
fixed: false,
sortable: false,
sortBy: undefined,
resizable: true,
showOverflowTooltip: true,
align: 'left',
className: '',
filter: false,
sortMethod (a, b) {
return 0
},
formatter (row, column, cellValue, index) {
return cellValue
}
},
filters: [],
filterObject: {},
filterProp: '',
timer: null,
selFoptions: [{
label: '未完成',
value: 3
},{
label: '已完成',
value: 4
},{
label: '未启动',
value: 1
}],
}
},
computed: {
curColumn () {
return Object.assign({}, this.defaultColumn, this.column)
}
},
watch: {
column: {
deep: true,
handler () {
this.filterObject = {}
}
},
filterObject: {
deep: true,
handler (val) {
let arr = []
for (let key in val) {
arr.push({
value: key,
text: val[key]
})
}
this.filters = arr
}
},
filterProp (val) {
clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.$emit('doFilter', val)
this.timer = null
}, 60)
}
},
mounted () {
},
methods: {
// 下路发生变化的时候
tableSelect (row) {
if (row.selfEvaluation === 1) {
row.taskScore = 0
} else if (row.selfEvaluation === 3) {
row.taskScore = 0.7 * (row.taskFactor * 100)
} else {
row.taskScore = row.taskFactor * 100
}
},
// 数据转换处理以及过滤列表初始化
initHandler (row, column, cellValue, index) {
let text = this.curColumn.formatter(row, column, cellValue, index) || cellValue
if (!this.filterObject[cellValue]) {
this.$set(this.filterObject, cellValue, text)
}
return text
},
// 表格内部过滤,不经过后台进行数据请求
doFilter (value, row, column) {
let prop = column['property']
return row[prop] + '' === value + ''
},
// 自定义模板渲染
renderTemplate (row, column, index) {
let col = this.curColumn
let val = row[column['property']] || ''
if (!col.template) return this.initHandler(row, column, val, index)
return typeof col.template === 'function'
? col.template(val)
: col.template.replace('{value}', this.initHandler(row, column, val, index))
},
// 自定义按钮事件
rowActions (row, handler = function () {}) {
handler.call(this, row)
},
btnrowActions(row, handler = function () {}) {
return handler.call(this, row)
},
// link用于配合layout组件进行跨组件通信
link (data) {
this.$emit('link', data)
}
}
}
</script>
<style scoped>
.column-buttons-container{
position: relative;
display: flex;
justify-content: space-between;
}
.column-btn-wrapper{
position: relative;
flex: 1;
}
.column-btn-action{
position: relative;
}
.long-column-btn-action{
position: relative;
width: 100%;
}
.el-date-editor.el-input, .el-date-editor.el-input__inner {
width: 150px;
}
</style>
TableCommon.vue
<template>
<div :id="curOption.id" class="table-common-wrapper c--table-common">
<div class="table-common-container">
<div
v-if="Object.keys(buttons).length > 0"
:class="{ 'btns-wrapper-absolute': buttons.absolute }"
class="btns-wrapper"
>
<el-button-group v-if="btnRefresh">
<el-button v-if="buttons.add && !isEditing" @click="add">
新增
</el-button>
<el-button v-if="buttons.update" @click="update"> 修改 </el-button>
<el-button v-if="buttons.planAdd" @click="planAdd"> 新增 </el-button>
<el-button v-if="buttons.planSave" @click="planSave">
保存
</el-button>
<el-button v-if="buttons.planSuv" @click="planSuv"> 提交 </el-button>
<el-button v-if="buttons.delete" @click="rowsDelete">
删除
</el-button>
<el-button v-if="buttons.export" @click="dataExport">
导出
</el-button>
<el-button v-if="buttons.import" @click="dataImport">
数据引入
</el-button>
<el-button v-if="buttons.edit" @click="doEdit()">
{{ isEditing ? "保存" : "编辑" }}
</el-button>
<el-button v-if="isEditing" @click="editCancel()">
取消编辑
</el-button>
<!--仅在数据引用时启用-->
<el-button v-if="isImporting" @click="selectedConfirm()">
确认
</el-button>
<el-button v-if="isImporting" @click="selectedClear()">
清除
</el-button>
<el-button v-if="isImporting" @click="selectedCancel()">
取消
</el-button>
<el-button
v-for="(btn, index) in buttons.custom"
:key="index"
:class="
typeof btn.classes === 'function' ? btn.classes(btn) : btn.classes
"
:save="elemRender(btn)"
@click="headerButtonHandler(btn)"
>
{{ typeof btn.name === "function" ? btn.name() : btn.name }}
</el-button>
</el-button-group>
</div>
<div v-if="curOption.title" class="table-common-title">
{{ curOption.title }}
</div>
<div
ref="tableCommonContent"
class="table-common-content"
@click="contextMenuClosed"
>
<el-table
v-if="showTable"
ref="tableCommon"
:data="curData"
:show-header="curOption.showHeader"
:height="curOption.height"
:max-height="curOption.maxHeight"
:fit="curOption.fit"
:show-summary="curOption.showSummary"
:border="curOption.border"
:size="curOption.size"
:row-class-name="curMethods.rowClassName"
:row-style="curMethods.rowStyle"
:cell-class-name="cellClassName"
:cell-style="curMethods.cellStyle"
:span-method="curMethods.spanMethod"
:summary-method="curMethods.summaryMethod"
@selection-change="selectHandler"
@select="select"
@select-all="selectAll"
@row-click="rowClick"
@row-contextmenu="contextMenu"
@row-dblclick="rowDoubleClick"
@cell-mouse-enter="cellEnter"
@cell-mouse-leave="cellLeave"
@current-change="selectCur"
>
<template v-for="(column, index) in columns">
<TableColumns
:key="index"
:column="Object.assign({}, { align: curOption.align }, column)"
@doFilter="doFilter"
@link="link"
/>
</template>
</el-table>
</div>
<div v-if="curOption.showPagination" class="pagination-wrapper">
<el-pagination
:current-page="curPagination.currentPage"
:page-sizes="curPagination.pageSizes"
:page-size="curPagination.pageSize"
:pager-count="curPagination.count"
:total="curPagination.total"
layout="total, sizes, prev, pager, next, jumper"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<el-card
v-if="curOption.contextMenu"
v-show="showContextMenu"
ref="cmenu"
:style="{ left: position.x + 'px', top: position.y + 'px' }"
class="context-menu-wrapper"
>
<div slot="header" class="context-menu-header">
<span>功能菜单</span>
<i
class="el-icon-close context-menu-close"
@click="contextMenuClosed"
/>
</div>
<div class="context-menu-list">
<el-button
:disabled="!buttons.delete"
size="small"
class="context-menu-btn"
@click="contextMenuHandler(rowsDelete)"
>
删除
</el-button>
</div>
<div class="context-menu-list">
<el-button
:disabled="!buttons.edit"
size="small"
class="context-menu-btn"
@click="contextMenuHandler(doEdit)"
>
{{ isEditing ? "保存" : "编辑" }}
</el-button>
</div>
<div class="context-menu-list">
<el-button
:disabled="!buttons.update"
size="small"
class="context-menu-btn"
@click="contextMenuHandler(update)"
>
修改
</el-button>
</div>
<div
v-for="(item, index) in curOption.contextMenu"
:key="index"
class="context-menu-list"
>
<el-button
size="small"
class="context-menu-btn"
@click="contextMenuHandler(item.handler)"
>
{{ item.name }}
</el-button>
</div>
</el-card>
</div>
</template>
<script>
import TableColumns from "./TableColumns";
import request from "@/util/request";
export default {
name: "TableCommon",
components: {
TableColumns,
},
props: {
option: {
type: Object,
default: () => {
return {};
},
},
api: {
type: Object,
default: () => {
return {};
},
},
methods: {
type: Object,
default: () => {
return {};
},
},
columns: {
type: Array,
default: () => [],
},
buttons: {
type: Object,
default: () => {
return {};
},
},
pagination: {
type: Object,
default: () => {
return {};
},
},
importMode: {
type: Boolean,
default: undefined,
},
defaultEedit: {
type: Function,
default: () => {
return {};
},
},
},
data() {
return {
// loading: true,
curData:[],
dataList: [],
showTable: false,
btnRefresh: true,
tableData: [],
curRow: null,
defaultOption: {
id: "",
title: "",
height: "100%",
maxHeight: undefined,
border: true,
size: "mini",
fit: true,
showSummary: false,
showHeader: true,
align: "left",
static: false,
contextMenu: null,
showPagination: true,
actiqeCellIndexBefore: 0,
},
defaultMethods: {
dataHandler(data) {
return data;
},
spanMethod({ row, column, rowIndex, columnIndex }) {
return { rowspan: 1, colspan: 1 };
},
summaryMethod({ columns, data }) {
let arr = [];
return arr;
},
rowClassName({ row, rowIndex }) {
return "";
},
rowStyle({ row, rowIndex }) {
return {};
},
cellClassName({ row, column, rowIndex, columnIndex }) {
return "";
},
cellStyle({ row, column, rowIndex, columnIndex }) {
return {};
},
rowClick(row, column, index) {},
rowContextmenu(row, column, index) {},
rowDoubleClick(row, column, index) {},
},
defaultPagination: {
currentPage: 1,
pageSizes: [5, 10, 20, 30, 40, 50, 100, 200, 500, 100000],
pageSize: 10,
total: 0,
count: 7,
},
isRequesting: false,
selection: [],
selectionOrigin: [],
isEditing: false,
showContextMenu: false,
position: {
x: 0,
y: 0,
},
contextMenuRow: null,
contextMenuColumn: null,
contextMenuRowIndex: -1,
formItemValue: "",
isImporting: this.importMode || false,
};
},
computed: {
curOption() {
return Object.assign({}, this.defaultOption, this.option);
},
curMethods() {
return Object.assign({}, this.defaultMethods, this.methods);
},
// curData() {
// if (this.formItemValue === "") return this.tableData;
// let arr = [];
// this.tableData.forEach((item) => {
// for (let key in item) {
// let val = "";
// this.columns.forEach((col) => {
// if (!col.prop || col.prop !== key) return;
// val =
// typeof col.formatter === "function"
// ? col.formatter(null, null, item[key]) + ""
// : typeof col.template === "function"
// ? col.template(item[key]) + ""
// : item[key] + "";
// });
// if (this.formItemValue !== "" && !val.includes(this.formItemValue))
// continue;
// arr.push(item);
// break;
// }
// });
// console.log(this.dataList,'curData');
// // return arr;
// return this.dataList
// },
curPagination() {
return Object.assign({}, this.defaultPagination, this.pagination);
},
},
watch: {
api: {
deep: true,
handler() {
// this.defaultPagination.currentPage = 1
this.doRequest();
},
},
columns: {
deep: true,
handler() {
this.showTable = false;
this.$nextTick(() => {
this.showTable = true;
});
},
},
importMode(val) {
this.isImporting = val;
},
"$store.state.doTableRefresh"(val) {
if (!val) return false;
// this.$store.commit('refreshTable', false)
this.$store.dispatch("changeTale", false);
this.showTable = false;
this.$nextTick(() => {
this.showTable = true;
this.doRequest("forceRefresh");
});
},
"$store.state.refreshTable"(val) {
if (!val) return false;
this.doRequest();
// table 表格更新
this.$store.dispatch("changeTale", false);
// this.$store.commit('refreshTable', false)
// this.showTable = false
// this.$nextTick(() => {
// this.showTable = true
// this.doRequest('forceRefresh')
// })
},
},
created() {
sessionStorage.setItem("entery", "0");
this.doRequest();
},
mounted() {
setTimeout(() => {
let height = this.$refs.tableCommonContent.clientHeight;
height = height === 0 ? "auto" : height + "px";
this.defaultOption = Object.assign({}, this.defaultOption, {
height,
});
this.showTable = true;
// 获取当前模式
let query = this.$route.query;
if (query.mode && query.mode === "import") {
this.isImporting =
this.importMode !== undefined ? this.importMode : true;
return false;
}
}, 0);
},
methods: {
// 请求数据
doRequest(type = "") {
console.log(1);
// 处于编辑中或请求中时,不进行下一次请求操作
if (this.isEditing) return false;
console.log(2);
if (this.isRequesting) return false;
console.log(3);
this.isRequesting = true;
let api = this.api || {};
// 获取分页信息
let curPagination = this.curPagination;
let size = curPagination.pageSize;
size = !Number.isFinite(size) ? 999999999 : size;
let page = curPagination.pageNum;
let headers =
typeof api.headers === "function"
? api.headers(this)
: api.headers || {};
api.url = api.url || api["dispatch-url"] || headers["dispatch-url"] || "";
api.method =
api.method ||
api["dispatch-method"] ||
headers["dispatch-method"] ||
"get";
api.name =
api.name || api["data-key"] || headers["data-key"] || "get-message";
api.prefix = api.prefix || headers.prefix || "";
if (!api.url || this.curOption.static) {
this.isRequesting = false;
api.mockData = api.mockData || [];
// 更新分页总数
let total = api.mockData.length;
this.$set(this.defaultPagination, "total", total);
let start = (page - 1) * size;
this.tableData =
api.mockData.slice(start, Math.min(size + start, total)) || [];
return false;
}
// 如果params是个函数,表示需要传入当前的vue对象进行计算返回一个新对象
let params =
typeof api.params === "function" ? api.params(this) : api.params;
let obj =
api.method.toLowerCase() === "get"
? {
url: api.url,
method: api.method,
headers: api.headers || {},
params: { ...params, pageSize: size, pageNum: page },
}
: {
url: api.url,
method: api.method || "post",
headers: api.headers || {},
data: { ...params, pageSize: size, pageNum: page },
};
let arr = api.dataPath.split(".");
// websocket的情况
request({
url: api.url,
method: api.method,
params: params,
}).then((res) => {
console.log(res,'res-request');
this.dataList = res.tableData;
this.curData = res.tableData;
this.dataHandler(res, arr, type);
});
},
// 返回数据处理
dataHandler(res, arr, type) {
let data = res || {};
for (let [i, len] = [0, arr.length]; i < len; i++) {
if (!data[arr[i]]) break;
data = data[arr[i]];
}
data = this.curMethods.dataHandler(data, this, type) || data;
this.isRequesting = false;
this.tableData = data;
// 更新总数
this.$set(
this.defaultPagination,
"total",
res.total ||
(res.data && res.data.total) ||
(res.data.paginationInfo && res.data.paginationInfo.total) ||
data.length
);
this.showTable = false;
this.$nextTick(() => {
this.showTable = true;
// table 表格更新
this.$store.commit("refreshTable", true);
});
},
// 选择
selectHandler(selection) {
console.log(selection, "selectionselection");
this.selection = selection || [];
},
select(selection, row) {
if (!this.curOption.selection) return false;
if (selection.length > 1) {
let del_row = selection.shift();
this.$refs.tableCommon.toggleRowSelection(del_row, false);
}
},
selectAll(selection) {
if (!this.curOption.selection) return false;
if (selection.length > 1) {
selection.length = 1;
}
},
selectCur(row, old) {
this.$refs.tableCommon.toggleRowSelection(row);
if (this.curOption.selection) {
this.$refs.tableCommon.toggleRowSelection(old, false);
}
},
// 编辑状态, 只有被选中的项能被编辑
doEdit() {
this.contextMenuClosed();
if (this.isEditing) {
// 保存并退出
this.curMethods.saveEdit(this);
return false;
}
if (this.selection.length === 0) {
this.$alert("请至少选择一条数据进行编辑");
return false;
}
this.isEditing = true;
// 表格选中位置匹配
let selection = [];
this._tableHandler((item, dataItem) => {
if (JSON.stringify(item) === JSON.stringify(dataItem)) {
selection.push(JSON.parse(JSON.stringify(item)));
this.$set(dataItem, "isEditing", true);
}
});
// 保存被选中行编辑前的原始状态
this.selectionOrigin = selection;
},
// 退出编辑状态
editCancel(save = false) {
let selection = [];
// 退出编辑状态
this.isEditing = false;
// 进行前置值判断
let noNeedCancelEdit = false;
this.tableData.forEach((dataItem) => {
if (dataItem.isEditing) this.$set(dataItem, "isEditing", false);
});
this.doRequest();
},
// 处理编辑状态下的表格和选择循环
_tableHandler(handler = function () {}, selection) {
selection = selection || this.selection;
for (let [i, len] = [0, this.tableData.length]; i < len; i++) {
let dataItem = this.tableData[i];
selection.forEach((item, index) => {
handler(item, dataItem, i, index);
});
}
},
// 新增事件上报
add() {
// 判断是否其他操作在进行中
// if (!this.checkIsPassing()) return false
this.$emit("add");
console.log(123);
},
// 修改事件上报
update() {
// 判断是否其他操作在进行中
this.contextMenuClosed();
if (!this.checkIsPassing(true, true)) return false;
this.$emit("update", this.selection);
},
planAdd() {
this.$emit("planBtn", { method: "add" });
},
planSave() {
this.$emit("planBtn", { method: "save" });
},
planSuv() {
this.$emit("planBtn", { method: "suv" });
},
// 删除事件上报
rowsDelete() {
// 判断是否其他操作在进行中
this.contextMenuClosed();
if (!this.checkIsPassing(true)) return false;
this.$emit("delete", this.selection);
},
// 导出表格
dataExport() {
this.$emit("dataExport", this.selection);
},
// 引入数据
dataImport() {
this.$emit("dataImport");
},
// 自定义按钮处理
headerButtonHandler(button) {
// 简单判断
if (typeof button !== "object") return;
if (!this.checkIsPassing(button.needSelection, button.onlyOne))
return false;
button.handler.call(this, this.selection, button);
},
// 检查是否通过检测
checkIsPassing(needSelection = false, onlyOne = false) {
if (this.isEditing) {
this.$notify("当前正在编辑中,请退出编辑再继续操作");
return false;
}
if (this.isRequesting) {
this.$notify("当前正在数据请求中,请等请求完成后再继续操作");
return false;
}
// if (needSelection && this.selection.length === 0) {
// this.$alert('请至少选择一条数据再进行操作')
// return false
// }
// if (needSelection && onlyOne && this.selection.length !== 1) {
// this.$alert('最多同时只能选择一条数据再进行操作')
// return false
// }
return true;
},
// 单元格class
cellClassName(data) {
return this.curMethods.cellClassName
? this.curMethods.cellClassName.call(this, data)
: "";
},
// 右键
contextMenu(row, column, event) {
event.preventDefault();
event.stopPropagation();
this.showContextMenu = true;
this.$nextTick(() => {
let curMethods = this.curMethods || {};
let el = this.$refs.cmenu.$el;
let cw = el.clientWidth + 10;
let ch = el.clientHeight + 10;
let [x, y] = [event.clientX, event.clientY];
if (x + cw >= window.innerWidth) x = window.innerWidth - cw;
if (y + ch >= window.innerHeight) y = window.innerHeight - ch;
curMethods.rowContextmenu &&
curMethods.rowContextmenu.call(this, row, column, event);
this.position = {
x,
y,
};
this.contextMenuRow = row;
this.contextMenuColumn = column;
this.tableData.forEach((item, index) => {
if (JSON.stringify(item) === JSON.stringify(row)) {
this.contextMenuRowIndex = index;
}
});
});
},
// 左键
rowClick(row, column, event) {
let curMethods = this.curMethods || {};
curMethods.rowClick && curMethods.rowClick.call(this, row, column, event);
},
// 左键双击
rowDoubleClick(row, column, event) {
let curMethods = this.curMethods || {};
curMethods.rowDoubleClick &&
curMethods.rowDoubleClick.call(this, row, column, event);
},
// 单元格鼠标移入
cellEnter(row, column, cell, event) {
// let curMethods = this.curMethods || {}
// this.curRow = this.$extend(true, {}, row)
// curMethods.cellEnter && curMethods.cellEnter.call(this, row, column, cell, event)
},
// 单元格鼠标移出
cellLeave(row, column, cell, event) {
let curMethods = this.curMethods || {};
this.curRow = null;
curMethods.cellLeave &&
curMethods.cellLeave.call(this, row, column, cell, event);
},
// 关闭右键菜单
contextMenuClosed() {
this.showContextMenu = false;
},
// 自定义右键处理
contextMenuHandler(handler = function () {}) {
this.$refs.tableCommon.clearSelection();
this.$refs.tableCommon.toggleRowSelection(this.contextMenuRow, true);
this.contextMenuClosed();
handler.call(
this,
this.contextMenuRow,
this.contextMenuColumn,
this.contextMenuRowIndex,
this.tableData
);
},
// 表头表单项内部过滤
doFilter(data) {
// 当表格有其他操作时进行查询阻止
if (this.isEditing || this.isRequesting) return false;
this.formItemValue = data;
},
// 分页跨度尺寸变化
handleSizeChange(val) {
val = Number.isNaN(val) ? Infinity : val;
this.$set(this.defaultPagination, "pageSize", val);
if (this.defaultPagination.currentPage > 1) {
this.$set(this.defaultPagination, "currentPage", 1);
}
this.$emit("pageSize", val);
this.doRequest();
},
// 当前页面变化
handleCurrentChange(val) {
console.log(val,'handleCurrentChange');
this.$set(this.defaultPagination, "currentPage", val);
// 提交父组件页面变化
this.$emit("pageChange", val);
this.doRequest();
},
// 确认并上传选择的数据
selectedConfirm() {
if (this.selection.length === 0) {
this.$comfirm("您还没有选择数据,是否继续执行").then(() => {
this.importedComplete();
});
return false;
}
this.importedComplete();
},
// 清空数据选择
selectedClear() {
this.$refs.tableCommon.clearSelection();
},
// 取消数据选择
selectedCancel() {
this.importedComplete([]);
},
importedComplete(selection) {
selection = selection || this.selection;
// 如果不存在嵌入页面的情况
if (window === window.parent) {
this.$emit("imported", selection);
return false;
}
// 调用父页面的getSelectedData事件
window.parent._getSelectedData &&
window.parent._getSelectedData(selection);
},
// link用于配合layout组件进行跨组件通信
link(data) {
this.$emit("link", data);
},
// 初始化部分不易获取的元素
elemRender(tar) {
tar && tar.render && tar.render(tar, this);
},
// 更新右上角的按钮
refreshBtns() {
this.btnRefresh = false;
this.$nextTick(() => {
this.btnRefresh = true;
});
},
},
};
</script>
<style scoped>
.table-common-wrapper {
position: relative;
height: 100%;
box-sizing: border-box;
}
.table-common-container {
position: relative;
height: 100%;
display: flex;
flex-wrap: nowrap;
flex-direction: column;
}
.table-common-content {
position: relative;
height: auto;
flex: 1;
}
.pagination-wrapper {
position: relative;
height: auto;
padding: 8px 4px;
box-sizing: border-box;
border-bottom: 1px solid #ebeef5;
border-right: 1px solid #ebeef5;
border-left: 1px solid #ebeef5;
}
.table-common-title {
position: relative;
height: auto;
padding: var(--extra-space) 0;
text-align: center;
font-size: 1.2em;
color: var(--table-default-color);
border: 1px solid var(--table-default-border);
border-bottom: none;
background-color: var(--table-default-background-color);
}
.btns-wrapper {
position: relative;
height: auto;
box-sizing: border-box;
text-align: right;
padding-bottom: 10px;
}
.btns-wrapper .el-button {
font-size: 12px;
border-radius: 3px;
padding: 7px 15px;
}
.context-menu-header {
position: relative;
display: flex;
align-content: space-between;
justify-content: center;
}
.context-menu-header > span {
flex: 1;
}
.context-menu-close:hover {
cursor: pointer;
color: var(--table-default-border);
}
.btns-wrapper-absolute {
position: absolute;
width: 100%;
transform: translateY(-100%);
}
.context-menu-wrapper {
position: fixed;
z-index: 10;
}
.context-menu-list {
width: 160px;
padding-bottom: 6px;
}
.context-menu-btn {
width: 100%;
}
.table-common-content /deep/ .cell-active {
background-color: var(--table-default-hover);
}
.table-common-content /deep/ .cell-canClick {
cursor: pointer;
}
</style>
页面使用情况
<template>
<div>
<TableCommon
ref="table"
:api="api"
:option="Keyoption"
:methods="methods"
:columns="plancolumns"
:buttons="Combuttons"
@link="link"
@pageChange="pageChange"
@pageSize="pageSize"
/>
</div>
</template>
<script>
import TableCommon from "@/components/TableCommon/TableCommon.vue";
let hashTable = {};
let tempHashTable = {};
let tempHashTables = {};
export default {
data() {
return {
Combuttons: {
add: true,
export: true,
},
Keyoption: {
align: "center",
id: "classTable",
showPagination: true,
},
methods: {
cellStyle({ row, column }) {},
rowClick(row, column) {},
dataHandler(res) {
hashTable = {};
tempHashTable = {};
for (let [i, len] = [0, res.length]; i < len; i++) {
let item = res[i];
// 建立哈希表用于表格行合并
if (!hashTable[item.departmentName])
hashTable[item.departmentName] = 0;
hashTable[item.departmentName]++;
}
return res;
},
},
plancolumns: [
{
type: "index",
label: "序号",
},
{
label: "部门",
prop: "departmentName",
width: "130",
},
{
label: "任务类型",
prop: "taskType",
width: "130",
formatter(row, column, cellValue) {
if (cellValue * 1 === 1) {
return "公司级";
} else if (cellValue * 1 === 3) {
return "部门级";
}
},
},
{
label: "重点工作任务名称",
prop: "workTaskType",
width: "270",
},
{
label: "牵头部门",
prop: "responsibleDepartment",
width: "130",
},
{
label: "任务来源",
prop: "taskSource",
width: "130",
},
{
label: "预计成果",
prop: "expectedResults",
width: "130",
formatter(row, column, cellValue) {
if (cellValue * 1 === 1) {
return "项目完工";
} else if (cellValue * 1 === 2) {
return "里程碑节点";
}
},
},
{
label: "任务系数",
prop: "taskFactor",
width: "130",
},
{
label: "计划完成时间",
prop: "planCompleteTime",
width: "130",
},
{
label: "任务描述",
prop: "taskDesc",
},
],
api: {
url: "/business/declare/detailHome",
method: "get",
dataPath: "data.list",
params($comp) {
return {
pageNum: 1,
pageSize: 10,
taskType: "2",
};
},
},
};
},
components: {
TableCommon,
},
methods: {
dataExport() {
console.log(123);
let year = this.$route.query.month.replace("年", "");
year = year.replace("月", "");
let classId = "94b780f57d5d8ef5017db86d366f5fda";
let obj = {
month: year,
organization: classId,
};
},
pageChange(val) {
var obj = {
pageNum: val,
pageSize: this.limit ? this.limit : 10,
taskType: "2",
};
this.api.params = obj;
},
pageSize(val) {
this.limit = val;
var obj = {
pageNum: 1,
pageSize: val,
taskType: "2",
};
this.api.params = obj;
},
link(data) {
if (data.method === "drop") {
let obj = {};
obj.id = data.id;
obj.isModify = "1";
Putask(obj).then((res) => {
this.$refs.table.doRequest();
});
console.log(data);
} else if (data.method === "upgrade") {
let up = {};
up.id = data.id;
up.isModify = "3";
Putask(up).then((res) => {
this.$refs.table.doRequest();
});
}
},
},
};
</script>
<style lang="scss" scoped>
</style>
展示效果
优缺点: 使用的时候简单,页面冗余少 封装难度大,并且在返回数据不是想要的情况下,处理起来比较麻烦