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.
479 lines
15 KiB
479 lines
15 KiB
<script lang="tsx">
|
|
import { ElTable, ElTableColumn, ElPagination,ElAffix } from 'element-plus'
|
|
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue'
|
|
import { propTypes } from '@/utils/propTypes'
|
|
import { setIndex } from './helper'
|
|
import { getSlot } from '@/utils/tsxHelper'
|
|
import type { TableProps } from './types'
|
|
import { set } from 'lodash-es'
|
|
const { t } = useI18n()
|
|
import { Pagination, TableColumn, TableSetPropsType, TableSlotDefault } from '@/types/table'
|
|
import { useCache } from '@/hooks/web/useCache'
|
|
const { wsCache } = useCache()
|
|
|
|
|
|
|
|
export default defineComponent({
|
|
// eslint-disable-next-line vue/no-reserved-component-names
|
|
name: 'Table',
|
|
props: {
|
|
pageSize: propTypes.number.def(20),
|
|
currentPage: propTypes.number.def(1),
|
|
// 是否多选
|
|
selection: propTypes.bool.def(false),
|
|
// 是否显示多选数量
|
|
selectionTotal: propTypes.bool.def(false),
|
|
// 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip,
|
|
showOverflowTooltip: propTypes.bool.def(true),
|
|
// 表头
|
|
columns: {
|
|
type: Array as PropType<TableColumn[]>,
|
|
default: () => []
|
|
},
|
|
// 展开行
|
|
expand: propTypes.bool.def(false),
|
|
expandFixed: {
|
|
type: Boolean || String,
|
|
default: 'left'
|
|
},
|
|
// 是否展示分页
|
|
pagination: {
|
|
type: Object as PropType<Pagination>,
|
|
default: (): Pagination | undefined => undefined
|
|
},
|
|
// 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key)
|
|
reserveSelection: propTypes.bool.def(false),
|
|
// 加载状态
|
|
loading: propTypes.bool.def(false),
|
|
// 是否叠加索引
|
|
reserveIndex: propTypes.bool.def(false),
|
|
// 对齐方式
|
|
align: propTypes.string
|
|
.validate((v: string) => ['left', 'center', 'right'].includes(v))
|
|
.def('center'),
|
|
border : propTypes.bool.def(true),
|
|
// 表头对齐方式
|
|
headerAlign: propTypes.string
|
|
.validate((v: string) => ['left', 'center', 'right'].includes(v))
|
|
.def('center'),
|
|
data: {
|
|
type: Array as PropType<Recordable[]>,
|
|
default: () => []
|
|
},
|
|
searchTableSelectionsList:[],//回显列表
|
|
selectionColor: propTypes.bool.def(false),
|
|
rowKey: propTypes.string.def(''),
|
|
// true 取消父子关联; false 关联
|
|
isCheckStrictly: propTypes.bool.def(false),
|
|
isShowPackNumber:propTypes.bool.def(false),// 默认不显示包装号,采购收货记录显示包装号
|
|
},
|
|
emits: ['update:pageSize', 'update:currentPage', 'register', 'update:sort','getSelectionRows','rowClick'],
|
|
setup(props, { attrs, slots, emit, expose }) {
|
|
const elTableRef = ref<ComponentRef<typeof ElTable>>()
|
|
|
|
// 注册
|
|
onMounted(() => {
|
|
const tableRef = unref(elTableRef)
|
|
emit('register', tableRef?.$parent, elTableRef)
|
|
if (props.searchTableSelectionsList && props.searchTableSelectionsList.length > 0) {
|
|
props.searchTableSelectionsList.forEach(row => {
|
|
elTableRef.value.toggleRowSelection(row, true);
|
|
});
|
|
}
|
|
})
|
|
|
|
const pageSizeRef = ref(props.pageSize)
|
|
|
|
const currentPageRef = ref(props.currentPage)
|
|
|
|
// useTable传入的props
|
|
const outsideProps = ref<TableProps>({})
|
|
|
|
const mergeProps = ref<TableProps>({})
|
|
|
|
const getProps = computed(() => {
|
|
const propsObj = { ...props }
|
|
Object.assign(propsObj, unref(mergeProps))
|
|
return propsObj
|
|
})
|
|
|
|
const setProps = (props: TableProps = {}) => {
|
|
mergeProps.value = Object.assign(unref(mergeProps), props)
|
|
outsideProps.value = props
|
|
}
|
|
|
|
const setColumn = (columnProps: TableSetPropsType[], columnsChildren?: TableColumn[]) => {
|
|
let { columns } = unref(getProps)
|
|
if (!wsCache.get('ShowPackingNumber')&&!props.isShowPackNumber) {
|
|
if (columns) {
|
|
columns = columns.filter(item => item.field != 'packingNumber' && item.field != 'fromPackingNumber'&& item.field != 'toPackingNumber')
|
|
}
|
|
}
|
|
for (const v of columnsChildren || columns) {
|
|
for (const item of columnProps) {
|
|
if (v.field === item.field) {
|
|
set(v, item.path, item.value)
|
|
} else if (v.children?.length) {
|
|
setColumn(columnProps, v.children)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const toggleRowSelection = (row: Recordable, selected: boolean) => {
|
|
const tableRef = unref(elTableRef)
|
|
tableRef?.toggleRowSelection(unref(getProps).data.find(item=>item.id === row.id), selected)
|
|
}
|
|
|
|
const selections = ref<Recordable[]>([])
|
|
const sortRef = ref()
|
|
// 选中某行
|
|
const selectRow = (selection,row)=>{
|
|
nextTick(()=>{
|
|
const selected = elTableRef.value?.getSelectionRows().some((item) => item.id === row.id)
|
|
|
|
if(props.isCheckStrictly&&selected&&row.children){//取消关联
|
|
row.children.forEach(item => {
|
|
elTableRef.value?.toggleRowSelection(item, false);
|
|
});
|
|
}
|
|
})
|
|
emit('getSelectionRows', currentPageRef.value,elTableRef.value?.getSelectionRows())
|
|
}
|
|
//全选回调
|
|
const selectAll = ()=>{
|
|
emit('getSelectionRows', currentPageRef.value,elTableRef.value?.getSelectionRows())
|
|
}
|
|
const rowClick = (row: any, column: any, event: Event)=>{
|
|
// const selected = elTableRef.value?.getSelectionRows().some(item => item.id === row.id)
|
|
// if (!selected) {
|
|
// elTableRef.value?.toggleRowSelection(row, true);
|
|
// } else {
|
|
// // 取消
|
|
// elTableRef.value?.toggleRowSelection(row, false);
|
|
// }
|
|
// console.log(elTableRef.value?.getSelectionRows())
|
|
emit('rowClick',row,column,event)
|
|
}
|
|
//反选
|
|
const togglePageSelection = ()=>{
|
|
unref(getProps).data.forEach(row=>{
|
|
elTableRef.value!.toggleRowSelection(row)
|
|
})
|
|
}
|
|
// 切换全选/不全选
|
|
const toggleAllSelection = (isAll)=>{
|
|
if(isAll){
|
|
//全选
|
|
unref(getProps).data.forEach(row=>{
|
|
elTableRef.value!.toggleRowSelection(row,true)
|
|
})
|
|
emit('getSelectionRows', currentPageRef.value,elTableRef.value?.getSelectionRows())
|
|
}
|
|
}
|
|
|
|
//清空选择
|
|
const clearSelection = ()=>{
|
|
elTableRef.value!.clearSelection()
|
|
}
|
|
|
|
const selectionChange = (selection: Recordable[]) => {
|
|
selections.value = selection
|
|
}
|
|
|
|
// 排序
|
|
const sortChange = (sortVal: Recordable) => {
|
|
sortRef.value = sortVal
|
|
}
|
|
|
|
expose({
|
|
setProps,
|
|
setColumn,
|
|
selections,
|
|
toggleRowSelection,
|
|
selectAll,
|
|
toggleAllSelection,
|
|
togglePageSelection,
|
|
clearSelection,
|
|
elTableRef
|
|
})
|
|
|
|
const pagination = computed(() => {
|
|
// update by 芋艿:保持和 Pagination 组件的逻辑一致
|
|
return Object.assign(
|
|
{
|
|
small: false,
|
|
background: true,
|
|
pagerCount: document.body.clientWidth < 992 ? 5 : 7,
|
|
layout: 'total, sizes, prev, pager, next, jumper',
|
|
pageSizes: [10, 20, 30, 50, 100],
|
|
disabled: false,
|
|
hideOnSinglePage: false,
|
|
total: 10
|
|
},
|
|
unref(getProps).pagination
|
|
)
|
|
})
|
|
|
|
watch(
|
|
() => sortRef.value,
|
|
(val: any) => {
|
|
emit('update:sort', val)
|
|
}
|
|
)
|
|
|
|
watch(
|
|
() => unref(getProps).pageSize,
|
|
(val: number) => {
|
|
pageSizeRef.value = val
|
|
}
|
|
)
|
|
|
|
watch(
|
|
() => unref(getProps).currentPage,
|
|
(val: number) => {
|
|
currentPageRef.value = val
|
|
}
|
|
)
|
|
|
|
watch(
|
|
() => pageSizeRef.value,
|
|
(val: number) => {
|
|
emit('update:pageSize', val)
|
|
}
|
|
)
|
|
|
|
watch(
|
|
() => currentPageRef.value,
|
|
(val: number) => {
|
|
emit('update:currentPage', val)
|
|
}
|
|
)
|
|
|
|
const getBindValue = computed(() => {
|
|
const bindValue: Recordable = { ...attrs, ...props }
|
|
delete bindValue.columns
|
|
delete bindValue.data
|
|
return bindValue
|
|
})
|
|
|
|
const renderTableSelection = () => {
|
|
const { selection, reserveSelection, align, headerAlign,border } = unref(getProps)
|
|
// 渲染多选
|
|
return selection ? (
|
|
<ElTableColumn fixed="left"
|
|
type="selection"
|
|
reserveSelection={reserveSelection}
|
|
align={align}
|
|
headerAlign={headerAlign}
|
|
border={border}
|
|
width="50"
|
|
></ElTableColumn>
|
|
) : undefined
|
|
}
|
|
|
|
const renderTableExpand = () => {
|
|
const { align, headerAlign, expand,border,expandFixed } = unref(getProps)
|
|
// 渲染展开行
|
|
return expand ? (
|
|
<ElTableColumn fixed={expandFixed} d type="expand" align={align} headerAlign={headerAlign} border={border}>
|
|
{{
|
|
// @ts-ignore
|
|
default: (data: TableSlotDefault) => getSlot(slots, 'expand', data)
|
|
}}
|
|
</ElTableColumn>
|
|
) : undefined
|
|
}
|
|
|
|
const rnderTreeTableColumn = (columnsChildren: TableColumn[]) => {
|
|
const { align, headerAlign, showOverflowTooltip,border } = unref(getProps)
|
|
return columnsChildren.map((v) => {
|
|
const props = { ...v }
|
|
if (props.children) delete props.children
|
|
return (
|
|
<ElTableColumn
|
|
showOverflowTooltip={showOverflowTooltip}
|
|
align={align}
|
|
border={border}
|
|
headerAlign={headerAlign}
|
|
{...props}
|
|
prop={v.field}
|
|
>
|
|
{{
|
|
default: (data: TableSlotDefault) =>
|
|
v.children && v.children.length
|
|
? rnderTableColumn(v.children)
|
|
: // @ts-ignore
|
|
getSlot(slots, v.field, data) ||
|
|
v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
|
|
data.row[v.field],
|
|
// @ts-ignore
|
|
header: getSlot(slots, `${v.field}-header`)
|
|
}}
|
|
</ElTableColumn>
|
|
)
|
|
})
|
|
}
|
|
|
|
// 默认排序
|
|
const updateSort= (val)=>{
|
|
val.forEach(item=>{
|
|
if(!item.sortTableDefault){
|
|
if(item.fixed=='left'){
|
|
item.sortTableDefault = 0
|
|
}else if(item.fixed=='right'){
|
|
item.sortTableDefault = 9999
|
|
}else{
|
|
item.sortTableDefault = 999 // 默认999
|
|
}
|
|
}
|
|
})
|
|
val.sort((column1,column2)=>{
|
|
return column1.sortTableDefault - column2.sortTableDefault
|
|
})
|
|
}
|
|
|
|
|
|
const rnderTableColumn = (columnsChildren?: TableColumn[]) => {
|
|
let {
|
|
columns,
|
|
reserveIndex,
|
|
pageSize,
|
|
currentPage,
|
|
align,
|
|
headerAlign,
|
|
showOverflowTooltip,
|
|
border
|
|
} = unref(getProps)
|
|
|
|
if (!wsCache.get('ShowPackingNumber') && !props.isShowPackNumber) {
|
|
if (columns) {
|
|
columns = columns.filter(item => item.field != 'packingNumber' && item.field != 'fromPackingNumber'&& item.field != 'toPackingNumber')
|
|
}
|
|
}
|
|
//默认排序
|
|
// updateSort(columns)
|
|
return [...[renderTableExpand()], ...[renderTableSelection()]].concat(
|
|
(columnsChildren || columns).map((v) => {
|
|
const zhName = v.label || '';//中文名称
|
|
let labelName = t(`ts.${v.label}`).replace('ts.','')
|
|
// 自定生成序号
|
|
if (v.type === 'index') {
|
|
return (
|
|
<ElTableColumn
|
|
type="index"
|
|
index={
|
|
v.index
|
|
? v.index
|
|
: (index) => setIndex(reserveIndex, index, pageSize, currentPage)
|
|
}
|
|
align={v.align || align}
|
|
border={v.border || border}
|
|
headerAlign={v.headerAlign || headerAlign}
|
|
label={labelName}
|
|
width="65px"
|
|
></ElTableColumn>
|
|
)
|
|
} else {
|
|
const props = { ...v }
|
|
if (props.children) delete props.children
|
|
return (
|
|
<ElTableColumn
|
|
showOverflowTooltip={showOverflowTooltip}
|
|
align={align}
|
|
border={border}
|
|
headerAlign={headerAlign}
|
|
{...props}
|
|
prop={v.field}
|
|
>
|
|
{{
|
|
default: (data: TableSlotDefault) =>
|
|
v.children && v.children.length
|
|
? rnderTreeTableColumn(v.children)
|
|
: // @ts-ignore
|
|
getSlot(slots, v.field, data) ||
|
|
v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
|
|
data.row[v.field],
|
|
// @ts-ignore
|
|
header: () => getSlot(slots, `${v.field}-header`) || labelName
|
|
}}
|
|
</ElTableColumn>
|
|
)
|
|
}
|
|
})
|
|
)
|
|
}
|
|
|
|
const selectionBg = ({row}) => {
|
|
if (unref(getProps).selectionColor&&elTableRef.value?.getSelectionRows().includes(row)) {
|
|
return 'selectionRow'
|
|
}
|
|
return 'tableRow'
|
|
}
|
|
|
|
return () => (
|
|
<div v-loading={unref(getProps).loading}>
|
|
<ElTable default-expand-all={true}
|
|
// @ts-ignore
|
|
ref={elTableRef}
|
|
data={unref(getProps).data}
|
|
header-cell-class-name="tableHeader"
|
|
row-class-name={selectionBg}
|
|
cell-class-name="tableRow"
|
|
onSelect={selectRow}
|
|
onSelect-all={selectAll}
|
|
onSelection-change={selectionChange}
|
|
onSort-change={sortChange}
|
|
row-key={unref(getProps).rowKey}
|
|
onRow-click={rowClick}
|
|
{...unref(getBindValue)}
|
|
>
|
|
{{
|
|
default: () => rnderTableColumn(),
|
|
// @ts-ignore
|
|
append: () => getSlot(slots, 'append')
|
|
}}
|
|
</ElTable>
|
|
{unref(getProps).selectionTotal ? (
|
|
<div class="mt-15px float-left" style='height:32px;line-height:32px'>已选{selections.value.length}条数据</div>
|
|
):undefined}
|
|
{unref(getProps).pagination ? (
|
|
// update by 芋艿:保持和 Pagination 组件一致
|
|
<ElPagination
|
|
v-model:pageSize={pageSizeRef.value}
|
|
v-model:currentPage={currentPageRef.value}
|
|
class="float-right mt-15px"
|
|
{...unref(pagination)}
|
|
></ElPagination>
|
|
) : undefined}
|
|
|
|
</div>
|
|
)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.expandTable .el-table .tableHeader {
|
|
--el-table-header-bg-color: #f5f5f5;
|
|
}
|
|
.expandTable .el-table .tableRow {
|
|
--el-table-tr-bg-color: #f5f5f5;
|
|
--el-bg-color: #f5f5f5;
|
|
}
|
|
|
|
.el-table .selectionRow {
|
|
--el-table-tr-bg-color: var(--el-color-primary-light-9);
|
|
}
|
|
</style>
|
|
<style lang="scss" scoped>
|
|
:deep(.el-button.is-text) {
|
|
padding: 8px 4px;
|
|
margin-left: 0;
|
|
}
|
|
|
|
:deep(.el-button.is-link) {
|
|
padding: 8px 4px;
|
|
margin-left: 0;
|
|
}
|
|
</style>
|
|
|