<template>
|
<div class="app-table-pro" v-loading="columnsLoading || false">
|
<div class="header">
|
<div class="title">
|
{{ $props.title }}
|
<el-popover
|
placement="bottom"
|
title="多选列表"
|
:width="'60%'"
|
trigger="click"
|
v-if="selection"
|
>
|
<el-table
|
show-overflow-tooltip
|
:data="selectedList"
|
border
|
:height="300"
|
>
|
<template v-for="e in groupColumns" :key="e.id">
|
<el-table-column v-if="e.group.indexOf('$') === -1" :label="e.group">
|
<template v-for="column in e.columns" :key="column.id">
|
<table-column :columns="[column]">
|
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
|
<slot :name="slot" v-bind="scope"/>
|
</template>
|
</table-column>
|
</template>
|
</el-table-column>
|
<table-column :columns="e.columns">
|
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
|
<slot :name="slot" v-bind="scope"/>
|
</template>
|
</table-column>
|
<el-table-column
|
label="操作"
|
width="70"
|
fixed
|
>
|
<template #default="scope">
|
<el-button
|
style="padding: 5px 10px;"
|
type="danger"
|
icon="Delete"
|
@click="onSelectedDel(scope.row)"
|
>
|
</el-button>
|
</template>
|
</el-table-column>
|
</template>
|
</el-table>
|
<template #reference>
|
<el-badge :value="selectedList.length">
|
<el-button icon="Tickets" link class="icon">
|
</el-button>
|
</el-badge>
|
</template>
|
</el-popover>
|
</div>
|
<div class="button">
|
<div>
|
<slot name="header-button"/>
|
</div>
|
</div>
|
<div class="tools">
|
<show-setting v-bind="showSettingProps"/>
|
<col-setting v-bind="colSettingProps"/>
|
<el-button class="btn" @click="getTableData(false)">
|
<el-icon>
|
<refresh-left/>
|
</el-icon>
|
</el-button>
|
<el-button class="btn" @click="settingForm.isLock = !settingForm.isLock">
|
<el-icon>
|
<Lock v-show="settingForm.isLock"/>
|
<Unlock v-show="!settingForm.isLock"/>
|
</el-icon>
|
</el-button>
|
</div>
|
</div>
|
<div
|
v-loading="loading"
|
class="card" v-show="settingForm.showSetting === 'card'"
|
:style="`height:${settingForm.isLock ? `${settingForm.lockHeight}px` : 'inherit'}`">
|
<el-card
|
:class="`item ${data[rowKey] === dataForm.current[rowKey] ? 'active' : ''}`"
|
v-for="data in tableData"
|
:key="data[rowKey]"
|
shadow="hover"
|
@click="onTableCurrent(data)"
|
>
|
<el-row>
|
<template v-for="item in useColumns" :key="item.id">
|
<el-col v-if="item.isShow && item.type === 'slot'" v-bind="item.card">
|
<div class="text">
|
<app-text-ellipsis :value="item.label" class="label"/>
|
<slot :name="`card-${item.prop}`" v-bind="{row:data}"/>
|
</div>
|
</el-col>
|
<el-col v-if="item.isShow && !otherType.includes(item.type)" v-bind="item.card">
|
<div class="text">
|
<app-text-ellipsis :value="item.label" class="label"/>
|
<app-text-ellipsis :value="formatData(item.format, getProperty(data,item.prop))" class="value"/>
|
</div>
|
</el-col>
|
|
<el-col v-if="item.isShow && item.type === 'dict'" v-bind="item.card">
|
<div class="text">
|
<app-text-ellipsis :value="item.label" class="label"/>
|
<app-text-ellipsis :value="formatData(item.format, item.options.dict[data[item.prop]])"
|
class="value"/>
|
</div>
|
</el-col>
|
|
</template>
|
</el-row>
|
</el-card>
|
<el-empty v-if="!tableData.length"/>
|
</div>
|
<el-table
|
ref='tableRef'
|
v-loading="loading"
|
v-show="settingForm.showSetting === 'table'"
|
v-bind="$props"
|
show-overflow-tooltip
|
:highlight-current-row="!selection"
|
:height="settingForm.isLock ? settingForm.lockHeight : 'inherit' "
|
:data="tableData"
|
:border="border"
|
:row-key="rowKey"
|
:header-cell-class-name="setHeaderClass"
|
:cell-style="cellStyle"
|
:expand-row-keys="expandRowKeys"
|
@sort-change="sortChange"
|
@current-change="onTableCurrent"
|
@select="selectionChange"
|
@select-all="selectionChange"
|
>
|
<el-table-column type="expand" v-if="expand">
|
<template #default="scope">
|
<slot name="table-expand" v-bind="scope"/>
|
</template>
|
</el-table-column>
|
|
<el-table-column type="selection" v-if="selection" width="40">
|
</el-table-column>
|
|
<template v-for="e in groupColumns" :key="e.id">
|
|
<el-table-column v-if="e.group.indexOf('$') === -1" :label="e.group">
|
<template v-for="column in e.columns" :key="column.id">
|
<table-column :columns="[column]">
|
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
|
<slot :name="slot" v-bind="scope"/>
|
</template>
|
</table-column>
|
</template>
|
</el-table-column>
|
<table-column :columns="e.columns">
|
<template v-for="slot in Object.keys($slots)" #[slot]="scope">
|
<slot :name="slot" v-bind="scope"/>
|
</template>
|
</table-column>
|
</template>
|
<!-- 插入表格最后一行之后的插槽 -->
|
<template #append>
|
<slot name="append"/>
|
</template>
|
<!-- 无数据 -->
|
<template #empty>
|
<el-empty/>
|
</template>
|
</el-table>
|
<el-pagination
|
class="pagination"
|
small
|
:background="true"
|
v-model:current-page="pageAble.pageNo"
|
v-model:page-size="pageAble.pageSize"
|
:page-sizes="[10, 50, 200, 400]"
|
layout="total, sizes, prev, pager, next, jumper"
|
:total="pageAble.recordCount"
|
@size-change="handleSizeChange"
|
@current-change="handleCurrentChange"
|
/>
|
</div>
|
</template>
|
|
<script setup>
|
import * as tools from '@/utils/tools.js'
|
|
import {useSettingStore} from '@/store/modules';
|
|
import ColSetting from "./ColSetting";
|
import ShowSetting from "./ShowSetting";
|
import TableColumn from './TableColumn';
|
|
const settingsStore = useSettingStore();
|
|
import {table, context} from '@/hooks';
|
|
const {useForceUpdate} = context;
|
|
import {getProperty} from "@/utils/tools.js";
|
|
const {useTable, useSelection, useExpand} = table;
|
|
const tableRef = ref();
|
const colSettingRef = ref();
|
|
const otherType = ['slot', 'dict'];
|
|
const props = defineProps({
|
columns: {
|
type: Array,
|
default: []
|
},
|
border: {
|
type: Boolean,
|
default: true
|
},
|
title: {
|
type: String,
|
default: ''
|
},
|
rowKey: {
|
type: String,
|
default: 'id'
|
},
|
expand: {
|
type: Boolean,
|
default: false
|
},
|
selection: {
|
type: Boolean,
|
default: false
|
},
|
subRequest: {
|
type: Function,
|
default: () => {
|
|
}
|
},
|
subCurrent: {
|
type: Function,
|
default: () => {
|
|
}
|
},
|
complementHeight: {
|
type: Number,
|
default: 0
|
},
|
subSelection: {
|
type: Function,
|
default: () => {
|
}
|
},
|
});
|
|
const [$props, forceUpdate] = useForceUpdate(props);
|
|
const $forceUpdate = async (force) => {
|
forceUpdate(force);
|
if (columnsLoading.value) {
|
columnsLoading.value = !Boolean(force.columns.length);
|
}
|
onInit();
|
}
|
|
const columnsLoading = ref(!Boolean($props.columns.length));
|
|
const {border, rowKey, expand, complementHeight} = $props;
|
|
const [
|
{
|
tableData,
|
pageAble,
|
loading,
|
},
|
{
|
sortChange,
|
setHeaderClass,
|
getTableData,
|
handleSizeChange,
|
handleCurrentChange,
|
}
|
] = useTable(props.subRequest);
|
|
const [selectionChange, selectedList, selectedIds, toggleSelection] = useSelection(rowKey, tableRef, tableData);
|
|
const [expandRowKeys, clearExpand] = useExpand();
|
|
const cellStyle = ({row}) => ({
|
backgroundColor: selectedIds.value.includes(row[rowKey]) ? 'var(--el-color-primary-light-6)' : ''
|
});
|
|
/**
|
* 接收 columns 并设置为响应式
|
* @type {UnwrapNestedRefs<any[]>}
|
*/
|
const useColumns = ref([]);
|
|
const onSelectedDel = (row) => {
|
|
selectedList.value.forEach((e, i) => {
|
if (row[rowKey] === e[rowKey]) {
|
selectedList.value.splice(i, 1);
|
toggleSelection();
|
}
|
});
|
}
|
|
const formatData = (format, data) => {
|
return format ? format(data) : (data || '')
|
}
|
|
watch(selectedList, (newValue) => {
|
props.subSelection(newValue);
|
});
|
|
const groupColumns = computed(() => {
|
const group = [...new Set(useColumns.value.map(column => column.group))];
|
return group.map(e => {
|
return {
|
id: tools.uuid(),
|
group: e,
|
columns: useColumns.value.filter(column => column.group === e)
|
}
|
});
|
});
|
|
|
const onInit = () => {
|
|
if (JSON.stringify(useColumns.value) !== JSON.stringify($props.columns)) {
|
const columns = $props.columns.map((e, index) => {
|
e.group = e.group || `$${index}`;
|
e.isShow = true;
|
e.id = tools.uuid();
|
return e;
|
});
|
|
useColumns.value = columns;
|
|
colSettingProps.columns = [...columns];
|
|
colSettingRef.value.$forceUpdate(colSettingProps);
|
}
|
|
}
|
|
watch(tableData, (val) => {
|
clearExpand();
|
if (Object.keys(dataForm.current).length > 0) {
|
if (!val.find(e => dataForm.current[rowKey] === e[rowKey])) {
|
onTableCurrent({});
|
}
|
}
|
})
|
|
const dataForm = reactive({
|
current: {}
|
});
|
|
const setTableCurrent = (row = {}) => {
|
dataForm.current = row;
|
tableRef.value?.setCurrentRow(row);
|
}
|
|
const onTableCurrent = (row = {}) => {
|
setTableCurrent(row);
|
props.subCurrent(row);
|
}
|
|
|
const getTableCurrent = () => {
|
return dataForm.current;
|
}
|
|
|
const settingForm = reactive({
|
showSetting: 'table',
|
isLock: true,
|
lockHeight: computed(() => window.innerHeight - 352 - (settingsStore.tagsView ? 39 : 0) + complementHeight)
|
});
|
|
watch(() => settingForm.showSetting, async (val) => {
|
if (val === 'table') {
|
await nextTick();
|
tableRef.value.setCurrentRow(dataForm.current)
|
}
|
})
|
|
const colSettingProps = {
|
ref: colSettingRef,
|
columns: [],
|
subColumns(list) {
|
useColumns.value = list;
|
}
|
};
|
|
const showSettingProps = {
|
subActive(active) {
|
settingForm.showSetting = active;
|
}
|
};
|
|
const getSelectedList = () => selectedList.value;
|
|
const getSelectedIds = () => selectedIds.value;
|
|
const setSelected = (selected) => {
|
selectedList.value = selected;
|
}
|
|
|
onMounted(() => {
|
onInit();
|
})
|
/**
|
* 暴露给父组件的参数和方法 (外部需要什么,都可以从这里暴露出去)
|
*/
|
defineExpose({
|
/**
|
* 初始化
|
*/
|
getTableData,
|
setSelected,
|
getTableCurrent,
|
setTableCurrent,
|
getSelectedList,
|
getSelectedIds,
|
$forceUpdate
|
});
|
|
</script>
|
|
<style lang='scss'>
|
.app-table-pro {
|
padding: 10px;
|
background: #fff;
|
border-radius: 5px;
|
border: 1px solid var(--el-border-color-light);
|
overflow: hidden;
|
|
> .header {
|
align-items: center;
|
padding-bottom: 10px;
|
display: flex;
|
justify-content: space-between;
|
|
.title {
|
font-size: 14px;
|
color: var(--el-text-color-primary);
|
font-weight: 600;
|
display: flex;
|
align-items: center;
|
|
.icon {
|
margin-left: 3px;
|
|
.el-icon {
|
font-size: 17px;
|
}
|
}
|
}
|
|
.tools {
|
|
button {
|
margin-left: 10px;
|
}
|
|
}
|
|
.button {
|
flex: 1;
|
overflow: auto;
|
text-align: right;
|
margin-left: 15px;
|
margin-right: 15px;
|
|
> div {
|
display: inline-flex;
|
}
|
}
|
}
|
|
> .card {
|
overflow: auto;
|
|
.active {
|
border: 1px solid var(--el-color-primary-dark-1);
|
}
|
|
.item {
|
margin-bottom: 10px;
|
cursor: pointer;
|
font-size: var(--el-font-size-small);
|
|
> .el-card__body {
|
padding-bottom: 0;
|
}
|
|
|
.el-col {
|
margin-bottom: 20px;
|
|
.text {
|
display: flex;
|
align-items: center;
|
margin-right: 10px;
|
|
.label {
|
font-size: var(--el-font-size-base);
|
color: var(--el-text-color-primary) !important;
|
font-weight: 500;
|
padding-right: 10px;
|
white-space: nowrap;
|
min-width: 80px;
|
}
|
|
.value {
|
position: relative;
|
top: 2px;
|
padding-bottom: 3px;
|
border-bottom: 1px solid var(--el-border-color);
|
}
|
}
|
}
|
}
|
}
|
|
> .pagination {
|
display: flex;
|
justify-content: flex-end;
|
margin-top: 10px;
|
}
|
}
|
</style>
|