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

867 lines
25 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"
use-virtual
:row-height="50"
:height="uTableHeight"
header-row-class-name="uTableHeader"
>
<!-- :tree-props="treeProps" 与 “height” 不能共存, 此组件暂不支持tree的格式-->
<!-- 操作列 (左侧)-->
<u-table-column
v-if="buttonOperationList_left"
:fixed="'left'"
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_left"
:key="indexButton"
type="text"
size="mini"
@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"
:style="{color:itemButton.color || '#409EFF'}"
@click="buttonOperationClick_right(scope.row, itemButton, indexButton)"
>{{itemButton.label}}</el-button>
</template>
</u-table-column>
<u-table-column v-if="selectionTable" fixed="left" type="selection" :reserve-selection="true" />
<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">
<!-- 时间转换 -->
<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>
</template>
</u-table-column>
</template>
<slot></slot>
<!-- 点开查看全部弹窗 -->
<el-dialog
: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
type="textarea"
readonly
autosize
resize="none"
v-model="showDetailData">
</el-input>
<!-- {{ showDetailData ? showDetailData + "" : showDetailData }} -->
</el-dialog>
<!-- json弹窗 -->
<el-dialog
id="copyJsonTextarea_dialog_ref"
:visible.sync="showJsonCopy"
:modal-append-to-body="false"
:append-to-body="true"
:show-close="true"
:close-on-click-modal="true"
:close-on-press-escape="true"
:title="'JSON详情'"
>
<el-input
ref="copyJsonTextarea_ref"
type="textarea"
readonly
autosize
resize="none"
v-model="showJsonData_str">
</el-input>
</el-dialog>
<!-- 点开查看Json转换后table弹窗 -->
<el-dialog
:visible.sync="showJsonDialog"
:modal-append-to-body="false"
:append-to-body="true"
:show-close="true"
:close-on-click-modal="true"
:close-on-press-escape="true"
:title="'内容详情'"
width="80%"
>
<el-button
@click="copyJsonHandle()"
type="primary"
style="margin-bottom: 10px;float: right;"
>复制JSON</el-button>
<!-- <el-table
:data="showJsonData_str"
stripe
style="width: 100%"
>
<el-table-column
v-for="(item,key) in showJsonHeader"
>
<template #header>
<span>{{ item }}</span>
</template>
<template slot-scope="scope">
</template>
</el-table-column>
</el-table> -->
<el-table
:data="showJsonData"
:border="true"
style="width: 100%">
<el-table-column
prop="name"
width="220"
label="属性">
</el-table-column>
<el-table-column
label="值"
>
<template slot-scope="scope">
<!-- DETAILS todo:DETAILS判断条件需要优化,使用传参的方式 -->
<span v-if="(scope.row.name).toUpperCase() != 'DETAILS'">{{scope.row.value}}</span>
<el-table
v-else
:data="scope.row.value"
stripe
:border="true"
style="width: 100%"
height="300"
>
<el-table-column
v-for="(head,h_key) in showJsonHeader"
:key="h_key"
min-width="120"
>
<template #header>
<span :title="head">{{ head }}</span>
</template>
<template slot-scope="scope2">
<div v-if="typeof scope2.row[head] == 'object'">
<div v-for="(o,o_key) in scope2.row[head]">
{{ o_key }}:{{ o }}
<div style="border-bottom: dashed 1px #ddd;"></div>
</div>
</div>
<div v-else>{{ scope2.row[head] }}</div>
</template>
</el-table-column>
</el-table>
<!-- <el-table
v-else
height="300"
:data="scope.row.value"
:border="true"
style="width: 100%">
<el-table-column
prop="name"
label="子属性"
></el-table-column>
<el-table-column
label="子值"
prop="value"
></el-table-column>
</el-table> -->
</template>
</el-table-column>
</el-table>
</el-dialog>
</u-table>
</template>
<script>
import { formatTimeStrToStr } from "@/utils/formatTime";
import _ from "lodash";
import { getMatchRegConformValue } from "@/utils/index"
export default {
name: "currenTable",
filters: {
formatDate(time) {
if (time == null) {
return '-'
}
return formatTimeStrToStr(time)
},
},
props: {
// 操作列按钮(左侧)
buttonOperationList_left:{
type: Array,
default: null,
},
// 操作列按钮(右侧)
buttonOperationList_right:{
type: Function,
default: null,
},
// 超出内容是否提示
showOverflowTooltip:{
type: Boolean,
default: false,
},
// table是否有border
tableBorder: {
type: Boolean,
default: false,
},
// 是否为第一个浮动,如果是,则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;
}
},
// 已app-main高度为100% 需要减掉的高度
setUTableHeight: {
type: Number,
default: () => {
return 280;
}
}
},
data() {
return {
dropCol: null,
selectLoading: false,
random: '',
uTableHeight:null,//表格撑开高度
showDetailDialog:false,//点开查看全部弹窗
showDetailData:null,//点开查看全部内容
showJsonDialog:false,//点开查看Json转换后table弹窗
showJsonHeader:null,//点开查看Json的header
showJsonData:null,//点开查看Json的数据
showJsonData_str:null,//复制的Json字符串
showJsonCopy:false,//复制json的Dialog显隐控制
};
},
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: {
// 设置表格高度
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
this.showDetailData = type == 'time' ? formatTimeStrToStr(row) : row
this.$emit("showDetailInfo", this.showDetailData);
},
// 可点出json转换的table弹窗
showJsonTable(row){
this.showJsonDialog = true
let _json = eval('(' + row + ')')
this.showJsonData_str = JSON.stringify(_json, null, '\t')//复制使用
let _arr = []
let __initJson = (data) => {
let _init = []
for(let item in data){
_init.push({name:item,value:data[item]})
}
return _init
}
for(let item in _json){
// 直接输出
if(!_json[item]){
_arr.push({name:item,value:_json[item]})
}else{
if(typeof _json[item] != 'object'){
_arr.push({name:item,value:_json[item] + ""})
}else{
// 如果是数组
if(Array.isArray(_json[item])){
// 如果是 DETAILS todo:DETAILS判断条件需要优化,使用传参的方式
// let _value = (item).toUpperCase() == 'DETAILS' ? __initJson(_json[item][0]) : (_json[item]).join(",")
this.showJsonHeader = []
for(let h in _json[item][0]){
this.showJsonHeader.push(h)
}
let _value = (item).toUpperCase() == 'DETAILS' ? _json[item] : (_json[item]).join(",")
_arr.push({name:item,value:_value})
}
// 如果是对象
else{
let _obj_arr = __initJson(_json[item])
// 对象有数据
if(_obj_arr.length > 0){
_arr = [..._obj_arr]
}else{
// 空对象
_arr.push({name:item,value:""})
}
}
}
}
}
this.showJsonData = _arr
this.$emit("showJsonTable", row);
},
// 复制Json按钮
copyJsonHandle(){
this.showJsonCopy = true
if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(this.showJsonData_str)
.then(() => {
this.$message.success('复制成功');
this.$refs.copyJsonTextarea_ref.focus()
document.getElementById('copyJsonTextarea_dialog_ref').scrollTop = 0
})
.catch(err => {
this.$message.error('复制失败');
this.$refs.copyJsonTextarea_ref.focus()
document.getElementById('copyJsonTextarea_dialog_ref').scrollTop = 0
});
}else {
// 创建text area
const textArea = document.createElement('textarea')
textArea.value = this.showJsonData_str
// 使text area不在viewport,同时设置不可见
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
return new Promise((resolve, reject) => {
// 执行复制命令并移除文本框
document.execCommand('copy') ? resolve() : reject(new Error('出错了'))
textArea.remove()
}).then(() => {
this.$nextTick(()=>{
if(this.$refs.copyJsonTextarea_ref){
this.$message.success('复制成功');
this.$refs.copyJsonTextarea_ref.focus()
document.getElementById('copyJsonTextarea_dialog_ref').scrollTop = 0
}
})
},() => {
this.$message.error('复制失败');
this.$refs.copyJsonTextarea_ref.focus()
document.getElementById('copyJsonTextarea_dialog_ref').scrollTop = 0
})
}
},
//点击按钮打开自定义弹窗
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 = {}
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)
},
},
};
</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: 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-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
}
</style>