You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

913 lines
27 KiB

4 years ago
<script>
3 years ago
import axios from 'axios';
import {getToken} from "@/utils/auth";
4 years ago
export default {
props: {
3 years ago
//请求相关
action:[String,Function], //String传入请求地址 Function传入返回promise的方法
reqOpt:Object, //请求参数配置
resProp:{
type:String,
default:'data'
},//请求后需要获取表格数据键名
//操作权限
auths: {
4 years ago
type: Array,
3 years ago
default: () => [],
4 years ago
},
3 years ago
//table props
/**
* @type {Array}
* [
* {
* type: ("selection"|"index"|"expand"),
* index: [Number,Function(index)], //type=index,通过传递 index 属性来自定义索引
* columnKey: "", //column 的 key如果需要使用 filter-change 事件,则需要此属性标识是哪个 column 的筛选条件
* label: "",
* prop: "",
* width: "",
* minWidth: "",
* fixed: ("true"| "left"| "right"),
* renderHeader:(h, { column, $index}) => {
* },
* sortable:[Boolean,String] (true|false|"custom"),//custom需监听 Table 的 sort-change 事件
* sortMethod: (a,b) => {}, //对数据进行排序的时候使用的方法,仅当 sortable 设置为 true 的时候有效,需返回一个数字,和 Array.sort 表现一致
* sortBy:[String,Array,Function(row, index)],//指定数据按照哪个属性进行排序,仅当 sortable 设置为 true 且没有设置 sort-method 的时候有效。如果 sort-by 为数组,则先按照第 1 个属性排序,如果第 1 个相等,再按照第 2 个排序,以此类推
* sortOrders:[Array] (['ascending', 'descending', null]),//数据在排序时所使用排序策略的轮转顺序,仅当 sortable 为 true 时有效
* resizable:[Boolean], //需要在 el-table 上设置 border 属性为真
* formatter:[Function(row, column, cellValue, index)],
* showOverflowTooltip:[Boolean],
* align: ("left","center","right"),
* headerAlign: ("left","center","right"),
* className:[String],
* labelClassName:[String],
* selectable:[Function(row, index)],//仅对 type=selection 的列有效,类型为 FunctionFunction 的返回值用来决定这一行的 CheckBox 是否可以勾选
* reserveSelection:[Boolean],//仅对 type=selection 的列有效,类型为 Boolean为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key
* filters:[Array[{ text, value }]],
* filterPlacement:[String],
* filterMultiple:[Boolean],
* filterMethod:[Function(value, row, column)],
* filteredValue:[Array]
* }
* ]
*/
4 years ago
tableItem: {
type: Array,
3 years ago
default: () => [],
4 years ago
},
3 years ago
//数据
list: {
type: Array,
default: () => [],
4 years ago
},
3 years ago
height: {
type: [String, Number],
4 years ago
},
3 years ago
maxHeight: [String, Number],
//斑马纹
stripe: {
type: Boolean,
default: false,
4 years ago
},
3 years ago
//是否带有纵向边框
border: {
type: Boolean,
default: true,
4 years ago
},
3 years ago
size: String, // medium / small / mini
fit: {
type: Boolean,
default: true,
4 years ago
},
3 years ago
showHeader: {
4 years ago
type: Boolean,
3 years ago
default: true,
4 years ago
},
3 years ago
highlightCurrentRow: {
type: Boolean,
default: true,
4 years ago
},
3 years ago
currentRowKey: [String, Number],
rowClassName: [Function, String],
rowStyle: [Function, Object],
cellClassName: [Function, String],
cellStyle: [Function, Object],
headerRowClassName: [Function, String],
headerRowStyle: [Function, Object],
headerCellClassName: [Function, String],
headerCellStyle: [Function, Object],
rowKey: {
type: [String, Function],
default: "id",
4 years ago
},
3 years ago
emptyText: String,
defaultExpandAll: {
4 years ago
type: Boolean,
3 years ago
default: true,
4 years ago
},
3 years ago
expandRowKeys: Array, //可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。
defaultSort: Object, //默认的排序列的 prop 和顺序。它的prop属性指定默认的排序的列order指定默认排序的顺序order: ascending, descending
tooltipEffect: String,
4 years ago
showSummary: {
type: Boolean,
3 years ago
default: false,
},
sumText: {
type: String,
default: "合计",
4 years ago
},
3 years ago
summaryMethod: Function, //Function({ columns, data })
spanMethod: Function,
selectOnIndeterminate: Boolean,
//展示树形数据时,树节点的缩进
indent: {
type: Number,
default: 18,
4 years ago
},
3 years ago
lazy: Boolean,
load: Function, //Function(row, treeNode, resolve)
4 years ago
treeProps: {
type: Object,
default: () => {
3 years ago
return { children: "children", hasChildren: "hasChildren" };
},
4 years ago
},
3 years ago
4 years ago
tableStyle: {
type: Object,
default: () => {
3 years ago
return { width: "100%", marginBottom: "20px" };
},
4 years ago
},
btnWidth: {
type: Number,
3 years ago
default: 140,
4 years ago
},
// 分页相关
isPage: {
type: Boolean,
3 years ago
default: true,
4 years ago
},
total: {
type: Number,
3 years ago
default: 0,
4 years ago
},
pageSize: {
3 years ago
type: Number,
4 years ago
},
3 years ago
pageSizeOpts: {
type: Array,
default: () => [10, 20, 30, 40],
},
showSizer: {
type: Boolean,
default: true,
4 years ago
},
},
data() {
return {
3 years ago
isBtns: false,
tableHeight: null,
//document.documentElement.clientHeight -50 -37 - 20- 25 - 76,
isShowPage: true,
checkTable: this.tableItem.map((item) => item?.prop),
loading: false,
visibleBtn: true, //操作按钮显示
totalData:0,
listData:[],
selectOpt:{
page:1,
page_size:10,
sort_name:'',
sort_type:''
},
};
4 years ago
},
methods: {
3 years ago
//方法
4 years ago
initLoad() {
3 years ago
let clientHeight = document.documentElement.clientHeight;
let lxheader = document.querySelector('.v-header').getBoundingClientRect()
let lxHeader_height = lxheader.height + 25; //查询 头部
let paginationHeight = 37; //分页的高度
let topHeight = 50; //页面 头部
this.tableHeight =
clientHeight - lxHeader_height - topHeight - paginationHeight - 20 - 25;
//console.log(this.tableHeight)
},
getTableData(isRefresh = false){
if(isRefresh){
this.selectOpt.page = 1
}
switch (typeof this.action){
case "string":
this.loading = true
axios({
baseURL:process.env.VUE_APP_BASE_API,
url:this.action,
headers:{
Authorization:"Bearer " + getToken()
},
params:(this.reqOpt.method && this.reqOpt.method === 'get') ? this.selectOpt : '',
data:(this.reqOpt.method && this.reqOpt.method === 'post') ? this.selectOpt : '',
...this.reqOpt
}).then(res => {
this.listData = this.getByStrkey(res.data,this.resProp)
this.totalData = res.data.total
setTimeout(() => {
this.loading = false
},300)
}).catch(err => {
console.error(err)
this.loading = false
})
break;
case "function":
this.loading = true
this.action(false,{
...this.selectOpt,
...this.reqOpt
}).then(res => {
this.listData = this.getByStrkey(res,this.resProp)
this.totalData = res.total
setTimeout(() => {
this.loading = false
},300)
}).catch(err => {
console.error(err)
this.loading = false
})
break;
}
},
getByStrkey(obj,str){
if(!str) return obj
let res = this.deepCopy(obj);
let keys = str.split('.')
keys.forEach(key => {
res = res[key]
})
return res;
},
deepCopy(data){
//string,number,bool,null,undefined,symbol
//object,array,date
if (data && typeof data === "object") {
//针对函数的拷贝
if (typeof data === "function") {
let tempFunc = data.bind(null);
tempFunc.prototype = this.deepCopy(data.prototype);
return tempFunc;
}
switch (Object.prototype.toString.call(data)) {
case "[object String]":
return data.toString();
case "[object Number]":
return Number(data.toString());
case "[object Boolean]":
return new Boolean(data.toString());
case "[object Date]":
return new Date(data.getTime());
case "[object Array]":
let arr = [];
for (let i = 0; i < data.length; i++) {
arr[i] = this.deepCopy(data[i]);
}
return arr;
//js自带对象或用户自定义类实例
case "[object Object]":
let obj = {};
for (let key in data) {
//会遍历原型链上的属性方法可以用hasOwnProperty来控制 obj.hasOwnProperty(prop)
obj[key] = this.deepCopy(data[key]);
}
return obj;
}
} else {
//string,number,bool,null,undefined,symbol
return data;
}
},
sortListener({column, prop, order}){
this.selectOpt.sort_name = prop
switch (order){
case "ascending":
this.selectOpt.sort_type = 'ASC'
break;
case "descending":
this.selectOpt.sort_type = 'DESC'
break;
default:
this.selectOpt.sort_type = ''
}
this.getTableData()
},
//element table方法
clearSelection() {
this.$refs.table.clearSelection();
},
toggleRowSelection(row) {
this.$nextTick(() => {
this.$refs.table.toggleRowSelection(row);
});
},
toggleAllSelection() {
this.$refs.table.toggleAllSelection();
},
toggleRowExpansion(row, expanded = true) {
this.$refs.table.toggleRowExpansion(row, expanded);
},
setCurrentRow(row) {
this.$refs.table.toggleRowExpansion(row);
},
clearSort() {
this.$refs.table.clearSort();
},
clearFilter() {
this.$refs.table.clearFilter();
},
doLayout() {
this.$refs.table.doLayout();
},
sort() {
this.$refs.table.sort();
},
//table通讯事件
delete(row, type) {
this.$emit(type, row);
},
editor(row, type) {
this.$emit(type, row);
},
select(selection, row) {
this.$emit("select", selection, row);
},
selectAll(selection) {
this.$emit("select-all", selection);
},
selectionChange(selection) {
this.$emit("selection-change", selection);
},
cellMouseEnter(row, column, cell, event) {
this.$emit("cell-mouse-enter", { row, column, cell, event });
},
cellMouseLeave(row, column, cell, event) {
this.$emit("cell-mouse-leave", { row, column, cell, event });
},
cellClick(row, column, cell, event) {
this.$emit("cell-click", { row, column, cell, event });
},
cellDblclick(row, column, cell, event) {
this.$emit("cell-dblclick", { row, column, cell, event });
},
rowClick(row, column, event) {
this.$emit("row-click", { row, column, event });
},
rowContextmenu(row, column, event) {
this.$emit("row-contextmenu", { row, column, event });
},
rowDblclick(row, column, event) {
this.$emit("row-dblclick", { row, column, event });
},
headerClick(column, event) {
this.$emit("header-click", column, event);
},
headerContextmenu(column, event) {
this.$emit("header-contextmenu", column, event);
},
sortChange({ column, prop, order }) {
this.$emit("sort-change", { column, prop, order });
},
filterChange(filters) {
this.$emit("filter-change", filters);
},
currentChange(currentRow, oldCurrentRow) {
this.$emit("current-change", currentRow, oldCurrentRow);
},
headerDragend(newWidth, oldWidth, column, event) {
this.$emit("header-dragend", { newWidth, oldWidth, column, event });
},
expandChange(row, expanded) {
this.$emit("expand-change", row, expanded);
4 years ago
},
deleteClick(row) {
this.$emit('delete', row)
},
editorClick(row) {
this.$emit('editor', row)
},
createPage() {
if (this.isPage)
return (
<div>
<transition
enter-active-class="slide-in-bottom"
3 years ago
leave-to-class="slide-out-down"
>
4 years ago
<Page
page-size-opts={this.pageSizeOpts}
show-total={true}
page-size={this.pageSize}
v-show={this.isShowPage}
3 years ago
total={this.action ? (this.totalData || this.listData.length) : this.total}
4 years ago
size="small"
show-elevator={true}
show-sizer={this.showSizer}
3 years ago
class="xy-table__page"
4 years ago
on={{
3 years ago
["on-page-size-change"]: (e) => {
if(this.action){
this.selectOpt.page_size = e
this.selectOpt.page = 1
this.getTableData()
}
this.$emit("pageSizeChange", e)
},
["on-change"]: (e) => {
if(this.action){
this.selectOpt.page = e
this.getTableData()
}
this.$emit("pageIndexChange", e)
}
}}
></Page>
4 years ago
</transition>
3 years ago
<el-popover
placement="top-start"
width="200"
trigger="hover"
scopedSlots={{
default: () => {
return (
<el-checkbox-group v-model={this.checkTable}>
{this.$props?.tableItem?.map((item, index) => {
return item.type !== 'expand' ? (
<el-checkbox label={item.prop} key={item.prop}>
{item.label}
</el-checkbox>
) : ''
})}
</el-checkbox-group>
);
},
}}
>
<el-button
slot="reference"
size="mini"
type="primary"
class="xy-table__setting"
icon="el-icon-s-tools"
circle
style={{
padding: "3px",
}}
></el-button>
</el-popover>
4 years ago
</div>
3 years ago
);
4 years ago
},
3 years ago
isCreateAuthBtns() {
let _this = this;
let res = (
<el-table-column
key={`xy-table-btn`}
fixed="right"
label="操作"
minWidth={_this.btnWidth}
header-align="center"
scopedSlots={{
default: (scope) => {
if (_this.auths?.length <= 0) return;
let { dom, flag } = _this.createAuthBtns(scope);
_this.isBtns = flag;
return dom;
},
}}
></el-table-column>
);
if (_this.isBtns !== 0) {
return res;
}
4 years ago
},
3 years ago
createAuthBtns(scope) {
let _this = this;
if (_this.auths?.length > 0) {
let btns = new Map();
btns.set(
"edit",
<i-button
style={{
"margin-right": "6px",
}}
type="primary"
size="small"
onClick={() => _this.editorClick(scope.row, "edit")}
>
编辑
</i-button>
);
btns.set(
"delete",
<Poptip
transfer={true}
confirm
title="确认要删除吗"
on={{ ["on-ok"]: () => _this.deleteClick(scope.row, "delete") }}
>
<i-button
style={{
"margin-right": "6px",
}}
type="error"
size="small"
>
删除
</i-button>
</Poptip>
);
let flag = 0;
let dom = (
<div
style={{
display: "flex",
"justify-content": "flex-start",
"align-items": "center",
"flex-wrap": "wrap",
}}
>
{_this.auths.map((item, index) => {
if (_this.$scopedSlots[item]) {
flag = index;
return _this.$scopedSlots[item](scope, item, index);
} else {
if (btns.get(item)) {
flag = index;
return btns.get(item);
}
}
})}
</div>
);
return { dom, flag };
}
},
},
computed: {
tableFormat() {
return this.tableItem.filter((item) => {
return this.checkTable.indexOf(item.prop) !== -1;
});
4 years ago
},
},
created() {
3 years ago
this.getTableData()
4 years ago
},
mounted() {
3 years ago
this.initLoad();
},
updated() {
this.$nextTick(() => {
this.doLayout();
})
4 years ago
},
render(h) {
3 years ago
let { $scopedSlots } = this;
4 years ago
return (
3 years ago
<div class="table-tree" style={{ position: "relative" }}>
{this.tableItem && this.tableItem.length > 0 ? (
<el-table
4 years ago
class="v-table"
3 years ago
v-loading={this.loading}
ref="table"
data={this.action ? this.listData : this.list}
height={this.height ?? this.tableHeight}
max-height={this.maxHeight}
stripe={this.stripe}
border={this.border}
size={this.size}
fit={this.fit}
show-header={this.showHeader}
highlight-current-row={this.highlightCurrentRow}
current-row-key={this.currentRowKey}
row-class-name={this.rowClassName}
row-style={this.rowStyle}
cell-class-name={this.cellClassName}
cell-style={this.cellStyle}
header-row-class-name={this.headerRowClassName}
header-row-style={this.headerRowStyle}
header-cell-class-name={this.headerCellClassName}
header-cell-style={this.headerCellStyle}
row-key={this.rowKey}
empty-text={this.emptyText}
default-expand-all={this.defaultExpandAll}
expand-row-keys={this.expandRowKeys}
default-sort={this.defaultSort}
tooltip-effect={this.tooltipEffect}
show-summary={this.showSummary}
sum-text={this.sumText}
summary-method={this.summaryMethod}
span-method={this.spanMethod}
select-on-indeterminate={this.selectOnIndeterminate}
indent={this.indent}
lazy={this.lazy}
load={this.load}
tree-props={this.treeProps}
4 years ago
on={{
3 years ago
["select"]: this.select,
["select-all"]: this.selectAll,
["selection-change"]: this.selectionChange,
["cell-mouse-enter"]: this.cellMouseEnter,
["cell-mouse-leave"]: this.cellMouseLeave,
["cell-click"]: this.cellClick,
["cell-dblclick"]: this.cellDblclick,
["row-click"]: this.rowClick,
["row-contextmenu"]: this.rowContextmenu,
["row-dblclick"]: this.rowDblclick,
["header-click"]: this.headerClick,
["header-contextmenu"]: this.headerContextmenu,
["sort-change"]: this.action ? this.sortListener : this.sortChange,
["filter-change"]: this.filterChange,
["current-change"]: this.currentChange,
["header-dragend"]: this.headerDragend,
["expand-change"]: this.expandChange,
}}
>
3 years ago
<el-table-column
type="index"
width="50"
align="center"
fixed='left'>
</el-table-column>
3 years ago
{this.tableFormat.map((item, index) => {
if ($scopedSlots[item.prop]) {
return $scopedSlots[item.prop](item, index);
}
return (
<el-table-column
3 years ago
key={Number(Math.random())+index}
3 years ago
type={item.type}
index={item.index}
column-key={String(Math.random())}
label={item.label}
prop={item.prop}
width={item.width ?? "auto"}
min-width={item.minWidth}
fixed={item.fixed ?? false}
render-header={item.renderHeader}
sortable={item.sortable}
sort-method={item.sortMethod}
sort-by={item.sortBy}
sort-orders={item.sortOrders}
resizale={item.resizale}
formatter={item.formatter}
show-overflow-tooltip={item.showOverflowTooltip ?? true}
align={item.align ?? "center"}
header-align={item.headerAlign ?? "center"}
class-name={`xy-table__row-fade ${item.className}`}
label-class-name={`xy-table__title-fade ${item.labelClassName}`}
selectable={item.selectable}
reserve-selection={item.reserveSelection}
filters={item.filters}
filter-placement={item.filterPlacement}
filter-multiple={item.filterMultiple}
filter-method={item.filterMethod}
filtered-value={item.filteredValue}
scopedSlots={
item.customFn || item.type === "expand"
? {
default(scope) {
if (item.type === "expand") {
return item.expandFn(scope);
}
if (item.customFn) {
return item.customFn(scope.row, scope);
}
},
4 years ago
}
3 years ago
: ""
}
>
{ item.multiHd
? item.multiHd.map((item1, index1) => {
return (
<el-table-column
key={`xy-table-col-multi-${item1.prop}`}
prop={`${item.pProp ? item.pProp + "." : ""}${
item1.prop
}`}
type={item1.type}
index={item1.index}
column-key={String(Math.random())}
label={item1.label}
prop={item1.prop}
width={item1.width ?? "auto"}
min-width={item1.minWidth}
fixed={item1.fixed}
render-header={item1.renderHeader}
sortable={item1.sortable ?? false}
sort-method={item1.sortMethod}
sort-by={item1.sortBy}
sort-orders={item1.sortOrders}
resizale={item1.resizale ?? false}
formatter={item1.formatter}
show-overflow-tooltip={item1.showOverflowTooltip ?? true}
align={item1.align ?? "center"}
header-align={item1.headerAlign ?? "center"}
class-name={`xy-table__row-fade ${item1.className}`}
label-class-name={`xy-table__title-fade ${item1.labelClassName}`}
selectable={item1.selectable}
reserve-selection={item1.reserveSelection}
filters={item1.filters}
filter-placement={item1.filterPlacement}
filter-multiple={item1.filterMultiple}
filter-method={item1.filterMethod}
filtered-value={item1.filteredValue}
scopedSlots={
item1.customFn
? {
default(scope1) {
return item1.customFn(scope1);
},
}
: ""
}
></el-table-column>
);
})
: ""}
</el-table-column>
);
})}
{ $scopedSlots.btns ? $scopedSlots.btns() : this.isCreateAuthBtns() }
</el-table>
) : (
<el-table height={this.height ?? this.tableHeight} />
)}
4 years ago
<el-backtop
target=".el-table__body-wrapper"
visibility-height={120}
bottom={100}
3 years ago
right={36}
></el-backtop>
{ this.createPage() }
4 years ago
</div>
3 years ago
);
},
};
4 years ago
</script>
<style lang="scss" scoped>
@import "../../styles/variables.scss";
3 years ago
::v-deep .el-table {
margin-bottom: 0 !important;
4 years ago
}
3 years ago
.xy-table__setting {
font-size: 14px;
position: absolute;
z-index: 99;
bottom: 7px;
left: 10px;
}
.xy-table__page {
4 years ago
display: flex;
justify-content: right;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
background: rgba(140, 140, 140, 0.6);
z-index: 10;
padding: 6px 10px !important;
position: relative;
top: 0;
left: 0;
right: 0;
3 years ago
::v-deep .ivu-page-item,
.ivu-page-options {
4 years ago
background: transparent !important;
}
::v-deep .ivu-page-item a {
color: #fff;
}
::v-deep .ivu-page-item-active a {
color: $primaryColor;
}
::v-deep .ivu-page-total {
color: #fff !important;
}
::v-deep .ivu-page-options-elevator {
color: #fff !important;
}
@media (max-width: 600px) {
::v-deep .ivu-page-options {
display: none !important;
}
}
}
.slide-in-bottom {
transform-origin: 0 100%;
3 years ago
animation: slide-in-bottom 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
4 years ago
}
@keyframes slide-in-bottom {
0% {
transform: scaleY(0);
opacity: 0;
}
100% {
transform: scaleY(1);
opacity: 1;
}
}
.slide-out-down {
transform-origin: 0 100%;
3 years ago
animation: slide-out-down 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
4 years ago
}
@keyframes slide-out-down {
0% {
transform: scaleY(1);
opacity: 1;
}
100% {
transform: scaleY(0);
opacity: 0;
}
}
.icon-scale-left {
animation: icon-scale-left 0.5s forwards ease-out;
@keyframes icon-scale-left {
from {
}
to {
clip-path: polygon(0 30%, 100% 30%, 100% 100%, 0 100%);
background: rgba(140, 140, 140, 0.7);
3 years ago
transform: scale(0.8, 0.8) translateX(-22px) translateY(-10px)
rotate(-90deg);
4 years ago
}
}
}
.icon-recover {
animation: icon-recover 0.5s forwards ease-out;
@keyframes icon-recover {
from {
background: rgba(120, 120, 120, 0.8);
transform: scale(0.8, 0.8) translateX(-30px) rotate(-90deg);
}
to {
}
}
}
</style>
3 years ago
<style>
.xy-table__row-fade {
animation: fade-in-row 600ms;
}
@keyframes fade-in-row {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
}
}
.xy-table__title-fade {
animation: fade-in-title 600ms;
}
@keyframes fade-in-title {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>