<template>
<div id="puilceTable">
<el-form ref="formDataRef" :model="tableFormData" :rules="tableFormData.rule">
<el-table v-if="refreshTable" ref="multipleTable"
v-loading="localCorptable.loading" :height="localCorptable.height"
:data="tableFormData.tableData"
:stripe="localCorptable.style && localCorptable.style.stripe ? true : false"
:row-key="localCorptable.rowKey"
:default-expand-all="localCorptable.defaultExpandAll?true:false"
:tree-props="localCorptable.treeProps?localCorptable.treeProps:{children: 'children', hasChildren: 'hasChildren'}"
:lazy="localCorptable.lazy"
:load="tableLoad"
:empty-text="localCorptable.emptyText ? localCorptable.emptyText : '暂无数据'"
:header-cell-style="localCorptable.style && localCorptable.style.heardeStyle ? localCorptable.style.heardeStyle : hStyle"
:cell-style="localCorptable.style && localCorptable.style.cellStyle ? localCorptable.style.cellStyle : cStyle"
@selection-change="handleSelectionChange($event, localCorptable.selection ? (localCorptable.selection === 'more' ? '1' : '0') : '0')"
@select="selectRow"
@select-all="selectAllRow"
>
<!-- 多选框 -->
<el-table-column v-if="localCorptable.isselection" type="selection" :reserve-selection="localCorptable.reserve ? localCorptable.reserve : false" />
<!-- 序号 -->
<el-table-column v-if="localCorptable.sNumber ? localCorptable.sNumber : false" label="序号" width="50">
<template slot-scope="scope">
{{ localCorptable.pageObj ? (localCorptable.pageObj.current - 1) * localCorptable.pageObj.size + scope.$index + 1 : scope.$index + 1 }}
</template>
</el-table-column>
<template v-for="(item, itemIndex) in localCorptable.header">
<el-table-column
v-if="item.formatter"
:key="`formatter-${itemIndex}`"
:formatter="item.formatter"
:label="item.name"
:min-width="item.width"
:prop="item.field + tableFormData.tableData[item.field]"
resizable
:align="item.align ? item.align : 'center'"
:show-overflow-tooltip="item.showOverflowTooltip==false?false:true"
:fixed="item.fixed"
/>
<el-table-column
v-else
:key="`normal-${itemIndex}`"
:label="item.name"
:min-width="item.width"
:prop="item.field + tableFormData.tableData[item.field]"
resizable
:align="item.align ? item.align: 'center'"
:show-overflow-tooltip="item.showOverflowTooltip==false?false:true"
:fixed="item.fixed"
>
<template v-if="item.headerSlot" slot="header">
<slot :name="item.headerSlot_name" />
</template>
<template slot-scope="scope">
<!-- 输入框 -->
<el-form-item v-if="item.type == 'input' || item.type == 'inputNumber' || item.type == 'select'" :prop="'tableData.' + scope.$index + '.' + item.field" :rules="tableFormData.rule[item.field]">
<el-input v-if="item.type == 'input'" v-model.trim="scope.row[item.field]" type="text" :placeholder="item.placeholder ? item.placeholder : '请输入' + item.name" :disabled="item.isDis" @input="inputChange($event, scope.row)">
<template v-if="item.sonType && item.sonType.template">
<div v-for="(i, j) in item.sonType.template" :key="j" :slot="i.slot">
<span v-if="i.sonTypeunit">{{ scope.row[i.sonTypeunit] }}</span>
<span v-else>{{ i.name }}</span>
</div>
</template>
<el-select
v-if="item.sonType.select"
:slot="item.select.slot ? item.select.slot : 'append'"
v-model="formdataObj[item.select.field]"
:min="item.min || 0"
:max="item.max || ''"
filterable
:multiple="item.select.mult"
:placeholder="item.select.placeholder ? item.select.placeholder : '请选择' + item.select.name"
:disabled="item.select.isDis"
@change="SelectChange($event, item.select.field)"
>
<el-option v-for="(sItem, Sindex) in item.select.option" :key="Sindex" :label="sItem[item.select.label ? item.select.label : 'name']" :value="sItem[item.select.value ? item.select.value : 'code']" :disabled="sItem.isDis" /> </el-select
></el-input>
<el-input v-if="item.type == 'inputNumber'" v-model.trim="scope.row[item.field]" type="number" :placeholder="item.placeholder ? item.placeholder : '请输入' + item.name" :disabled="item.isDis" @input="inputChange($event, scope.row)">
<template v-if="item.sonType && item.sonType.template">
<div v-for="(i, j) in item.sonType.template" :key="j" :slot="i.slot">
<span v-if="i.sonTypeunit">{{ scope.row[i.sonTypeunit] }}</span>
<span v-else>{{ i.name }}</span>
</div>
</template>
<el-select
v-if="item.sonType && item.sonType.select"
:slot="item.select.slot ? item.select.slot : 'append'"
v-model="formdataObj[item.select.field]"
:min="item.min || 0"
:max="item.max || ''"
filterable
:multiple="item.select.mult"
:placeholder="item.select.placeholder ? item.select.placeholder : '请选择' + item.select.name"
:disabled="item.select.isDis"
@change="SelectChange($event, item.select.field)"
>
<el-option v-for="(sItem, Sindex) in item.select.option" :key="Sindex" :label="sItem[item.select.label ? item.select.label : 'name']" :value="sItem[item.select.value ? item.select.value : 'code']" :disabled="sItem.isDis" /> </el-select
></el-input>
<!-- 下拉框 -->
<el-select v-if="item.type == 'select'" v-model="scope.row[item.field]" filterable :disabled="item.isDis" @change="SelectChange(scope.row, item.field)">
<el-option v-for="(sItem, Sindex) in scope.row[item.option]" :key="Sindex" :label="sItem[item.label ? item.label : 'name']" :value="sItem[item.value ? item.value : 'code']" :disabled="sItem.isDis" />
</el-select>
</el-form-item>
<!-- 文字 -->
<span v-if="!item.type && item.type != 'button'">{{ (scope.row[item.field]||scope.row[item.field]==0) ? (typeof scope.row[item.field] == 'object' ? scope.row[item.field].text : scope.row[item.field]) : '-' }}</span>
<!-- 文字区别展示(增加样式) -->
<span v-if="item.type && item.type == 'style'">
<span :style="scope.row[item.field].style">{{ scope.row[item.field] instanceof Object ? scope.row[item.field].text : scope.row[item.field] }}</span>
</span>
<!-- tag -->
<span v-if=" item.type == 'tag'">
<el-tag :type="item.tagType" :style="scope.row[item.field].style">{{ scope.row[item.field] }}</el-tag>
</span>
<!-- 查看 -->
<span v-if="item.type == 'btnText'" class="buttonClick textGrColor" @click="childByValue(scope.row, item.field)">{{ scope.row[item.field] != '' && scope.row[item.field] != null ? '点击查看' : '' }}</span>
<!-- 是否必选必填(限制性展示) -->
<span v-if="item.type == 'required'">
<span :style="colorRequired">{{ scope.row.isRequired ? (scope.row.isRequired === true ? '(*)' : scope.row.isRequired) : '' }}</span>
{{ scope.row[item.field] }}
</span>
<!-- 按钮 -->
<span v-if="item.type == 'button'" class="buttonClick textGrColor" @click="childByValue(scope.row, item.field)">{{ scope.row[item.field] instanceof Object ? scope.row[item.field].text : scope.row[item.field] }}</span>
<!-- 下载 -->
<span v-if="item.type == 'downLoad'" class="buttonClick textDlColor" @click="childByValue(scope.row, item.field)">{{ scope.row[item.field] ? scope.row[item.field] : '下载文件' }}</span>
<!-- 上传 -->
<el-upload
v-if="item.type == 'upload'"
ref="upload"
class="upload-demo"
:accept="item.accept ? item.accept : '.png,.jpg,.jpeg,.pdf'"
action="s"
:show-file-list="false"
:http-request="
file => {
return upLoadFile(file, scope.row, item.size)
}
"
>
<el-button type="text" class="textGrColor">上传文件</el-button>
</el-upload>
<!-- 时间 -->
<span v-if="item.type == 'date'">{{ parseTime(scope.row[item.field]) }}</span>
<!-- 插槽 -->
<span v-if="item.type == 'slot'">
<slot :name="item.slot_name" :row="scope.row" />
</span>
</template>
</el-table-column>
</template>
<el-table-column v-if="localCorptable.operation" fixed="right" label="操作" :width="localCorptable.operationWidth" :align="item.align ? item.align : 'center'">
<template #default="scope">
<span v-for="(item, index) in btnFunc(scope.row, localCorptable.isoperaState)" :key="index" class="buttonS">
<el-button v-if="item.genre !== 'upload'" type="text" size="small" :class="item.class" @click="childByValue(scope.row, item.mark)">
{{ item.text }}
</el-button>
<el-upload
v-else-if="item.genre === 'upload'"
ref="upload"
style="display: inline-block !important"
class="upload-demo"
:accept="item.accept ? item.accept : '.png,.jpg,.jpeg,.pdf'"
action="s"
:show-file-list="false"
:http-request="
file => {
return upLoadFile(file, scope.row, item.size)
}
"
>
<el-button type="text" class="textGrColor">{{ item.text }}</el-button>
</el-upload>
</span>
</template>
</el-table-column>
</el-table>
</el-form>
<span v-if="localCorptable.pageObj?.total>0">
<el-pagination
v-if="localCorptable.pageObj"
background
:current-page="localCorptable.pageObj.current"
:page-sizes="localCorptable.pageObj.sizes && localCorptable.pageObj.sizes.length > 0 ? localCorptable.pageObj.sizes : [10, 20, 30, 40]"
:page-size="localCorptable.pageObj.size"
layout="total, sizes, prev, pager, next, jumper"
:total="localCorptable.pageObj.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</span>
</div>
</template>
<script>
export default {
name: "PuilceTable",
props: {
corptable: {
type: Object,
default: () => ({})
}
},
data() {
return {
refreshTable: true,
localCorptable: { ...this.corptable }, // 深拷贝初始化
itow: 1,
keyValue: "",
pageshow: true,
isTypeValue: false,
tableFormData: {
tableData: [],
rule: {}
},
echoTable: [],
currentPage: 1,
hStyle: {
background: "#F3F6FF",
color: "#000"
},
cStyle: {
color: "#000"
},
colorRequired: {
color: "red"
},
text: "*"
}
},
watch: {
corptable: {
handler(newD) {
this.localCorptable = { ...newD };
},
deep: true,
immediate: true
},
localCorptable: {
handler(newD) {
this.tableFormData.tableData = newD.list
this.echoTable = newD.echoTable
if (newD.rules) this.tableFormData.rule = newD.rules
else this.tableFormData.rule = {}
this.$forceUpdate()
},
deep: true,
immediate: true
}
},
mounted() {
this.getList()
},
methods: {
// 多选回显
multipleEcho() {
if (this.localCorptable.reserve) {
if (this.echoTable && this.echoTable.length > 0) {
this.$refs.multipleTable.clearSelection()
this.echoTable.forEach(item => {
this.$refs.multipleTable.toggleRowSelection(item, true)
})
} else {
this.$nextTick(() => {
this.$refs.multipleTable.clearSelection()
})
}
}
},
// 按钮权限
btnFunc(_data, _isoperaState) {
const perms = this.$route.meta.perms
if (typeof _isoperaState === "string" || _isoperaState === undefined || _isoperaState == null || _isoperaState === "") {
const _status = _data[_isoperaState !== undefined ? _isoperaState : "status"]
let operation
let newArr
// 判断按钮对象是否为空,为空不在不在向下执行
if (Object.prototype.toString.call(this.localCorptable.operation) === "[object Object]") {
if (!this.localCorptable.operation[typeof _status === "object" ? _status.value : _status]) {
return false
}
}
if (perms.length > 0) {
if (Object.prototype.toString.call(this.localCorptable.operation) === "[object Object]") {
operation = this.localCorptable.operation[typeof _status === "object" ? _status.value : _status]
} else {
operation = this.localCorptable.operation
}
newArr = operation.filter(n => {
return perms.indexOf(n) !== -1
})
} else {
if (Object.prototype.toString.call(this.localCorptable.operation) === "[object Object]") {
newArr = this.localCorptable.operation[typeof _status === "object" ? _status.value : _status]
} else {
newArr = this.localCorptable.operation
}
}
return this.$bp.GetBp().filter(item => {
return newArr.indexOf(item.mark) !== -1
})
} else if (_isoperaState instanceof Object) {
const operation = [] // 用于和路由按钮权限标识匹配的数组
let newArr = [] // 用于和按钮标识JSON文件匹配的数组
const tco = this.localCorptable.operation // 页面规则对象
const BtnLogo = Object.keys(tco) // 页面的规则对象根据key值转成数组
let s // 用于接收替换后的规则
BtnLogo.forEach(i => {
_isoperaState.forEach((t, ind) => {
// 根据页面传递过来的“状态字段”,用于把_data中的数据的“状态字段”的值替换到某一条规则当中
const ReplaceField = JSON.stringify(_data[t] && typeof _data[t] === "object" ? _data[t].value : _data[t])
if (ind === 0) s = tco[i].replace(new RegExp(t, "g"), ReplaceField) // 根据第一次状态字段的循环赋值给s
if (ind > 0) s = s.replace(new RegExp(t, "g"), ReplaceField) // 从第二次开始,直接拿s值去做字符串替换(保证里面所有的判断规则的==号之前的值,都能替换成_data中对应的字段的值用于用于匹配==后面的值)
})
if (eval(s)) {
operation.push(i)
} // 接收到的规则用eval函数执行字符串,为true,则把BtnLogo中的对应的按钮标识push到operation数组中
})
if (perms.length > 0) {
newArr = operation.filter(n => {
return perms.indexOf(n) !== -1
})
} else newArr = operation
return this.$bp.GetBp().filter(item => {
return newArr.indexOf(item.mark) !== -1
}).length > 0
? this.$bp.GetBp().filter(item => {
return newArr.indexOf(item.mark) !== -1
})
: [{ class: "notButton", mark: "", text: "--" }]
}
},
// 表格树异步回调
tableLoad(tree, treeNode, resolve) {
this.$emit("tableLoad", { tree, treeNode, resolve })
},
// ---文件上传(文件上传控制大小)
upLoadFile(parme, row, size) {
const fileSize = Number(parme.file.size / 1024 / 1024)
let isSize = ""
// 默认上传大小为10mb
if (this.isJudge(size)) isSize = size
else isSize = 10
if (fileSize > isSize) {
this.$msgbox({
title: "",
message: "上传图片/文件不可以超过" + isSize + "MB!",
type: "warning"
})
return false
}
this.$emit("childCallbackFunc", [parme, "upload", row])
},
// 选择多选框时的回调
handleSelectionChange(val, type) {
if (type === "0") {
if (val.length > 1) {
this.$refs.multipleTable.clearSelection()
this.$refs.multipleTable.toggleRowSelection(val.pop())
return
} else {
this.$emit("childCallbackFunc", [val, "multiSelect"])
}
} else {
this.$emit("childCallbackFunc", [val, "multiSelect"])
}
},
// 勾选父节点时,子层级一起勾选或一起取消勾选
selectRow(selection, row) {
this.setRowIsSelect(row)
// const data = selection.some((item) => {
// return row[this.localCorptable.rowKey] === item[this.localCorptable.rowKey]
// });
// if (data) {
// // 勾选节点时
// if (row.children) {
// this.setChildren(row.children, true);
// } else {
// }
// } else {
// // 取消勾选节点时
// if (row.children) {
// this.setChildren(row.children, false);
// }
// }
},
// 复选框点击事件
setRowIsSelect(row) {
// 为空字符串则对应行半选态,点击则应该变为全选,则为true
if (row.isSelect === "") {
this.$refs.multipleTable.toggleRowSelection(row, true)
}
row.isSelect = !row.isSelect
const _this = this
// 判断操作的是子级点复选框还是父级点复选框,如果是父级点,则控制子级点的全选和不全选
// 1、只是父级 2、既是子集,又是父级 3、只是子级
const levelStatus = getCurrentLevel(row)
if (levelStatus === 1) {
// 操作的是父级,则所有子集同步父级状态
selectAllChildren(row.children)
} else {
selectAllChildren(row.children)
operateLastLevel(row)
}
// 判断当前操作行的层级状态
function getCurrentLevel(row) {
// 1、只是父级 2、既是子集,又是父级 3、只是子级
if (row.categoryParentIds === undefined) {
return 1
} else if (row.categoryParentIds && !(row.children && row.children.length)) {
return 3
} else {
return 2
}
}
// 递归处理children子级数据的isSelect状态以及对选框状态
function selectAllChildren(children) {
if (children) {
children && children.length && children.forEach((item) => {
item.isSelect = row.isSelect
_this.toggleRowSelection(item, row.isSelect)
if (item.children && item.children.length) {
selectAllChildren(item.children)
}
})
} else {
_this.toggleRowSelection(row, row.isSelect)
}
}
// 子级向上操作父级状态
function operateLastLevel(row) {
// 操作的是子节点,则
// 1、获取父节点
// 2、判断子节点选中个数,如果全部选中则父节点设为选中状态,如果都不选中,则为不选中状态,如果部分选择,则设为不明确状态
const item = getExplicitNode(_this.tableFormData.tableData, row.categoryId)
if (item) {
const selectStatusArr = getSelectStatus([], item.children)
// 判断所有子集选中态,都为true则父级也选中,都为false则不选中,否则为半选
const allSelected = selectStatusArr.every(selectItem => selectItem === true)
const allUnselected = selectStatusArr.every(selectItem => selectItem === false)
if (allSelected) {
item.isSelect = true
_this.toggleRowSelection(item, true)
} else if (allUnselected) {
item.isSelect = false
_this.toggleRowSelection(item, false)
} else {
item.isSelect = ""
}
// 如果当前节点的parentId存在,则还有父级,继续递归往上一层操作
if (item.categoryParentIds !== undefined) {
operateLastLevel(item)
}
}
}
// 递归获取所有子级的选中状态数组
function getSelectStatus(selectStatusArr, children) {
if (!children || !children.length) {
return selectStatusArr
}
children.forEach((child) => {
selectStatusArr.push(child.isSelect)
if (child.children && child.children.length) {
getSelectStatus(selectStatusArr, child.children)
}
})
return selectStatusArr
}
// 获取明确父级的节点
function getExplicitNode(tableData, parentId) {
let result = null
function findNode(data) {
for (let i = 0; i < data.length; i++) {
if (data[i].id === parentId) {
result = data[i]
break
}
if (data[i].children && data[i].children.length) {
findNode(data[i].children)
}
}
}
findNode(tableData)
return result
}
},
// 设置当前行的选中态
toggleRowSelection(row, flag) {
if (row) {
this.$nextTick(() => {
this.$refs.multipleTable &&
this.$refs.multipleTable.toggleRowSelection(row, flag)
})
}
},
// 全选按钮
selectAllRow(selection) {
// 全选
const isSelect = selection.some((item) => {
const tableDataIds = this.tableFormData.tableData.map((data) => data[this.localCorptable.rowKey]);
if (tableDataIds.indexOf(item[this.localCorptable.rowKey]) !== -1) {
return true;
} else {
return false;
}
});
// 全不选
const isCancel = !this.tableFormData.tableData.every((item) => {
const selectIds = selection.map((data) => data[this.localCorptable.rowKey]);
if (selectIds.indexOf(item[this.localCorptable.rowKey]) !== -1) {
return true;
} else {
return false;
}
});
if (isSelect) {
selection.map((item) => {
if (item.children) {
this.setChildren(item.children, true);
}
});
}
if (isCancel) {
this.tableFormData.tableData.map((item) => {
if (item.children) {
this.setChildren(item.children, false);
}
});
}
},
// 父节点含多个子层级
setChildren(children, type) {
children.map((item) => {
this.toggleSelection(item, type);
if (item.children) {
this.setChildren(item.children, type);
}
});
},
toggleSelection(row, select) {
if (row) {
this.$nextTick(() => {
this.$refs.multipleTable.toggleRowSelection(row, select);
});
}
},
// 调用父级方法
childByValue(callback, type) {
const modelName = this.getModelName()
const modelId = this.getModelId()
if (modelName && modelId) {
const action = this.generateAction(callback, type, modelName, modelId)
action ? eval("this.$parent." + action) : this.$emit("childCallbackFunc", [callback, type])
} else {
this.$emit("childCallbackFunc", [callback, type])
}
},
// 监听当前页有多少条数据变化
handleSizeChange(val) {
// this.$emit('childCallbackFunc',[callback,type])
this.$store.state.isPage = false
this.isTypeValue = true
// this.setLoad()
this.$set(this.localCorptable.pageObj, "size", val)
this.getList(this.$parent.queryData)
this.$emit("childCallbackFunc", [val, "page"])
this.$store.state.isPage = false
},
// 监听当前页变化
handleCurrentChange(val) {
this.$store.state.isPage = false
this.isTypeValue = true
// this.setLoad()
this.$set(this.localCorptable.pageObj, "current", val)
this.getList(this.$parent.queryData)
this.$emit("childCallbackFunc", [val, "page"])
this.$store.state.isPage = false
},
// 默认按钮事件
generateAction(callback, type, modelName, modelId) {
let action
switch (type) {
// 分页
case "page":
action = "get" + modelName + "ListPage()"
break
// 修改
case "edit":
action = "showEdit" + modelName + "Dialog(" + eval("callback." + modelId) + ")"
break
case "remove":
action = "delete" + modelName + "(" + eval("callback." + modelId) + ")"
break
}
return action
},
// 获取模块变量
getModelName() {
return this.$parent.modelName
},
// 获取模块Id
getModelId() {
return this.$parent.modelId
},
// 自动执行列表查询
getList(query) {
const modelName = this.getModelName()
const modelId = this.getModelId()
if (modelName && modelId) {
// this.setLoad()
eval("this.$parent.api." + "get" + modelName + "ListPage(" + JSON.stringify(Object.assign({}, this.localCorptable.pageObj, query)) + ")").then(res => {
if (res.code !== 200) return this.$message.error(res.msg)
if (res.data.records) {
this.localCorptable.list = []
this.localCorptable.list = res.data.records
this.localCorptable.pageObj.total = res.data.total
this.$forceUpdate()
} else {
this.localCorptable.list = res.data
}
})
}
},
// 监听输入框
inputChange(val, data) {
this.$emit("childCallbackFunc", [val, "input", data])
this.$forceUpdate()
},
SelectChange(row, field) {
this.$emit("childCallbackFunc", [row, field])
this.$forceUpdate()
},
// 执行循环判断表格form表单
tableFromValidation() {
let v = false
this.$refs.formDataRef.validate(valid => {
if (valid) v = true
else v = false
})
return v
},
onEmpty() {
this.$refs.multipleTable.clearSelection()
}
}
}
</script>
<style lang="scss" scoped>
#puilceTable {
margin-top: 15px;
.buttonClick {
cursor: pointer;
}
::v-deep .el-table thead {
background: #f8f8f8;
color: #000;
}
// .el-table th,
// .el-table tr {
// // background-color: transparent !important;
// }
.el-table tr td:last-child {
background-color: #fff;
}
.el-table__fixed::before,
.el-table__fixed-right::before {
background-color: transparent;
}
.el-table__fixed,
.el-table__fixed-right {
-webkit-box-shadow: 0 -10px 10px rgb(0, 0, 0, 0.12);
box-shadow: 0 -10px 10px rgb(0, 0, 0, 0.12);
}
.buttonS {
margin: 0 4px;
}
.el-checkbox__input.is-checked .el-checkbox__inner,
.el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #409eff;
border-color: #409eff;
}
.el-table .cell.el-tooltip {
.el-form-item {
margin-bottom: 13px;
.el-input {
height: 30px;
margin-top: 10px;
}
.el-input__icon {
line-height: 30px;
}
.el-input__inner {
height: 30px;
padding: 0 5px;
}
.el-form-item__error {
top: 85%;
}
}
}
}
</style>
以下文章描述如何使用这个组件