埃驰前端
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.
 
 
 
 

802 lines
23 KiB

<template>
<!-- 解决el-table 数据量过大导致卡顿现象 -->
<u-table
:id="_uid"
:key="isUpdate"
v-loading="tableLoading"
@sort-change="sortChange"
@selection-change="handleSelectionChange"
ref="multipleTable"
:data="tableData"
:row-key="rowKey"
:border="tableBorder"
style="width: 100%"
:cell-style="cellStyle"
:cell-class-name="cellClassName"
use-virtual
:height="uTableHeight"
header-row-class-name="uTableHeader"
>
<!-- :tree-props="treeProps" 与 “height” 不能共存, 此组件暂不支持tree的格式-->
<u-table-column
v-if="selectionTable"
fixed="left"
type="selection"
:reserve-selection="true"
:selectable="selectionHandle"
/>
<u-table-column v-if="isShowIndex" type="index" fixed="left" label="序号" width="50" />
<template v-for="(item, index) in TableSize">
<u-table-column
min-width="150"
:key="index"
:prop="item.showProp ? item.prop + '.' + item.showProp : item.prop"
:sortable="item.sortable"
:fixed="setItemFixed(item,index)"
:show-overflow-tooltip="showOverflowTooltip"
:width="item.width"
:align="item.tableAlign"
:header-align="item.tableHeaderAlign"
v-if="item.istrue==null?true:item.istrue"
>
<template #header>
<span>{{ item.label }}</span>
<i style="color: #f56c6c" v-if="item.rules && requiredRules">*</i>
</template>
<template slot-scope="scope">
<!-- 正常表格回显 -->
<div v-if="!isEditable">
<!-- 时间转换 -->
<span v-if="item.apiBaseType == 'datetime'"
@click="showDetailInfo(scope.row[item.prop],'time')"
style="cursor: pointer;"
:title="'点击查看详情'"
class="showDetailHover itemSpan"
>
{{ scope.row[item.prop] | formatDate }}
</span>
<!-- 枚举 -->
<span v-else-if="item.isEnums" class="itemSpan">
{{ initApiEnumList(item,scope.row[item.prop]) }}
</span>
<!-- 布尔 -->
<span v-else-if="item.apiBaseType == 'boolean'" class="itemSpan">
{{ scope.row[item.prop] ? '是' : '否' }}
</span>
<!-- 数值 -->
<span v-else-if="item.apiBaseType == 'number'" class="itemSpan">
{{ scope.row[item.prop] }}
</span>
<!-- 点击可出详情 | 点击可点出json (目前已知String|Guid)-->
<span v-else
class="itemSpan"
@click="showTypeHandle(initApiOtherType(scope.row[item.prop])[1],scope.row[item.prop])"
:style="initApiOtherType(scope.row[item.prop])[1] != 'show' ? 'cursor: pointer' : ''"
:title="'点击查看详情'"
:class="{ showDetailHover: initApiOtherType(scope.row[item.prop])[1] != 'show' }"
>
{{ initApiOtherType(scope.row[item.prop])[0] }}
</span>
</div>
<!-- 可编辑表格 -->
<div v-else>
<!-- 时间转换 -->
<el-date-picker
v-if="item.apiBaseType === 'datetime'"
v-model="scope.row[item.prop]"
type="datetime"
placeholder="选择日期时间"
format="yyyy-MM-dd HH:mm:ss"
value-format="yyyy-MM-ddTHH:mm:ss"
:disabled="Boolean(item.disabled)"
size="mini"
></el-date-picker>
<!-- 布尔、枚举 -->
<el-select
v-else-if="item.isEnums || item.apiBaseType === 'boolean'"
v-model="scope.row[item.prop]"
:placeholder="item.placeholder || '请选择' + item.label"
:disabled="Boolean(item.disabled)"
size="mini"
>
<el-option
v-for="item in item.enums_list"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<!-- 数值 -->
<el-input-number
v-else-if="item.apiBaseType === 'number'"
v-model="scope.row[item.prop]"
:min="item.minimum || undefined"
:max="item.maximum || undefined"
:maxlength="item.maxLength || undefined"
:minlength="item.minLength || undefined"
:disabled="Boolean(item.disabled)"
:placeholder="item.placeholder || '请输入' + item.label"
@change="changeValue(item.prop,item,$event)"
@clear="clearValue(item.prop,$event)"
size="mini"
></el-input-number>
<!-- 文本值 -->
<el-input
v-else
v-model="scope.row[item.prop]"
:placeholder="item.placeholder || '请输入' + item.label"
:disabled="Boolean(item.disabled)"
size="mini"
></el-input>
</div>
</template>
</u-table-column>
</template>
<!-- 操作列 (左侧)-->
<u-table-column
v-if="buttonOperationList_left"
:fixed="'left'"
:width="operationLeftColumnWidth()"
min-width="120px"
:align="'center'"
:header-align="'center'"
>
<template #header>
<span>{{ $t('common.handle') }}</span>
</template>
<template slot-scope="scope">
<el-button
v-for="(itemButton, indexButton) in buttonOperationList_left"
:key="indexButton"
type="text"
size="mini"
:style="{color:itemButton.color || '#409EFF'}"
@click="buttonOperationClick_left(scope.row, itemButton, indexButton)"
>{{itemButton.label}}</el-button>
</template>
</u-table-column>
<!-- 操作列 (右侧)-->
<u-table-column
v-if="buttonOperationList_right"
:fixed="'right'"
width="auto"
min-width="120px"
:align="'center'"
:header-align="'center'"
>
<template #header>
<span>{{ $t('common.handle') }}</span>
</template>
<template slot-scope="scope">
<el-button
v-for="(itemButton, indexButton) in buttonOperationList_right(scope.row)"
v-show="!itemButton.hide"
:key="indexButton"
type="text"
size="mini"
v-permission="itemButton.permission || []"
:style="{color:itemButton.color || '#409EFF'}"
@click="buttonOperationClick_right(scope.row, itemButton, indexButton)"
>{{itemButton.label}}</el-button>
</template>
</u-table-column>
<slot></slot>
<!-- 点开查看全部弹窗 -->
<el-dialog
top="50px"
:visible.sync="showDetailDialog"
width="35%"
:modal-append-to-body="false"
:append-to-body="true"
:show-close="true"
:title="'内容详情'"
:close-on-click-modal="true"
:close-on-press-escape="true"
>
<el-input
class="copyDetailInput"
ref="copyDetailInput_ref"
type="textarea"
readonly
autosize
resize="none"
v-model="showDetailData">
</el-input>
<!-- {{ showDetailData ? showDetailData + "" : showDetailData }} -->
</el-dialog>
<!-- 查看Json -->
<showCopyJsonPop
v-if="showJsonDialog"
:JsonData="showJsonData"
@closePop="closeJsonPop"
></showCopyJsonPop>
</u-table>
</template>
<script>
import { formatTimeStrToStr } from "@/utils/formatTime";
import _ from "lodash";
import { getMatchRegConformValue } from "@/utils/index"
import showCopyJsonPop from "@/components/showCopyJsonPop"
import permission from "@/directive/permission/index"
export default {
name: "currenTable",
directives: { permission },
components:{ showCopyJsonPop },
filters: {
formatDate(time) {
if (time == null) {
return '-'
}
return formatTimeStrToStr(time)
},
},
props: {
// table内表单数据是否可编辑
isEditable:{
type: Boolean,
default: false
},
// 操作列按钮(左侧)
buttonOperationList_left:{
type: Array,
default: null,
},
// 操作列按钮(右侧)
buttonOperationList_right:{
type: Function,
default: null,
},
// 超出内容是否提示
showOverflowTooltip:{
type: Boolean,
default: false,
},
// table是否有border
tableBorder: {
type: Boolean,
default: true,
},
// 是否为第一个浮动,如果是,则item中的fixed无效(应用场景:主列表,因为用户排序不定元素为第一项)
firstFixed: {
type: Boolean,
default: false,
},
propsData: {
type: Object,
default: () => {
return {};
},
},
tableData: {
type: Array,
default: () => {
return [];
},
},
tableLoading: {
type: Boolean,
default: false,
},
tableColumns: {
type: Array,
default: () => {
return [];
},
},
selectionTable: {
type: Boolean,
default: true,
},
requiredRules: {
type: Boolean,
default: true,
},
searchOptions: {
type: Object,
default: () => {
return {};
},
},
treeProps: {
type: Object,
default: () => {
return {};
}
},
// table key 字段设置顺序后需更新该值
isUpdate: {
type: Boolean,
default: () => {
return false
}
},
rowKey:{
type: String,
default: 'id',
},
isShowIndex: {
type: Boolean,
default: false,
},
cellStyle: {
type: Function,
default: () => {
return Function;
}
},
cellClassName: {
type: Function,
default: () => {
return Function;
}
},
// 已app-main高度为100% 需要减掉的高度
setUTableHeight: {
type: Number,
default: () => {
return 280;
}
},
// 多选列处理
selectionColumnHandle:{
type: Function,
default: null
}
},
data() {
return {
dropCol: null,
selectLoading: false,
random: '',
uTableHeight:null,//表格撑开高度
showDetailDialog:false,//点开查看全部弹窗
showDetailData:null,//点开查看全部内容
showJsonDialog:false,//点开查看Json转换后table弹窗
showJsonData:null,//点开查看Json的数据
};
},
computed: {
selectOptions() {
return (val) => {
if (val) {
let options = this.$staticOptions[val];
if (options) {
return options();
} else {
return [];
}
} else {
return false;
}
};
},
TableSize(){
return this.tableColumnsFilter()
}
},
watch: {
tableData: {
handler(val, oldVal) {
this.$nextTick(() => {
if (this.$refs.multipleTable && this.$refs.multipleTable.doLayout) {
this.$refs.multipleTable.doLayout()
}
});
},
immediate: false,
},
tableColumns: {
handler(val, oldVal) {
this.$nextTick(() => {
if (this.$refs.multipleTable && this.$refs.multipleTable.doLayout) {
this.$refs.multipleTable.doLayout()
}
});
},
immediate: false,
},
setUTableHeight(n,o){
this.setTableHeightHandle(n)
}
},
activated() {
this.$refs.multipleTable.doLayout();
},
mounted() {
this.random = this._uid
this.setTableHeightHandle()
},
methods: {
// 多选列处理
selectionHandle(row,index){
return this.selectionColumnHandle(row,index)
},
// 清除已选(外部使用,勿删)
clearTableSelection(){
this.$refs.multipleTable.clearSelection();
},
// 左侧操作列宽度适应
operationLeftColumnWidth(){
if(this.buttonOperationList_left.length == 1){return 120}
else{return this.buttonOperationList_left.length * 100}
},
// 重新渲染表格
doFreshLayout(){
if (this.$refs.multipleTable && this.$refs.multipleTable.doLayout) {
this.$refs.multipleTable.doLayout()
}
},
// 设置表格高度
setTableHeightHandle(height){
let _height = height || this.setUTableHeight
let _app_height = document.getElementsByClassName('app-main')[0].clientHeight
this.uTableHeight = Number(_app_height) - Number(_height)
this.$nextTick(() => {
this.$refs.multipleTable.doLayout()
this.$refs.multipleTable.$forceUpdate()
});
},
// 获取表格高度 勿删 业务当中有用
getTableHeight(){
return this.uTableHeight
},
setItemFixed(item,index){
let _re = false
if(this.firstFixed && item.fixed == 'left'){
if(index == 0)_re = true
}else{
_re = item.fixed
}
return _re
},
// type=input情况下,框实时校验
itemOnKeyUp(item,value){
if(item.onkeyup){
return item.onkeyup()
}else{
if(value && item.validType){
this.searchData[item.prop]=getMatchRegConformValue(item.validType,value,item.pointNumberFixed)
}
}
},
// 数字类型input框onkeyup最大最小值处理
typeNumberOnkeyup(item,value){
if(value){
let _match = String(value).match(/\d+/)//正整数
this.searchData[item.prop] = _match?_match[0]:_match
}
if(this.searchData[item.prop] > item.max){
this.searchData[item.prop] = item.max
}
if(this.searchData[item.prop] && this.searchData[item.prop] < item.min){
this.searchData[item.prop] = item.min
}
if(item.onkeyup)item.onkeyup()
},
tableColumnsFilter() {
let widthSize = _.cloneDeep(this.tableColumns);
for(let i = 0;i<widthSize.length;i++){
let item = widthSize[i]
if (item.type == "autocomplete" || item.type == "objectAutocomplete") {
item.width = item.width ? item.width: "300px";
} else if (item.type == "input" || item.type == "objectInput") {
if (item.width == '100%') {
item.width = ''
} else {
item.width = item.width ? item.width: "200px";
}
} else if (
item.type == "dateTimeInput" ||
item.type == "objectDateTimeInput"
) {
item.width =item.width ? item.width: "200px";
} else if (item.type == "objectDateTime" || item.type == "dateTime") {
item.width =item.width ? item.width: "200px";
} else if (item.width == '100%') {
item.width = ''
} else {
item.width =item.width ? item.width: 'auto';
}
}
return widthSize
},
inputPlaceholder(val, item, type, func) {
if (item.valueType) {
if (type == "focus") {
val.target.placeholder = "请输入" + item.label;
if (val.target.value != "") {
val.target.placeholder = val.target.value;
val.target.value = "";
}
} else if (type == "blur") {
// val.target.value = "0"
val.target.value = val.target.value.toString().replace(/[^\d.]/g,'')
if (val.target.value == "") {
// if (val.target.placeholder.indexOf('请输入') == -1) {
val.target.value = val.target.placeholder;
// }
}
if(item.showProp){
func[item.prop][item.showProp]= Number(val.target.value)
}else{
func[item.prop]= Number(val.target.value)
}
}
}
},
//autocomplete下拉
querySearch(queryString, cb, val, row) {
const { options, optionsValue, optionsLabel } = val;
let func = val.focus;
let data = {
scope: row,
};
if (queryString) {
data.filter = [
{
logic: "And",
column: optionsValue,
action: "Like",
value: queryString,
},
];
}
func(data).then((res) => {
var restaurants = this.searchOptions["options"];
let results = queryString
? restaurants.filter(
this.createFilter(queryString, optionsValue, optionsLabel)
)
: restaurants;
results.forEach((key) => {
if (typeof optionsValue === "string") {
key.value = key[optionsValue] + "----" + key[optionsLabel];
} else {
key.value =
key[optionsValue[0]][optionsValue[1]] +
"----" +
key[optionsLabel];
}
});
// 调用 callback 返回建议列表的数据
cb(results);
});
},
createFilter(queryString, optionsValue, optionsLabel) {
return (restaurant) => {
if (typeof optionsValue === "string") {
restaurant.value =
restaurant[optionsValue] + "----" + restaurant[optionsLabel];
} else {
restaurant.value =
restaurant[optionsValue[0]][optionsValue[1]] +
"----" +
restaurant[optionsLabel];
}
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) !=
-1
);
};
},
handleSelect(item, val, scope) {
let data = this.tableData[scope.$index];
if (!val.showProp) {
if (typeof val.optionsValue === "string") {
data[val.prop] = item[val.optionsValue];
} else {
data[val.prop] = item[val.optionsValue[0]][val.optionsValue[1]];
}
} else {
if (typeof val.optionsValue === "string") {
data[val.prop][val.showProp] = item[val.optionsValue];
} else {
data[val.prop][val.showProp] =
item[val.optionsValue[0]][val.optionsValue[1]];
}
}
this.$emit("push", item, scope, val);
},
getMaxLength(arr) {
return arr.reduce((acc, item) => {
if (item) {
const calcLen = this.getTexWidth(item);
if (acc < calcLen) {
acc = calcLen;
}
}
return acc;
}, 0);
},
getTexWidth(str) {
let width = 0;
const html = document.createElement("span");
html.innerText = str;
html.className = "getTextWidth";
document.querySelector("body").appendChild(html);
width = document.querySelector(".getTextWidth").offsetWidth;
document.querySelector(".getTextWidth").remove();
return width;
},
flexColumnWidth(label, prop) {
const arr = this.tableData.map((x) => {
if (typeof prop !== "string") {
return x[prop[0]][prop[1]];
} else {
return x[prop];
}
});
arr.push(label);
return this.getMaxLength(arr) + 60 + "px";
},
//排序
sortChange(data) {
this.$emit("sortChange", data);
},
//点击selection框
handleSelectionChange(val) {
this.$emit("handleSelectionChange", val);
},
//点击name提交emit打开编辑页面
inlineDialog(row) {
this.$emit("inlineDialog", row);
},
//可点出详情弹窗
showDetailInfo(row,type) {
this.showDetailDialog = true
let _row = typeof row == 'object' ? JSON.stringify(row, null, '\t') : row
this.showDetailData = type == 'time' ? formatTimeStrToStr(row) : _row
this.$emit("showDetailInfo", this.showDetailData);
this.$nextTick(()=>{
if(this.$refs.copyDetailInput_ref){
this.$refs.copyDetailInput_ref.focus()
this.$refs.copyDetailInput_ref.$refs.textarea.scrollTop = 0
}
})
},
closeJsonPop(){
this.showJsonDialog = false
},
// 可点出json转换的table弹窗
showJsonTable(row){
this.showJsonDialog = true
let _json = eval('(' + row + ')')
this.showJsonData = _json
},
//点击按钮打开自定义弹窗
buttonClick(row, index, label) {
this.$emit("buttonClick", row, index, label);
},
//点击table操作列(左侧)按钮
buttonOperationClick_left(row, item, index) {
this.$emit("buttonOperationClick_left", row, item, index);
},
//点击table操作列(右侧)按钮
buttonOperationClick_right(row, item, index) {
this.$emit("buttonOperationClick_right", row, item, index);
},
// 转义枚举值
initApiEnumList(item,data){
let _item_enumList = {}
if(item.enums_list){
item.enums_list.forEach((item,key)=>{
_item_enumList[item.value] = item.label
})
}
return _item_enumList[data] || '未定义'
},
// 其他类型转义,直接显示 | 点击可出详情 | 点击可点出json
initApiOtherType(data){
try {
let _json = JSON.parse(data)
// 数值类型
if(typeof _json == 'number' && _json){
return [data,'show']
}else{
return [data,'json']
}
}
// 直接显示
catch(err){
return [data,'detail']
}
},
// 判断操作
showTypeHandle(type,row){
if(type == 'detail')this.showDetailInfo(row)
if(type == 'json')this.showJsonTable(row)
},
// 可编辑表单:值更改函数
changeValue(prop,item,val) {
this.$emit("changeValue", prop, item, val)
},
// 可编辑表单:值清除函数
clearValue(prop,item,val) {
this.$emit("clearValue", prop, item, val)
},
},
};
</script>
<style lang="scss" scoped>
::v-deep .uTableHeader th{
background: #f6f7fb !important;
border: none !important;
// &:last-child{
// background: unset !important;
// }
}
::v-deep .el-form-item__error{
position: relative;
}
::v-deep .el-table__fixed {
display: block-inline !important;
height: 100% !important;
// bottom: 13px !important;
.el-table__fixed-header-wrapper {
z-index: auto !important;
}
.el-table__fixed-body-wrapper {
z-index: auto !important;
}
}
::v-deep .el-table__fixed-right {
display: block-inline !important;
height: auto !important;
bottom: 13px !important;
.el-table__fixed-header-wrapper {
z-index: auto !important;
}
.el-table__fixed-body-wrapper {
z-index: auto !important;
}
}
::v-deep .el-table__fixed::before,
.el-table__fixed-right::before {
z-index: auto !important;
}
.spamHover {
color: #409eff;
cursor: pointer;
}
.spamHover:hover {
border-bottom: 1px solid #409eff;
color: blue;
}
.showDetailHover:hover{
color: #409eff;
}
span {
white-space: pre;
}
::v-deep .el-select {
.el-input__inner {
padding: 0 30px 0 15px;
}
.el-input__prefix {
width: 100% !important;
left: 0;
.el-button {
position: absolute;
top: 0;
right: 0;
}
}
}
.u-table::before {
height: 0px;
}
.itemSpan{
white-space: nowrap !important
}
::v-deep .copyDetailInput{
textarea{
max-height: calc(100vh - 220px) !important;
overflow: auto !important;
}
}
</style>