style: lint格式化文件

This commit is contained in:
2025-10-21 18:04:02 +08:00
parent f9ca969fec
commit 7629120548
1092 changed files with 148218 additions and 157907 deletions

View File

@ -1,446 +1,383 @@
<template>
<div ref="wrapRef" :class="getWrapperClass">
<BasicForm
ref="formRef"
submitOnReset
v-bind="getFormProps"
v-if="getBindValues.useSearchForm"
:tableAction="tableAction"
@register="registerForm"
@submit="handleSearchInfoChange"
@advanced-change="redoHeight"
>
<template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</BasicForm>
<div ref="wrapRef" :class="getWrapperClass">
<BasicForm ref="formRef" submitOnReset v-bind="getFormProps" v-if="getBindValues.useSearchForm" :tableAction="tableAction" @register="registerForm" @submit="handleSearchInfoChange" @advanced-change="redoHeight">
<template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</BasicForm>
<a-table
ref="tableElRef"
v-bind="getBindValues"
:rowClassName="getRowClassName"
v-show="getEmptyDataIsShowTable"
@change="handleTableChange"
>
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
<template #headerCell="{ column }">
<HeaderCell :column="column" />
</template>
<!-- <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">-->
<!-- <HeaderCell :column="column" />-->
<!-- </template>-->
</a-table>
</div>
<a-table ref="tableElRef" v-bind="getBindValues" :rowClassName="getRowClassName" v-show="getEmptyDataIsShowTable" @change="handleTableChange">
<template #[item]="data" v-for="item in Object.keys($slots)" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
<template #headerCell="{ column }">
<HeaderCell :column="column" />
</template>
<!-- <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index">-->
<!-- <HeaderCell :column="column" />-->
<!-- </template>-->
</a-table>
</div>
</template>
<script lang="ts">
import type {
BasicTableProps,
TableActionType,
SizeType,
ColumnChangeParam,
} from './types/table';
import type { BasicTableProps, TableActionType, SizeType, ColumnChangeParam } from './types/table';
import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue';
import { BasicForm, useForm } from '/@/components/TableForm/index';
import { PageWrapperFixedHeightKey } from '/@/components/Page';
import HeaderCell from './components/HeaderCell.vue';
import { InnerHandlers } from './types/table';
import { defineComponent, ref, computed, unref, toRaw, inject, watchEffect } from 'vue';
import { BasicForm, useForm } from '/@/components/TableForm/index';
import { PageWrapperFixedHeightKey } from '/@/components/Page';
import HeaderCell from './components/HeaderCell.vue';
import { InnerHandlers } from './types/table';
import { usePagination } from './hooks/usePagination';
import { useColumns } from './hooks/useColumns';
import { useDataSource } from './hooks/useDataSource';
import { useLoading } from './hooks/useLoading';
import { useRowSelection } from './hooks/useRowSelection';
import { useTableScroll } from './hooks/useTableScroll';
import { useTableScrollTo } from './hooks/useScrollTo';
import { useCustomRow } from './hooks/useCustomRow';
import { useTableStyle } from './hooks/useTableStyle';
import { useTableHeader } from './hooks/useTableHeader';
import { useTableExpand } from './hooks/useTableExpand';
import { createTableContext } from './hooks/useTableContext';
import { useTableFooter } from './hooks/useTableFooter';
import { useTableForm } from './hooks/useTableForm';
import { useDesign } from '/@/hooks/web/useDesign';
import { usePagination } from './hooks/usePagination';
import { useColumns } from './hooks/useColumns';
import { useDataSource } from './hooks/useDataSource';
import { useLoading } from './hooks/useLoading';
import { useRowSelection } from './hooks/useRowSelection';
import { useTableScroll } from './hooks/useTableScroll';
import { useTableScrollTo } from './hooks/useScrollTo';
import { useCustomRow } from './hooks/useCustomRow';
import { useTableStyle } from './hooks/useTableStyle';
import { useTableHeader } from './hooks/useTableHeader';
import { useTableExpand } from './hooks/useTableExpand';
import { createTableContext } from './hooks/useTableContext';
import { useTableFooter } from './hooks/useTableFooter';
import { useTableForm } from './hooks/useTableForm';
import { useDesign } from '/@/hooks/web/useDesign';
import { omit } from 'lodash-es';
import { basicProps } from './props';
import { isFunction } from '/@/utils/is';
import { warn } from '/@/utils/log';
import { omit } from 'lodash-es';
import { basicProps } from './props';
import { isFunction } from '/@/utils/is';
import { warn } from '/@/utils/log';
export default defineComponent({
components: {
BasicForm,
HeaderCell,
},
props: basicProps,
emits: [
'fetch-success',
'fetch-error',
'selection-change',
'register',
'row-click',
'row-dbClick',
'row-contextmenu',
'row-mouseenter',
'row-mouseleave',
'edit-end',
'edit-cancel',
'edit-row-end',
'edit-change',
'expanded-rows-change',
'change',
'columns-change',
],
setup(props, { attrs, emit, slots, expose }) {
const tableElRef = ref(null);
const tableData = ref<Recordable[]>([]);
const wrapRef = ref(null);
const formRef = ref(null);
const innerPropsRef = ref<Partial<BasicTableProps>>();
const { prefixCls } = useDesign('basic-table');
const [registerForm, formActions] = useForm();
const getProps = computed(() => {
return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
});
const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);
watchEffect(() => {
unref(isFixedHeightPage) &&
props.canResize &&
warn(
"'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)",
);
});
const { getLoading, setLoading } = useLoading(getProps);
const {
getPaginationInfo,
getPagination,
setPagination,
setShowPagination,
getShowPagination,
} = usePagination(getProps);
const {
getRowSelection,
getRowSelectionRef,
getSelectRows,
clearSelectedRowKeys,
getSelectRowKeys,
deleteSelectRowByKey,
setSelectedRowKeys,
} = useRowSelection(getProps, tableData, emit);
const {
handleTableChange: onTableChange,
getDataSourceRef,
getDataSource,
getRawDataSource,
setTableData,
updateTableDataRecord,
deleteTableDataRecord,
insertTableDataRecord,
findTableDataRecord,
fetch,
getRowKey,
reload,
getAutoCreateKey,
updateTableData,
} = useDataSource(
getProps,
{
tableData,
getPaginationInfo,
setLoading,
setPagination,
getFieldsValue: formActions.getFieldsValue,
clearSelectedRowKeys,
export default defineComponent({
components: {
BasicForm,
HeaderCell
},
emit,
);
props: basicProps,
emits: [
'fetch-success',
'fetch-error',
'selection-change',
'register',
'row-click',
'row-dbClick',
'row-contextmenu',
'row-mouseenter',
'row-mouseleave',
'edit-end',
'edit-cancel',
'edit-row-end',
'edit-change',
'expanded-rows-change',
'change',
'columns-change'
],
setup(props, { attrs, emit, slots, expose }) {
const tableElRef = ref(null);
const tableData = ref<Recordable[]>([]);
function handleTableChange(...args) {
onTableChange.call(undefined, ...args);
emit('change', ...args);
// 解决通过useTable注册onChange时不起作用的问题
const { onChange } = unref(getProps);
onChange && isFunction(onChange) && onChange.call(undefined, ...args);
}
const wrapRef = ref(null);
const formRef = ref(null);
const innerPropsRef = ref<Partial<BasicTableProps>>();
const {
getViewColumns,
getColumns,
setCacheColumnsByField,
setColumns,
getColumnsRef,
getCacheColumns,
} = useColumns(getProps, getPaginationInfo);
const { prefixCls } = useDesign('basic-table');
const [registerForm, formActions] = useForm();
const { getScrollRef, redoHeight } = useTableScroll(
getProps,
tableElRef,
getColumnsRef,
getRowSelectionRef,
getDataSourceRef,
wrapRef,
formRef,
);
const getProps = computed(() => {
return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
});
const { scrollTo } = useTableScrollTo(tableElRef, getDataSourceRef);
const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);
watchEffect(() => {
unref(isFixedHeightPage) && props.canResize && warn("'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)");
});
const { customRow } = useCustomRow(getProps, {
setSelectedRowKeys,
getSelectRowKeys,
clearSelectedRowKeys,
getAutoCreateKey,
emit,
});
const { getLoading, setLoading } = useLoading(getProps);
const { getPaginationInfo, getPagination, setPagination, setShowPagination, getShowPagination } = usePagination(getProps);
const { getRowClassName } = useTableStyle(getProps, prefixCls);
const { getRowSelection, getRowSelectionRef, getSelectRows, clearSelectedRowKeys, getSelectRowKeys, deleteSelectRowByKey, setSelectedRowKeys } = useRowSelection(getProps, tableData, emit);
const { getExpandOption, expandAll, expandRows, collapseAll } = useTableExpand(
getProps,
tableData,
emit,
);
const {
handleTableChange: onTableChange,
getDataSourceRef,
getDataSource,
getRawDataSource,
setTableData,
updateTableDataRecord,
deleteTableDataRecord,
insertTableDataRecord,
findTableDataRecord,
fetch,
getRowKey,
reload,
getAutoCreateKey,
updateTableData
} = useDataSource(
getProps,
{
tableData,
getPaginationInfo,
setLoading,
setPagination,
getFieldsValue: formActions.getFieldsValue,
clearSelectedRowKeys
},
emit
);
const handlers: InnerHandlers = {
onColumnsChange: (data: ColumnChangeParam[]) => {
emit('columns-change', data);
// support useTable
unref(getProps).onColumnsChange?.(data);
},
};
function handleTableChange(...args) {
onTableChange.call(undefined, ...args);
emit('change', ...args);
// 解决通过useTable注册onChange时不起作用的问题
const { onChange } = unref(getProps);
onChange && isFunction(onChange) && onChange.call(undefined, ...args);
}
const { getHeaderProps } = useTableHeader(getProps, slots, handlers);
const { getViewColumns, getColumns, setCacheColumnsByField, setColumns, getColumnsRef, getCacheColumns } = useColumns(getProps, getPaginationInfo);
const { getFooterProps } = useTableFooter(
getProps,
getScrollRef,
tableElRef,
getDataSourceRef,
);
const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef, getColumnsRef, getRowSelectionRef, getDataSourceRef, wrapRef, formRef);
const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } =
useTableForm(getProps, slots, fetch, getLoading);
const { scrollTo } = useTableScrollTo(tableElRef, getDataSourceRef);
const getBindValues = computed(() => {
const dataSource = unref(getDataSourceRef);
let propsData: Recordable = {
...attrs,
customRow,
...unref(getProps),
...unref(getHeaderProps),
scroll: unref(getScrollRef),
loading: unref(getLoading),
tableLayout: 'fixed',
rowSelection: unref(getRowSelectionRef),
rowKey: unref(getRowKey),
columns: toRaw(unref(getViewColumns)),
pagination: toRaw(unref(getPaginationInfo)),
dataSource,
footer: unref(getFooterProps),
...unref(getExpandOption),
};
if (slots.expandedRowRender) {
propsData = omit(propsData, 'scroll');
const { customRow } = useCustomRow(getProps, {
setSelectedRowKeys,
getSelectRowKeys,
clearSelectedRowKeys,
getAutoCreateKey,
emit
});
const { getRowClassName } = useTableStyle(getProps, prefixCls);
const { getExpandOption, expandAll, expandRows, collapseAll } = useTableExpand(getProps, tableData, emit);
const handlers: InnerHandlers = {
onColumnsChange: (data: ColumnChangeParam[]) => {
emit('columns-change', data);
// support useTable
unref(getProps).onColumnsChange?.(data);
}
};
const { getHeaderProps } = useTableHeader(getProps, slots, handlers);
const { getFooterProps } = useTableFooter(getProps, getScrollRef, tableElRef, getDataSourceRef);
const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = useTableForm(getProps, slots, fetch, getLoading);
const getBindValues = computed(() => {
const dataSource = unref(getDataSourceRef);
let propsData: Recordable = {
...attrs,
customRow,
...unref(getProps),
...unref(getHeaderProps),
scroll: unref(getScrollRef),
loading: unref(getLoading),
tableLayout: 'fixed',
rowSelection: unref(getRowSelectionRef),
rowKey: unref(getRowKey),
columns: toRaw(unref(getViewColumns)),
pagination: toRaw(unref(getPaginationInfo)),
dataSource,
footer: unref(getFooterProps),
...unref(getExpandOption)
};
if (slots.expandedRowRender) {
propsData = omit(propsData, 'scroll');
}
propsData = omit(propsData, ['class', 'onChange']);
return propsData;
});
const getWrapperClass = computed(() => {
const values = unref(getBindValues);
return [
prefixCls,
attrs.class,
{
[`${prefixCls}-form-container`]: values.useSearchForm,
[`${prefixCls}--inset`]: values.inset
}
];
});
const getEmptyDataIsShowTable = computed(() => {
const { emptyDataIsShowTable, useSearchForm } = unref(getProps);
if (emptyDataIsShowTable || !useSearchForm) {
return true;
}
return !!unref(getDataSourceRef).length;
});
function setProps(props: Partial<BasicTableProps>) {
innerPropsRef.value = { ...unref(innerPropsRef), ...props };
}
const tableAction: TableActionType = {
reload,
getSelectRows,
clearSelectedRowKeys,
getSelectRowKeys,
deleteSelectRowByKey,
setPagination,
setTableData,
updateTableDataRecord,
deleteTableDataRecord,
insertTableDataRecord,
findTableDataRecord,
redoHeight,
setSelectedRowKeys,
setColumns,
setLoading,
getDataSource,
getRawDataSource,
setProps,
getRowSelection,
getPaginationRef: getPagination,
getColumns,
getCacheColumns,
emit,
updateTableData,
setShowPagination,
getShowPagination,
setCacheColumnsByField,
expandAll,
expandRows,
collapseAll,
scrollTo,
getSize: () => {
return unref(getBindValues).size as SizeType;
}
};
createTableContext({ ...tableAction, wrapRef, getBindValues });
expose(tableAction);
emit('register', tableAction, formActions);
return {
formRef,
tableElRef,
getBindValues,
getLoading,
registerForm,
handleSearchInfoChange,
getEmptyDataIsShowTable,
handleTableChange,
getRowClassName,
wrapRef,
tableAction,
redoHeight,
getFormProps: getFormProps as any,
replaceFormSlotKey,
getFormSlotKeys,
getWrapperClass,
columns: getViewColumns
};
}
propsData = omit(propsData, ['class', 'onChange']);
return propsData;
});
const getWrapperClass = computed(() => {
const values = unref(getBindValues);
return [
prefixCls,
attrs.class,
{
[`${prefixCls}-form-container`]: values.useSearchForm,
[`${prefixCls}--inset`]: values.inset,
},
];
});
const getEmptyDataIsShowTable = computed(() => {
const { emptyDataIsShowTable, useSearchForm } = unref(getProps);
if (emptyDataIsShowTable || !useSearchForm) {
return true;
}
return !!unref(getDataSourceRef).length;
});
function setProps(props: Partial<BasicTableProps>) {
innerPropsRef.value = { ...unref(innerPropsRef), ...props };
}
const tableAction: TableActionType = {
reload,
getSelectRows,
clearSelectedRowKeys,
getSelectRowKeys,
deleteSelectRowByKey,
setPagination,
setTableData,
updateTableDataRecord,
deleteTableDataRecord,
insertTableDataRecord,
findTableDataRecord,
redoHeight,
setSelectedRowKeys,
setColumns,
setLoading,
getDataSource,
getRawDataSource,
setProps,
getRowSelection,
getPaginationRef: getPagination,
getColumns,
getCacheColumns,
emit,
updateTableData,
setShowPagination,
getShowPagination,
setCacheColumnsByField,
expandAll,
expandRows,
collapseAll,
scrollTo,
getSize: () => {
return unref(getBindValues).size as SizeType;
},
};
createTableContext({ ...tableAction, wrapRef, getBindValues });
expose(tableAction);
emit('register', tableAction, formActions);
return {
formRef,
tableElRef,
getBindValues,
getLoading,
registerForm,
handleSearchInfoChange,
getEmptyDataIsShowTable,
handleTableChange,
getRowClassName,
wrapRef,
tableAction,
redoHeight,
getFormProps: getFormProps as any,
replaceFormSlotKey,
getFormSlotKeys,
getWrapperClass,
columns: getViewColumns,
};
},
});
});
</script>
<style lang="less" scoped>
@border-color: #cecece4d;
@border-color: #cecece4d;
@prefix-cls: ~'@{namespace}-basic-table';
@prefix-cls: ~'@{namespace}-basic-table';
[data-theme='dark'] {
.ant-table-tbody > tr:hover.ant-table-row-selected > td,
.ant-table-tbody > tr.ant-table-row-selected td {
background-color: #262626;
}
}
.@{prefix-cls} {
max-width: 100%;
height: 100%;
&-row__striped {
td {
background-color: @app-content-background;
}
[data-theme='dark'] {
.ant-table-tbody > tr:hover.ant-table-row-selected > td,
.ant-table-tbody > tr.ant-table-row-selected td {
background-color: #262626;
}
}
&-form-container {
padding: 16px;
.@{prefix-cls} {
max-width: 100%;
height: 100%;
.ant-form {
padding: 12px 10px 6px;
margin-bottom: 16px;
background-color: @component-background;
border-radius: 2px;
}
&-row__striped {
td {
background-color: @app-content-background;
}
}
&-form-container {
padding: 16px;
.ant-form {
padding: 12px 10px 6px;
margin-bottom: 16px;
background-color: @component-background;
border-radius: 2px;
}
}
.ant-tag {
margin-right: 0;
}
.ant-table-wrapper {
padding: 6px;
background-color: @component-background;
border-radius: 2px;
.ant-table-title {
min-height: 40px;
padding: 0 0 8px !important;
}
.ant-table.ant-table-bordered .ant-table-title {
border: none !important;
}
}
.ant-table {
width: 100%;
overflow-x: hidden;
&-title {
display: flex;
padding: 8px 6px;
border-bottom: none;
justify-content: space-between;
align-items: center;
}
//.ant-table-tbody > tr.ant-table-row-selected td {
//background-color: fade(@primary-color, 8%) !important;
//}
}
.ant-pagination {
margin: 10px 0 0;
}
.ant-table-footer {
padding: 0;
.ant-table-wrapper {
padding: 0;
}
table {
border: none !important;
}
.ant-table-body {
overflow-x: hidden !important;
// overflow-y: scroll !important;
}
td {
padding: 12px 8px;
}
}
&--inset {
.ant-table-wrapper {
padding: 0;
}
}
}
.ant-tag {
margin-right: 0;
}
.ant-table-wrapper {
padding: 6px;
background-color: @component-background;
border-radius: 2px;
.ant-table-title {
min-height: 40px;
padding: 0 0 8px !important;
}
.ant-table.ant-table-bordered .ant-table-title {
border: none !important;
}
}
.ant-table {
width: 100%;
overflow-x: hidden;
&-title {
display: flex;
padding: 8px 6px;
border-bottom: none;
justify-content: space-between;
align-items: center;
}
//.ant-table-tbody > tr.ant-table-row-selected td {
//background-color: fade(@primary-color, 8%) !important;
//}
}
.ant-pagination {
margin: 10px 0 0;
}
.ant-table-footer {
padding: 0;
.ant-table-wrapper {
padding: 0;
}
table {
border: none !important;
}
.ant-table-body {
overflow-x: hidden !important;
// overflow-y: scroll !important;
}
td {
padding: 12px 8px;
}
}
&--inset {
.ant-table-wrapper {
padding: 0;
}
}
}
</style>

View File

@ -1,16 +1,16 @@
<template>
<span>
<slot></slot>
{{ title }}
<FormOutlined />
</span>
<span>
<slot></slot>
{{ title }}
<FormOutlined />
</span>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { FormOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'EditTableHeaderIcon',
components: { FormOutlined },
props: { title: { type: String, default: '' } },
});
import { defineComponent } from 'vue';
import { FormOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'EditTableHeaderIcon',
components: { FormOutlined },
props: { title: { type: String, default: '' } }
});
</script>

View File

@ -1,48 +1,48 @@
<template>
<EditTableHeaderCell v-if="getIsEdit">
{{ getTitle }}
</EditTableHeaderCell>
<span v-else>{{ getTitle }}</span>
<BasicHelp v-if="getHelpMessage" :text="getHelpMessage" :class="`${prefixCls}__help`" />
<EditTableHeaderCell v-if="getIsEdit">
{{ getTitle }}
</EditTableHeaderCell>
<span v-else>{{ getTitle }}</span>
<BasicHelp v-if="getHelpMessage" :text="getHelpMessage" :class="`${prefixCls}__help`" />
</template>
<script lang="ts">
import type { PropType } from 'vue';
import type { BasicColumn } from '../types/table';
import { defineComponent, computed } from 'vue';
import BasicHelp from '/@/components/Basic/src/BasicHelp.vue';
import EditTableHeaderCell from './EditTableHeaderIcon.vue';
import { useDesign } from '/@/hooks/web/useDesign';
import type { PropType } from 'vue';
import type { BasicColumn } from '../types/table';
import { defineComponent, computed } from 'vue';
import BasicHelp from '/@/components/Basic/src/BasicHelp.vue';
import EditTableHeaderCell from './EditTableHeaderIcon.vue';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'TableHeaderCell',
components: {
EditTableHeaderCell,
BasicHelp,
},
props: {
column: {
type: Object as PropType<BasicColumn>,
default: () => ({}),
},
},
setup(props) {
const { prefixCls } = useDesign('basic-table-header-cell');
export default defineComponent({
name: 'TableHeaderCell',
components: {
EditTableHeaderCell,
BasicHelp
},
props: {
column: {
type: Object as PropType<BasicColumn>,
default: () => ({})
}
},
setup(props) {
const { prefixCls } = useDesign('basic-table-header-cell');
const getIsEdit = computed(() => !!props.column?.edit);
const getTitle = computed(() => props.column?.customTitle || props.column?.title);
const getHelpMessage = computed(() => props.column?.helpMessage);
const getIsEdit = computed(() => !!props.column?.edit);
const getTitle = computed(() => props.column?.customTitle || props.column?.title);
const getHelpMessage = computed(() => props.column?.helpMessage);
return { prefixCls, getIsEdit, getTitle, getHelpMessage };
},
});
return { prefixCls, getIsEdit, getTitle, getHelpMessage };
}
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-header-cell';
@prefix-cls: ~'@{namespace}-basic-table-header-cell';
.@{prefix-cls} {
&__help {
margin-left: 8px;
color: rgb(0 0 0 / 65%) !important;
.@{prefix-cls} {
&__help {
margin-left: 8px;
color: rgb(0 0 0 / 65%) !important;
}
}
}
</style>

View File

@ -1,202 +1,193 @@
<template>
<div :class="[prefixCls, getAlign]" @click="onCellClick">
<template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
<Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
<PopConfirmButton v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
</Tooltip>
<PopConfirmButton v-else v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
<Divider
type="vertical"
class="action-divider"
v-if="divider && index < getActions.length - 1"
/>
</template>
<Dropdown
:trigger="['hover']"
:dropMenuList="getDropdownList"
popconfirm
v-if="dropDownActions && getDropdownList.length > 0"
>
<slot name="more"></slot>
<a-button type="link" size="small" v-if="!$slots.more">
<MoreOutlined class="icon-more" />
</a-button>
</Dropdown>
</div>
<div :class="[prefixCls, getAlign]" @click="onCellClick">
<template v-for="(action, index) in getActions" :key="`${index}-${action.label}`">
<Tooltip v-if="action.tooltip" v-bind="getTooltip(action.tooltip)">
<PopConfirmButton v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
</Tooltip>
<PopConfirmButton v-else v-bind="action">
<Icon :icon="action.icon" :class="{ 'mr-1': !!action.label }" v-if="action.icon" />
<template v-if="action.label">{{ action.label }}</template>
</PopConfirmButton>
<Divider type="vertical" class="action-divider" v-if="divider && index < getActions.length - 1" />
</template>
<Dropdown :trigger="['hover']" :dropMenuList="getDropdownList" popconfirm v-if="dropDownActions && getDropdownList.length > 0">
<slot name="more"></slot>
<a-button type="link" size="small" v-if="!$slots.more">
<MoreOutlined class="icon-more" />
</a-button>
</Dropdown>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType, computed, toRaw, unref } from 'vue';
import { MoreOutlined } from '@ant-design/icons-vue';
import { Divider, Tooltip, TooltipProps } from 'ant-design-vue';
import Icon from '/@/components/Icon/index';
import { ActionItem, TableActionType } from '/@/components/Table';
import { PopConfirmButton } from '/@/components/Button';
import { Dropdown } from '/@/components/Dropdown';
import { useDesign } from '/@/hooks/web/useDesign';
import { useTableContext } from '../hooks/useTableContext';
import { usePermission } from '/@/hooks/web/usePermission';
import { isBoolean, isFunction, isString } from '/@/utils/is';
import { propTypes } from '/@/utils/propTypes';
import { ACTION_COLUMN_FLAG } from '../const';
import { defineComponent, PropType, computed, toRaw, unref } from 'vue';
import { MoreOutlined } from '@ant-design/icons-vue';
import { Divider, Tooltip, TooltipProps } from 'ant-design-vue';
import Icon from '/@/components/Icon/index';
import { ActionItem, TableActionType } from '/@/components/Table';
import { PopConfirmButton } from '/@/components/Button';
import { Dropdown } from '/@/components/Dropdown';
import { useDesign } from '/@/hooks/web/useDesign';
import { useTableContext } from '../hooks/useTableContext';
import { usePermission } from '/@/hooks/web/usePermission';
import { isBoolean, isFunction, isString } from '/@/utils/is';
import { propTypes } from '/@/utils/propTypes';
import { ACTION_COLUMN_FLAG } from '../const';
export default defineComponent({
name: 'TableAction',
components: { Icon, PopConfirmButton, Divider, Dropdown, MoreOutlined, Tooltip },
props: {
actions: {
type: Array as PropType<ActionItem[]>,
default: null,
},
dropDownActions: {
type: Array as PropType<ActionItem[]>,
default: null,
},
divider: propTypes.bool.def(true),
outside: propTypes.bool,
stopButtonPropagation: propTypes.bool.def(false),
},
setup(props) {
const { prefixCls } = useDesign('basic-table-action');
let table: Partial<TableActionType> = {};
if (!props.outside) {
table = useTableContext();
}
export default defineComponent({
name: 'TableAction',
components: { Icon, PopConfirmButton, Divider, Dropdown, MoreOutlined, Tooltip },
props: {
actions: {
type: Array as PropType<ActionItem[]>,
default: null
},
dropDownActions: {
type: Array as PropType<ActionItem[]>,
default: null
},
divider: propTypes.bool.def(true),
outside: propTypes.bool,
stopButtonPropagation: propTypes.bool.def(false)
},
setup(props) {
const { prefixCls } = useDesign('basic-table-action');
let table: Partial<TableActionType> = {};
if (!props.outside) {
table = useTableContext();
}
const { hasPermission } = usePermission();
function isIfShow(action: ActionItem): boolean {
const ifShow = action.ifShow;
const { hasPermission } = usePermission();
function isIfShow(action: ActionItem): boolean {
const ifShow = action.ifShow;
let isIfShow = true;
let isIfShow = true;
if (isBoolean(ifShow)) {
isIfShow = ifShow;
if (isBoolean(ifShow)) {
isIfShow = ifShow;
}
if (isFunction(ifShow)) {
isIfShow = ifShow(action);
}
return isIfShow;
}
const getActions = computed(() => {
return (toRaw(props.actions) || [])
.filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
})
.map((action) => {
const { popConfirm } = action;
return {
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
type: 'link',
size: 'small',
...action,
...(popConfirm || {}),
onConfirm: popConfirm?.confirm,
onCancel: popConfirm?.cancel,
enable: !!popConfirm
};
});
});
const getDropdownList = computed((): any[] => {
const list = (toRaw(props.dropDownActions) || []).filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
});
return list.map((action, index) => {
const { label, popConfirm } = action;
return {
...action,
...popConfirm,
onConfirm: popConfirm?.confirm,
onCancel: popConfirm?.cancel,
text: label,
divider: index < list.length - 1 ? props.divider : false
};
});
});
const getAlign = computed(() => {
const columns = (table as TableActionType)?.getColumns?.() || [];
const actionColumn = columns.find((item) => item.flag === ACTION_COLUMN_FLAG);
return actionColumn?.align ?? 'left';
});
function getTooltip(data: string | TooltipProps): TooltipProps {
return {
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
placement: 'bottom',
...(isString(data) ? { title: data } : data)
};
}
function onCellClick(e: MouseEvent) {
if (!props.stopButtonPropagation) return;
const path = e.composedPath() as HTMLElement[];
const isInButton = path.find((ele) => {
return ele.tagName?.toUpperCase() === 'BUTTON';
});
isInButton && e.stopPropagation();
}
return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip };
}
if (isFunction(ifShow)) {
isIfShow = ifShow(action);
}
return isIfShow;
}
const getActions = computed(() => {
return (toRaw(props.actions) || [])
.filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
})
.map((action) => {
const { popConfirm } = action;
return {
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
type: 'link',
size: 'small',
...action,
...(popConfirm || {}),
onConfirm: popConfirm?.confirm,
onCancel: popConfirm?.cancel,
enable: !!popConfirm,
};
});
});
const getDropdownList = computed((): any[] => {
const list = (toRaw(props.dropDownActions) || []).filter((action) => {
return hasPermission(action.auth) && isIfShow(action);
});
return list.map((action, index) => {
const { label, popConfirm } = action;
return {
...action,
...popConfirm,
onConfirm: popConfirm?.confirm,
onCancel: popConfirm?.cancel,
text: label,
divider: index < list.length - 1 ? props.divider : false,
};
});
});
const getAlign = computed(() => {
const columns = (table as TableActionType)?.getColumns?.() || [];
const actionColumn = columns.find((item) => item.flag === ACTION_COLUMN_FLAG);
return actionColumn?.align ?? 'left';
});
function getTooltip(data: string | TooltipProps): TooltipProps {
return {
getPopupContainer: () => unref((table as any)?.wrapRef.value) ?? document.body,
placement: 'bottom',
...(isString(data) ? { title: data } : data),
};
}
function onCellClick(e: MouseEvent) {
if (!props.stopButtonPropagation) return;
const path = e.composedPath() as HTMLElement[];
const isInButton = path.find((ele) => {
return ele.tagName?.toUpperCase() === 'BUTTON';
});
isInButton && e.stopPropagation();
}
return { prefixCls, getActions, getDropdownList, getAlign, onCellClick, getTooltip };
},
});
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-action';
@prefix-cls: ~'@{namespace}-basic-table-action';
.@{prefix-cls} {
display: flex;
align-items: center;
.@{prefix-cls} {
display: flex;
align-items: center;
.action-divider {
display: table;
.action-divider {
display: table;
}
&.left {
justify-content: flex-start;
}
&.center {
justify-content: center;
}
&.right {
justify-content: flex-end;
}
button {
display: flex;
align-items: center;
span {
margin-left: 0 !important;
}
}
button.ant-btn-circle {
span {
margin: auto !important;
}
}
.ant-divider,
.ant-divider-vertical {
margin: 0 2px;
}
.icon-more {
transform: rotate(90deg);
svg {
font-size: 1.1em;
font-weight: 700;
}
}
}
&.left {
justify-content: flex-start;
}
&.center {
justify-content: center;
}
&.right {
justify-content: flex-end;
}
button {
display: flex;
align-items: center;
span {
margin-left: 0 !important;
}
}
button.ant-btn-circle {
span {
margin: auto !important;
}
}
.ant-divider,
.ant-divider-vertical {
margin: 0 2px;
}
.icon-more {
transform: rotate(90deg);
svg {
font-size: 1.1em;
font-weight: 700;
}
}
}
</style>

View File

@ -1,94 +1,84 @@
<template>
<Table
v-if="summaryFunc || summaryData"
:showHeader="false"
:bordered="false"
:pagination="false"
:dataSource="getDataSource"
:rowKey="(r) => r[rowKey]"
:columns="getColumns"
tableLayout="fixed"
:scroll="scroll"
/>
<Table v-if="summaryFunc || summaryData" :showHeader="false" :bordered="false" :pagination="false" :dataSource="getDataSource" :rowKey="(r) => r[rowKey]" :columns="getColumns" tableLayout="fixed" :scroll="scroll" />
</template>
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, unref, computed, toRaw } from 'vue';
import { Table } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
import { isFunction } from '/@/utils/is';
import type { BasicColumn } from '../types/table';
import { INDEX_COLUMN_FLAG } from '../const';
import { propTypes } from '/@/utils/propTypes';
import { useTableContext } from '../hooks/useTableContext';
import type { PropType } from 'vue';
import { defineComponent, unref, computed, toRaw } from 'vue';
import { Table } from 'ant-design-vue';
import { cloneDeep } from 'lodash-es';
import { isFunction } from '/@/utils/is';
import type { BasicColumn } from '../types/table';
import { INDEX_COLUMN_FLAG } from '../const';
import { propTypes } from '/@/utils/propTypes';
import { useTableContext } from '../hooks/useTableContext';
const SUMMARY_ROW_KEY = '_row';
const SUMMARY_INDEX_KEY = '_index';
export default defineComponent({
name: 'BasicTableFooter',
components: { Table },
props: {
summaryFunc: {
type: Function as PropType<Fn>,
},
summaryData: {
type: Array as PropType<Recordable[]>,
},
scroll: {
type: Object as PropType<Recordable>,
},
rowKey: propTypes.string.def('key'),
},
setup(props) {
const table = useTableContext();
const SUMMARY_ROW_KEY = '_row';
const SUMMARY_INDEX_KEY = '_index';
export default defineComponent({
name: 'BasicTableFooter',
components: { Table },
props: {
summaryFunc: {
type: Function as PropType<Fn>
},
summaryData: {
type: Array as PropType<Recordable[]>
},
scroll: {
type: Object as PropType<Recordable>
},
rowKey: propTypes.string.def('key')
},
setup(props) {
const table = useTableContext();
const getDataSource = computed((): Recordable[] => {
const { summaryFunc, summaryData } = props;
if (summaryData?.length) {
summaryData.forEach((item, i) => (item[props.rowKey] = `${i}`));
return summaryData;
const getDataSource = computed((): Recordable[] => {
const { summaryFunc, summaryData } = props;
if (summaryData?.length) {
summaryData.forEach((item, i) => (item[props.rowKey] = `${i}`));
return summaryData;
}
if (!isFunction(summaryFunc)) {
return [];
}
let dataSource = toRaw(unref(table.getDataSource()));
dataSource = summaryFunc(dataSource);
dataSource.forEach((item, i) => {
item[props.rowKey] = `${i}`;
});
return dataSource;
});
const getColumns = computed(() => {
const dataSource = unref(getDataSource);
const columns: BasicColumn[] = cloneDeep(table.getColumns());
const index = columns.findIndex((item) => item.flag === INDEX_COLUMN_FLAG);
const hasRowSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_ROW_KEY));
const hasIndexSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_INDEX_KEY));
if (index !== -1) {
if (hasIndexSummary) {
columns[index].customRender = ({ record }) => record[SUMMARY_INDEX_KEY];
columns[index].ellipsis = false;
} else {
Reflect.deleteProperty(columns[index], 'customRender');
}
}
if (table.getRowSelection() && hasRowSummary) {
const isFixed = columns.some((col) => col.fixed === 'left');
columns.unshift({
width: 60,
title: 'selection',
key: 'selectionKey',
align: 'center',
...(isFixed ? { fixed: 'left' } : {}),
customRender: ({ record }) => record[SUMMARY_ROW_KEY]
});
}
return columns;
});
return { getColumns, getDataSource };
}
if (!isFunction(summaryFunc)) {
return [];
}
let dataSource = toRaw(unref(table.getDataSource()));
dataSource = summaryFunc(dataSource);
dataSource.forEach((item, i) => {
item[props.rowKey] = `${i}`;
});
return dataSource;
});
const getColumns = computed(() => {
const dataSource = unref(getDataSource);
const columns: BasicColumn[] = cloneDeep(table.getColumns());
const index = columns.findIndex((item) => item.flag === INDEX_COLUMN_FLAG);
const hasRowSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_ROW_KEY));
const hasIndexSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_INDEX_KEY));
if (index !== -1) {
if (hasIndexSummary) {
columns[index].customRender = ({ record }) => record[SUMMARY_INDEX_KEY];
columns[index].ellipsis = false;
} else {
Reflect.deleteProperty(columns[index], 'customRender');
}
}
if (table.getRowSelection() && hasRowSummary) {
const isFixed = columns.some((col) => col.fixed === 'left');
columns.unshift({
width: 60,
title: 'selection',
key: 'selectionKey',
align: 'center',
...(isFixed ? { fixed: 'left' } : {}),
customRender: ({ record }) => record[SUMMARY_ROW_KEY],
});
}
return columns;
});
return { getColumns, getDataSource };
},
});
});
</script>

View File

@ -1,81 +1,73 @@
<template>
<div style="width: 100%">
<div v-if="$slots.headerTop" style="margin: 5px">
<slot name="headerTop"></slot>
<div style="width: 100%">
<div v-if="$slots.headerTop" style="margin: 5px">
<slot name="headerTop"></slot>
</div>
<div class="flex items-center">
<slot name="tableTitle" v-if="$slots.tableTitle"></slot>
<TableTitle :helpMessage="titleHelpMessage" :title="title" v-if="!$slots.tableTitle && title" />
<div :class="`${prefixCls}__toolbar`">
<slot name="toolbar"></slot>
<Divider type="vertical" v-if="$slots.toolbar && showTableSetting" />
<TableSetting :setting="tableSetting" v-if="showTableSetting" @columns-change="handleColumnChange" />
</div>
</div>
</div>
<div class="flex items-center">
<slot name="tableTitle" v-if="$slots.tableTitle"></slot>
<TableTitle
:helpMessage="titleHelpMessage"
:title="title"
v-if="!$slots.tableTitle && title"
/>
<div :class="`${prefixCls}__toolbar`">
<slot name="toolbar"></slot>
<Divider type="vertical" v-if="$slots.toolbar && showTableSetting" />
<TableSetting
:setting="tableSetting"
v-if="showTableSetting"
@columns-change="handleColumnChange"
/>
</div>
</div>
</div>
</template>
<script lang="ts">
import type { TableSetting, ColumnChangeParam } from '../types/table';
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import { Divider } from 'ant-design-vue';
import TableSettingComponent from './settings/index.vue';
import TableTitle from './TableTitle.vue';
import { useDesign } from '/@/hooks/web/useDesign';
import type { TableSetting, ColumnChangeParam } from '../types/table';
import type { PropType } from 'vue';
import { defineComponent } from 'vue';
import { Divider } from 'ant-design-vue';
import TableSettingComponent from './settings/index.vue';
import TableTitle from './TableTitle.vue';
import { useDesign } from '/@/hooks/web/useDesign';
export default defineComponent({
name: 'BasicTableHeader',
components: {
Divider,
TableTitle,
TableSetting: TableSettingComponent,
},
props: {
title: {
type: [Function, String] as PropType<string | ((data: Recordable) => string)>,
},
tableSetting: {
type: Object as PropType<TableSetting>,
},
showTableSetting: {
type: Boolean,
},
titleHelpMessage: {
type: [String, Array] as PropType<string | string[]>,
default: '',
},
},
emits: ['columns-change'],
setup(_, { emit }) {
const { prefixCls } = useDesign('basic-table-header');
function handleColumnChange(data: ColumnChangeParam[]) {
emit('columns-change', data);
}
return { prefixCls, handleColumnChange };
},
});
export default defineComponent({
name: 'BasicTableHeader',
components: {
Divider,
TableTitle,
TableSetting: TableSettingComponent
},
props: {
title: {
type: [Function, String] as PropType<string | ((data: Recordable) => string)>
},
tableSetting: {
type: Object as PropType<TableSetting>
},
showTableSetting: {
type: Boolean
},
titleHelpMessage: {
type: [String, Array] as PropType<string | string[]>,
default: ''
}
},
emits: ['columns-change'],
setup(_, { emit }) {
const { prefixCls } = useDesign('basic-table-header');
function handleColumnChange(data: ColumnChangeParam[]) {
emit('columns-change', data);
}
return { prefixCls, handleColumnChange };
}
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-header';
@prefix-cls: ~'@{namespace}-basic-table-header';
.@{prefix-cls} {
&__toolbar {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
.@{prefix-cls} {
&__toolbar {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
> * {
margin-right: 8px;
}
> * {
margin-right: 8px;
}
}
}
}
</style>

View File

@ -1,85 +1,76 @@
<template>
<div
:class="prefixCls"
class="flex items-center mx-auto"
v-if="imgList && imgList.length"
:style="getWrapStyle"
>
<Badge :count="!showBadge || imgList.length == 1 ? 0 : imgList.length" v-if="simpleShow">
<div class="img-div">
<PreviewGroup>
<template v-for="(img, index) in imgList" :key="img">
<Image
:width="size"
:style="{
display: index === 0 ? '' : 'none !important',
}"
:src="srcPrefix + img"
/>
</template>
<div :class="prefixCls" class="flex items-center mx-auto" v-if="imgList && imgList.length" :style="getWrapStyle">
<Badge :count="!showBadge || imgList.length == 1 ? 0 : imgList.length" v-if="simpleShow">
<div class="img-div">
<PreviewGroup>
<template v-for="(img, index) in imgList" :key="img">
<Image
:width="size"
:style="{
display: index === 0 ? '' : 'none !important'
}"
:src="srcPrefix + img"
/>
</template>
</PreviewGroup>
</div>
</Badge>
<PreviewGroup v-else>
<template v-for="(img, index) in imgList" :key="img">
<Image :width="size" :style="{ marginLeft: index === 0 ? 0 : margin }" :src="srcPrefix + img" />
</template>
</PreviewGroup>
</div>
</Badge>
<PreviewGroup v-else>
<template v-for="(img, index) in imgList" :key="img">
<Image
:width="size"
:style="{ marginLeft: index === 0 ? 0 : margin }"
:src="srcPrefix + img"
/>
</template>
</PreviewGroup>
</div>
</div>
</template>
<script lang="ts">
import type { CSSProperties } from 'vue';
import { defineComponent, computed } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { Image, Badge } from 'ant-design-vue';
import { propTypes } from '/@/utils/propTypes';
import type { CSSProperties } from 'vue';
import { defineComponent, computed } from 'vue';
import { useDesign } from '/@/hooks/web/useDesign';
import { Image, Badge } from 'ant-design-vue';
import { propTypes } from '/@/utils/propTypes';
export default defineComponent({
name: 'TableImage',
components: { Image, PreviewGroup: Image.PreviewGroup, Badge },
props: {
imgList: propTypes.arrayOf(propTypes.string),
size: propTypes.number.def(40),
// 是否简单显示(只显示第一张图片)
simpleShow: propTypes.bool,
// 简单模式下是否显示图片数量的badge
showBadge: propTypes.bool.def(true),
// 图片间距
margin: propTypes.number.def(4),
// src前缀将会附加在imgList中每一项之前
srcPrefix: propTypes.string.def(''),
},
setup(props) {
const getWrapStyle = computed((): CSSProperties => {
const { size } = props;
const s = `${size}px`;
return { height: s, width: s };
});
export default defineComponent({
name: 'TableImage',
components: { Image, PreviewGroup: Image.PreviewGroup, Badge },
props: {
imgList: propTypes.arrayOf(propTypes.string),
size: propTypes.number.def(40),
// 是否简单显示(只显示第一张图片)
simpleShow: propTypes.bool,
// 简单模式下是否显示图片数量的badge
showBadge: propTypes.bool.def(true),
// 图片间距
margin: propTypes.number.def(4),
// src前缀将会附加在imgList中每一项之前
srcPrefix: propTypes.string.def('')
},
setup(props) {
const getWrapStyle = computed((): CSSProperties => {
const { size } = props;
const s = `${size}px`;
return { height: s, width: s };
});
const { prefixCls } = useDesign('basic-table-img');
return { prefixCls, getWrapStyle };
},
});
const { prefixCls } = useDesign('basic-table-img');
return { prefixCls, getWrapStyle };
}
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-img';
@prefix-cls: ~'@{namespace}-basic-table-img';
.@{prefix-cls} {
.ant-image {
margin-right: 4px;
cursor: zoom-in;
.@{prefix-cls} {
.ant-image {
margin-right: 4px;
cursor: zoom-in;
img {
border-radius: 2px;
}
img {
border-radius: 2px;
}
}
.img-div {
display: inline-grid;
}
}
.img-div {
display: inline-grid;
}
}
</style>

View File

@ -1,54 +1,54 @@
<template>
<BasicTitle :class="prefixCls" v-if="getTitle" :helpMessage="helpMessage">
{{ getTitle }}
</BasicTitle>
<BasicTitle :class="prefixCls" v-if="getTitle" :helpMessage="helpMessage">
{{ getTitle }}
</BasicTitle>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import { BasicTitle } from '/@/components/Basic/index';
import { useDesign } from '/@/hooks/web/useDesign';
import { isFunction } from '/@/utils/is';
import { computed, defineComponent, PropType } from 'vue';
import { BasicTitle } from '/@/components/Basic/index';
import { useDesign } from '/@/hooks/web/useDesign';
import { isFunction } from '/@/utils/is';
export default defineComponent({
name: 'BasicTableTitle',
components: { BasicTitle },
props: {
title: {
type: [Function, String] as PropType<string | ((data: Recordable) => string)>,
},
getSelectRows: {
type: Function as PropType<() => Recordable[]>,
},
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
},
},
setup(props) {
const { prefixCls } = useDesign('basic-table-title');
export default defineComponent({
name: 'BasicTableTitle',
components: { BasicTitle },
props: {
title: {
type: [Function, String] as PropType<string | ((data: Recordable) => string)>
},
getSelectRows: {
type: Function as PropType<() => Recordable[]>
},
helpMessage: {
type: [String, Array] as PropType<string | string[]>
}
},
setup(props) {
const { prefixCls } = useDesign('basic-table-title');
const getTitle = computed(() => {
const { title, getSelectRows = () => {} } = props;
let tit = title;
const getTitle = computed(() => {
const { title, getSelectRows = () => {} } = props;
let tit = title;
if (isFunction(title)) {
tit = title({
selectRows: getSelectRows(),
});
if (isFunction(title)) {
tit = title({
selectRows: getSelectRows()
});
}
return tit;
});
return { getTitle, prefixCls };
}
return tit;
});
return { getTitle, prefixCls };
},
});
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-table-title';
@prefix-cls: ~'@{namespace}-basic-table-title';
.@{prefix-cls} {
display: flex;
justify-content: space-between;
align-items: center;
// border-left: 8px solid @primary-color;
}
.@{prefix-cls} {
display: flex;
justify-content: space-between;
align-items: center;
// border-left: 8px solid @primary-color;
}
</style>

View File

@ -1,512 +1,500 @@
<script lang="tsx">
import type { CSSProperties, PropType } from 'vue';
import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue';
import type { BasicColumn } from '../../types/table';
import type { EditRecordRow } from './index';
import { CheckOutlined, CloseOutlined, FormOutlined } from '@ant-design/icons-vue';
import { CellComponent } from './CellComponent';
import type { CSSProperties, PropType } from 'vue';
import { computed, defineComponent, nextTick, ref, toRaw, unref, watchEffect } from 'vue';
import type { BasicColumn } from '../../types/table';
import type { EditRecordRow } from './index';
import { CheckOutlined, CloseOutlined, FormOutlined } from '@ant-design/icons-vue';
import { CellComponent } from './CellComponent';
import { useDesign } from '/@/hooks/web/useDesign';
import { useTableContext } from '../../hooks/useTableContext';
import { useDesign } from '/@/hooks/web/useDesign';
import { useTableContext } from '../../hooks/useTableContext';
import clickOutside from '/@/directives/clickOutside';
import clickOutside from '/@/directives/clickOutside';
import { propTypes } from '/@/utils/propTypes';
import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is';
import { createPlaceholderMessage } from './helper';
import { omit, pick, set } from 'lodash-es';
import { treeToList } from '/@/utils/helper/treeHelper';
import { Spin } from 'ant-design-vue';
import { propTypes } from '/@/utils/propTypes';
import { isArray, isBoolean, isFunction, isNumber, isString } from '/@/utils/is';
import { createPlaceholderMessage } from './helper';
import { omit, pick, set } from 'lodash-es';
import { treeToList } from '/@/utils/helper/treeHelper';
import { Spin } from 'ant-design-vue';
export default defineComponent({
name: 'EditableCell',
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, Spin },
directives: {
clickOutside,
},
props: {
value: {
type: [String, Number, Boolean, Object] as PropType<string | number | boolean | Recordable>,
default: '',
},
record: {
type: Object as PropType<EditRecordRow>,
},
column: {
type: Object as PropType<BasicColumn>,
default: () => ({}),
},
index: propTypes.number,
},
setup(props) {
const table = useTableContext();
const isEdit = ref(false);
const elRef = ref();
const ruleVisible = ref(false);
const ruleMessage = ref('');
const optionsRef = ref<LabelValueOptions>([]);
const currentValueRef = ref<any>(props.value);
const defaultValueRef = ref<any>(props.value);
const spinning = ref<boolean>(false);
export default defineComponent({
name: 'EditableCell',
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent, Spin },
directives: {
clickOutside
},
props: {
value: {
type: [String, Number, Boolean, Object] as PropType<string | number | boolean | Recordable>,
default: ''
},
record: {
type: Object as PropType<EditRecordRow>
},
column: {
type: Object as PropType<BasicColumn>,
default: () => ({})
},
index: propTypes.number
},
setup(props) {
const table = useTableContext();
const isEdit = ref(false);
const elRef = ref();
const ruleVisible = ref(false);
const ruleMessage = ref('');
const optionsRef = ref<LabelValueOptions>([]);
const currentValueRef = ref<any>(props.value);
const defaultValueRef = ref<any>(props.value);
const spinning = ref<boolean>(false);
const { prefixCls } = useDesign('editable-cell');
const { prefixCls } = useDesign('editable-cell');
const getComponent = computed(() => props.column?.editComponent || 'Input');
const getRule = computed(() => props.column?.editRule);
const getComponent = computed(() => props.column?.editComponent || 'Input');
const getRule = computed(() => props.column?.editRule);
const getRuleVisible = computed(() => {
return unref(ruleMessage) && unref(ruleVisible);
});
const getRuleVisible = computed(() => {
return unref(ruleMessage) && unref(ruleVisible);
});
const getIsCheckComp = computed(() => {
const component = unref(getComponent);
return ['Checkbox', 'Switch'].includes(component);
});
const getIsCheckComp = computed(() => {
const component = unref(getComponent);
return ['Checkbox', 'Switch'].includes(component);
});
const getComponentProps = computed(() => {
const isCheckValue = unref(getIsCheckComp);
const getComponentProps = computed(() => {
const isCheckValue = unref(getIsCheckComp);
const valueField = isCheckValue ? 'checked' : 'value';
const val = unref(currentValueRef);
const valueField = isCheckValue ? 'checked' : 'value';
const val = unref(currentValueRef);
const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val;
const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val;
let compProps = props.column?.editComponentProps ?? {};
const { record, column, index } = props;
let compProps = props.column?.editComponentProps ?? {};
const { record, column, index } = props;
if (isFunction(compProps)) {
compProps = compProps({ text: val, record, column, index }) ?? {};
}
const component = unref(getComponent);
const apiSelectProps: Recordable = {};
if (component === 'ApiSelect') {
apiSelectProps.cache = true;
}
if (isFunction(compProps)) {
compProps = compProps({ text: val, record, column, index }) ?? {};
}
const component = unref(getComponent);
const apiSelectProps: Recordable = {};
if (component === 'ApiSelect') {
apiSelectProps.cache = true;
}
return {
size: 'small',
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
placeholder: createPlaceholderMessage(unref(getComponent)),
...apiSelectProps,
...omit(compProps, 'onChange'),
[valueField]: value,
} as any;
});
return {
size: 'small',
getPopupContainer: () => unref(table?.wrapRef.value) ?? document.body,
placeholder: createPlaceholderMessage(unref(getComponent)),
...apiSelectProps,
...omit(compProps, 'onChange'),
[valueField]: value
} as any;
});
const getValues = computed(() => {
const { editValueMap } = props.column;
const getValues = computed(() => {
const { editValueMap } = props.column;
const value = unref(currentValueRef);
const value = unref(currentValueRef);
if (editValueMap && isFunction(editValueMap)) {
return editValueMap(value);
}
if (editValueMap && isFunction(editValueMap)) {
return editValueMap(value);
}
const component = unref(getComponent);
if (!component.includes('Select')) {
return value;
}
const component = unref(getComponent);
if (!component.includes('Select')) {
return value;
}
const options: LabelValueOptions =
unref(getComponentProps)?.options ?? (unref(optionsRef) || []);
const option = options.find((item) => `${item.value}` === `${value}`);
const options: LabelValueOptions = unref(getComponentProps)?.options ?? (unref(optionsRef) || []);
const option = options.find((item) => `${item.value}` === `${value}`);
return option?.label ?? value;
});
return option?.label ?? value;
});
const getWrapperStyle = computed((): CSSProperties => {
if (unref(getIsCheckComp) || unref(getRowEditable)) {
return {};
}
return {
width: 'calc(100% - 48px)',
};
});
const getWrapperStyle = computed((): CSSProperties => {
if (unref(getIsCheckComp) || unref(getRowEditable)) {
return {};
}
return {
width: 'calc(100% - 48px)'
};
});
const getWrapperClass = computed(() => {
const { align = 'center' } = props.column;
return `edit-cell-align-${align}`;
});
const getWrapperClass = computed(() => {
const { align = 'center' } = props.column;
return `edit-cell-align-${align}`;
});
const getRowEditable = computed(() => {
const { editable } = props.record || {};
return !!editable;
});
const getRowEditable = computed(() => {
const { editable } = props.record || {};
return !!editable;
});
watchEffect(() => {
defaultValueRef.value = props.value;
currentValueRef.value = props.value;
});
watchEffect(() => {
defaultValueRef.value = props.value;
currentValueRef.value = props.value;
});
watchEffect(() => {
const { editable } = props.column;
if (isBoolean(editable) || isBoolean(unref(getRowEditable))) {
isEdit.value = !!editable || unref(getRowEditable);
}
});
watchEffect(() => {
const { editable } = props.column;
if (isBoolean(editable) || isBoolean(unref(getRowEditable))) {
isEdit.value = !!editable || unref(getRowEditable);
}
});
function handleEdit() {
if (unref(getRowEditable) || unref(props.column?.editRow)) return;
ruleMessage.value = '';
isEdit.value = true;
nextTick(() => {
const el = unref(elRef);
el?.focus?.();
});
}
async function handleChange(e: any) {
const component = unref(getComponent);
if (!e) {
currentValueRef.value = e;
} else if (component === 'Checkbox') {
currentValueRef.value = (e as ChangeEvent).target.checked;
} else if (component === 'Switch') {
currentValueRef.value = e;
} else if (e?.target && Reflect.has(e.target, 'value')) {
currentValueRef.value = (e as ChangeEvent).target.value;
} else if (isString(e) || isBoolean(e) || isNumber(e)) {
currentValueRef.value = e;
}
const onChange = unref(getComponentProps)?.onChange;
if (onChange && isFunction(onChange)) onChange(...arguments);
table.emit?.('edit-change', {
column: props.column,
value: unref(currentValueRef),
record: toRaw(props.record),
});
handleSubmiRule();
}
async function handleSubmiRule() {
const { column, record } = props;
const { editRule } = column;
const currentValue = unref(currentValueRef);
if (editRule) {
if (isBoolean(editRule) && !currentValue && !isNumber(currentValue)) {
ruleVisible.value = true;
const component = unref(getComponent);
ruleMessage.value = createPlaceholderMessage(component);
return false;
}
if (isFunction(editRule)) {
const res = await editRule(currentValue, record as Recordable);
if (!!res) {
ruleMessage.value = res;
ruleVisible.value = true;
return false;
} else {
ruleMessage.value = '';
return true;
function handleEdit() {
if (unref(getRowEditable) || unref(props.column?.editRow)) return;
ruleMessage.value = '';
isEdit.value = true;
nextTick(() => {
const el = unref(elRef);
el?.focus?.();
});
}
}
}
ruleMessage.value = '';
return true;
}
async function handleSubmit(needEmit = true, valid = true) {
if (valid) {
const isPass = await handleSubmiRule();
if (!isPass) return false;
}
async function handleChange(e: any) {
const component = unref(getComponent);
if (!e) {
currentValueRef.value = e;
} else if (component === 'Checkbox') {
currentValueRef.value = (e as ChangeEvent).target.checked;
} else if (component === 'Switch') {
currentValueRef.value = e;
} else if (e?.target && Reflect.has(e.target, 'value')) {
currentValueRef.value = (e as ChangeEvent).target.value;
} else if (isString(e) || isBoolean(e) || isNumber(e)) {
currentValueRef.value = e;
}
const onChange = unref(getComponentProps)?.onChange;
if (onChange && isFunction(onChange)) onChange(...arguments);
const { column, index, record } = props;
if (!record) return false;
const { key, dataIndex } = column;
const value = unref(currentValueRef);
if (!key && !dataIndex) return;
const dataKey = (dataIndex || key) as string;
if (!record.editable) {
const { getBindValues } = table;
const { beforeEditSubmit, columns } = unref(getBindValues);
if (beforeEditSubmit && isFunction(beforeEditSubmit)) {
spinning.value = true;
const keys: string[] = columns
.map((_column) => _column.dataIndex)
.filter((field) => !!field) as string[];
let result: any = true;
try {
result = await beforeEditSubmit({
record: pick(record, keys),
index,
key: dataKey as string,
value,
});
} catch (e) {
result = false;
} finally {
spinning.value = false;
table.emit?.('edit-change', {
column: props.column,
value: unref(currentValueRef),
record: toRaw(props.record)
});
handleSubmiRule();
}
if (result === false) {
return;
async function handleSubmiRule() {
const { column, record } = props;
const { editRule } = column;
const currentValue = unref(currentValueRef);
if (editRule) {
if (isBoolean(editRule) && !currentValue && !isNumber(currentValue)) {
ruleVisible.value = true;
const component = unref(getComponent);
ruleMessage.value = createPlaceholderMessage(component);
return false;
}
if (isFunction(editRule)) {
const res = await editRule(currentValue, record as Recordable);
if (!!res) {
ruleMessage.value = res;
ruleVisible.value = true;
return false;
} else {
ruleMessage.value = '';
return true;
}
}
}
ruleMessage.value = '';
return true;
}
}
}
set(record, dataKey, value);
//const record = await table.updateTableData(index, dataKey, value);
needEmit && table.emit?.('edit-end', { record, index, key: dataKey, value });
isEdit.value = false;
}
async function handleSubmit(needEmit = true, valid = true) {
if (valid) {
const isPass = await handleSubmiRule();
if (!isPass) return false;
}
async function handleEnter() {
if (props.column?.editRow) {
return;
}
handleSubmit();
}
const { column, index, record } = props;
if (!record) return false;
const { key, dataIndex } = column;
const value = unref(currentValueRef);
if (!key && !dataIndex) return;
function handleSubmitClick() {
handleSubmit();
}
const dataKey = (dataIndex || key) as string;
function handleCancel() {
isEdit.value = false;
currentValueRef.value = defaultValueRef.value;
const { column, index, record } = props;
const { key, dataIndex } = column;
table.emit?.('edit-cancel', {
record,
index,
key: dataIndex || key,
value: unref(currentValueRef),
});
}
if (!record.editable) {
const { getBindValues } = table;
function onClickOutside() {
if (props.column?.editable || unref(getRowEditable)) {
return;
}
const component = unref(getComponent);
const { beforeEditSubmit, columns } = unref(getBindValues);
if (component.includes('Input')) {
handleCancel();
}
}
if (beforeEditSubmit && isFunction(beforeEditSubmit)) {
spinning.value = true;
const keys: string[] = columns.map((_column) => _column.dataIndex).filter((field) => !!field) as string[];
let result: any = true;
try {
result = await beforeEditSubmit({
record: pick(record, keys),
index,
key: dataKey as string,
value
});
} catch (e) {
result = false;
} finally {
spinning.value = false;
}
if (result === false) {
return;
}
}
}
set(record, dataKey, value);
//const record = await table.updateTableData(index, dataKey, value);
needEmit && table.emit?.('edit-end', { record, index, key: dataKey, value });
isEdit.value = false;
}
async function handleEnter() {
if (props.column?.editRow) {
return;
}
handleSubmit();
}
function handleSubmitClick() {
handleSubmit();
}
function handleCancel() {
isEdit.value = false;
currentValueRef.value = defaultValueRef.value;
const { column, index, record } = props;
const { key, dataIndex } = column;
table.emit?.('edit-cancel', {
record,
index,
key: dataIndex || key,
value: unref(currentValueRef)
});
}
function onClickOutside() {
if (props.column?.editable || unref(getRowEditable)) {
return;
}
const component = unref(getComponent);
if (component.includes('Input')) {
handleCancel();
}
}
// only ApiSelect or TreeSelect
function handleOptionsChange(options: LabelValueOptions) {
const { replaceFields } = unref(getComponentProps);
const component = unref(getComponent);
if (component === 'ApiTreeSelect') {
const { title = 'title', value = 'value', children = 'children' } = replaceFields || {};
let listOptions: Recordable[] = treeToList(options, { children });
listOptions = listOptions.map((item) => {
return {
label: item[title],
value: item[value]
};
});
optionsRef.value = listOptions as LabelValueOptions;
} else {
optionsRef.value = options;
}
}
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) {
if (props.record) {
/* eslint-disable */
isArray(props.record[cbs]) ? props.record[cbs]?.push(handle) : (props.record[cbs] = [handle]);
}
}
if (props.record) {
initCbs('submitCbs', handleSubmit);
initCbs('validCbs', handleSubmiRule);
initCbs('cancelCbs', handleCancel);
if (props.column.dataIndex) {
if (!props.record.editValueRefs) props.record.editValueRefs = {};
props.record.editValueRefs[props.column.dataIndex as any] = currentValueRef;
}
/* eslint-disable */
props.record.onCancelEdit = () => {
isArray(props.record?.cancelCbs) && props.record?.cancelCbs.forEach((fn) => fn());
};
/* eslint-disable */
props.record.onSubmitEdit = async () => {
if (isArray(props.record?.submitCbs)) {
if (!props.record?.onValid?.()) return;
const submitFns = props.record?.submitCbs || [];
submitFns.forEach((fn) => fn(false, false));
table.emit?.('edit-row-end');
return true;
}
};
}
// only ApiSelect or TreeSelect
function handleOptionsChange(options: LabelValueOptions) {
const { replaceFields } = unref(getComponentProps);
const component = unref(getComponent);
if (component === 'ApiTreeSelect') {
const { title = 'title', value = 'value', children = 'children' } = replaceFields || {};
let listOptions: Recordable[] = treeToList(options, { children });
listOptions = listOptions.map((item) => {
return {
label: item[title],
value: item[value],
isEdit,
prefixCls,
handleEdit,
currentValueRef,
handleSubmit,
handleChange,
handleCancel,
elRef,
getComponent,
getRule,
onClickOutside,
ruleMessage,
getRuleVisible,
getComponentProps,
handleOptionsChange,
getWrapperStyle,
getWrapperClass,
getRowEditable,
getValues,
handleEnter,
handleSubmitClick,
spinning
};
});
optionsRef.value = listOptions as LabelValueOptions;
} else {
optionsRef.value = options;
},
render() {
return (
<div class={this.prefixCls}>
<div v-show={!this.isEdit} class={{ [`${this.prefixCls}__normal`]: true, 'ellipsis-cell': this.column.ellipsis }} onClick={this.handleEdit}>
<div class="cell-content" title={this.column.ellipsis ? this.getValues ?? '' : ''}>
{this.column.editRender
? this.column.editRender({
text: this.value,
record: this.record as Recordable,
column: this.column,
index: this.index
})
: this.getValues
? this.getValues
: '\u00A0'}
</div>
{!this.column.editRow && <FormOutlined class={`${this.prefixCls}__normal-icon`} />}
</div>
{this.isEdit && (
<Spin spinning={this.spinning}>
<div class={`${this.prefixCls}__wrapper`} v-click-outside={this.onClickOutside}>
<CellComponent
{...this.getComponentProps}
component={this.getComponent}
style={this.getWrapperStyle}
popoverVisible={this.getRuleVisible}
rule={this.getRule}
ruleMessage={this.ruleMessage}
class={this.getWrapperClass}
ref="elRef"
onChange={this.handleChange}
onOptionsChange={this.handleOptionsChange}
onPressEnter={this.handleEnter}
/>
{!this.getRowEditable && (
<div class={`${this.prefixCls}__action`}>
<CheckOutlined class={[`${this.prefixCls}__icon`, 'mx-2']} onClick={this.handleSubmitClick} />
<CloseOutlined class={`${this.prefixCls}__icon `} onClick={this.handleCancel} />
</div>
)}
</div>
</Spin>
)}
</div>
);
}
}
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) {
if (props.record) {
/* eslint-disable */
isArray(props.record[cbs])
? props.record[cbs]?.push(handle)
: (props.record[cbs] = [handle]);
}
}
if (props.record) {
initCbs('submitCbs', handleSubmit);
initCbs('validCbs', handleSubmiRule);
initCbs('cancelCbs', handleCancel);
if (props.column.dataIndex) {
if (!props.record.editValueRefs) props.record.editValueRefs = {};
props.record.editValueRefs[props.column.dataIndex as any] = currentValueRef;
}
/* eslint-disable */
props.record.onCancelEdit = () => {
isArray(props.record?.cancelCbs) && props.record?.cancelCbs.forEach((fn) => fn());
};
/* eslint-disable */
props.record.onSubmitEdit = async () => {
if (isArray(props.record?.submitCbs)) {
if (!props.record?.onValid?.()) return;
const submitFns = props.record?.submitCbs || [];
submitFns.forEach((fn) => fn(false, false));
table.emit?.('edit-row-end');
return true;
}
};
}
return {
isEdit,
prefixCls,
handleEdit,
currentValueRef,
handleSubmit,
handleChange,
handleCancel,
elRef,
getComponent,
getRule,
onClickOutside,
ruleMessage,
getRuleVisible,
getComponentProps,
handleOptionsChange,
getWrapperStyle,
getWrapperClass,
getRowEditable,
getValues,
handleEnter,
handleSubmitClick,
spinning,
};
},
render() {
return (
<div class={this.prefixCls}>
<div
v-show={!this.isEdit}
class={{ [`${this.prefixCls}__normal`]: true, 'ellipsis-cell': this.column.ellipsis }}
onClick={this.handleEdit}
>
<div class="cell-content" title={this.column.ellipsis ? this.getValues ?? '' : ''}>
{this.column.editRender
? this.column.editRender({
text: this.value,
record: this.record as Recordable,
column: this.column,
index: this.index,
})
: this.getValues
? this.getValues
: '\u00A0'}
</div>
{!this.column.editRow && <FormOutlined class={`${this.prefixCls}__normal-icon`} />}
</div>
{this.isEdit && (
<Spin spinning={this.spinning}>
<div class={`${this.prefixCls}__wrapper`} v-click-outside={this.onClickOutside}>
<CellComponent
{...this.getComponentProps}
component={this.getComponent}
style={this.getWrapperStyle}
popoverVisible={this.getRuleVisible}
rule={this.getRule}
ruleMessage={this.ruleMessage}
class={this.getWrapperClass}
ref="elRef"
onChange={this.handleChange}
onOptionsChange={this.handleOptionsChange}
onPressEnter={this.handleEnter}
/>
{!this.getRowEditable && (
<div class={`${this.prefixCls}__action`}>
<CheckOutlined
class={[`${this.prefixCls}__icon`, 'mx-2']}
onClick={this.handleSubmitClick}
/>
<CloseOutlined class={`${this.prefixCls}__icon `} onClick={this.handleCancel} />
</div>
)}
</div>
</Spin>
)}
</div>
);
},
});
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-editable-cell';
@prefix-cls: ~'@{namespace}-editable-cell';
.edit-cell-align-left {
text-align: left;
.edit-cell-align-left {
text-align: left;
input:not(.ant-calendar-picker-input, .ant-time-picker-input) {
text-align: left;
}
}
.edit-cell-align-center {
text-align: center;
input:not(.ant-calendar-picker-input, .ant-time-picker-input) {
text-align: center;
}
}
.edit-cell-align-right {
text-align: right;
input:not(.ant-calendar-picker-input, .ant-time-picker-input) {
text-align: right;
}
}
.edit-cell-rule-popover {
.ant-popover-inner-content {
padding: 4px 8px;
color: @error-color;
// border: 1px solid @error-color;
border-radius: 2px;
}
}
.@{prefix-cls} {
position: relative;
&__wrapper {
display: flex;
align-items: center;
justify-content: center;
> .ant-select {
min-width: calc(100% - 50px);
}
}
&__icon {
&:hover {
transform: scale(1.2);
svg {
color: @primary-color;
input:not(.ant-calendar-picker-input, .ant-time-picker-input) {
text-align: left;
}
}
}
.ellipsis-cell {
.cell-content {
overflow-wrap: break-word;
word-break: break-word;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.edit-cell-align-center {
text-align: center;
input:not(.ant-calendar-picker-input, .ant-time-picker-input) {
text-align: center;
}
}
&__normal {
&-icon {
position: absolute;
top: 4px;
right: 0;
display: none;
width: 20px;
cursor: pointer;
}
.edit-cell-align-right {
text-align: right;
input:not(.ant-calendar-picker-input, .ant-time-picker-input) {
text-align: right;
}
}
&:hover {
.@{prefix-cls}__normal-icon {
display: inline-block;
}
.edit-cell-rule-popover {
.ant-popover-inner-content {
padding: 4px 8px;
color: @error-color;
// border: 1px solid @error-color;
border-radius: 2px;
}
}
.@{prefix-cls} {
position: relative;
&__wrapper {
display: flex;
align-items: center;
justify-content: center;
> .ant-select {
min-width: calc(100% - 50px);
}
}
&__icon {
&:hover {
transform: scale(1.2);
svg {
color: @primary-color;
}
}
}
.ellipsis-cell {
.cell-content {
overflow-wrap: break-word;
word-break: break-word;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
&__normal {
&-icon {
position: absolute;
top: 4px;
right: 0;
display: none;
width: 20px;
cursor: pointer;
}
}
&:hover {
.@{prefix-cls}__normal-icon {
display: inline-block;
}
}
}
}
</style>

View File

@ -1,474 +1,437 @@
<template>
<Tooltip placement="top">
<template #title>
<span>{{ t('列设置') }}</span>
</template>
<Popover
placement="bottomLeft"
trigger="click"
@visible-change="handleVisibleChange"
:overlayClassName="`${prefixCls}__cloumn-list`"
:getPopupContainer="getPopupContainer"
>
<template #title>
<div :class="`${prefixCls}__popover-title`">
<Checkbox
:indeterminate="indeterminate"
v-model:checked="checkAll"
@change="onCheckAllChange"
>
{{ t('列展示') }}
</Checkbox>
<Tooltip placement="top">
<template #title>
<span>{{ t('列设置') }}</span>
</template>
<Popover placement="bottomLeft" trigger="click" @visible-change="handleVisibleChange" :overlayClassName="`${prefixCls}__cloumn-list`" :getPopupContainer="getPopupContainer">
<template #title>
<div :class="`${prefixCls}__popover-title`">
<Checkbox :indeterminate="indeterminate" v-model:checked="checkAll" @change="onCheckAllChange">
{{ t('列展示') }}
</Checkbox>
<Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange">
{{ t('序号列') }}
</Checkbox>
<Checkbox v-model:checked="checkIndex" @change="handleIndexCheckChange">
{{ t('序号列') }}
</Checkbox>
<Checkbox
v-model:checked="checkSelect"
@change="handleSelectCheckChange"
:disabled="!defaultRowSelection"
>
{{ t('勾选列') }}
</Checkbox>
<Checkbox v-model:checked="checkSelect" @change="handleSelectCheckChange" :disabled="!defaultRowSelection">
{{ t('勾选列') }}
</Checkbox>
<a-button size="small" type="link" @click="reset">
{{ t('重置') }}
</a-button>
</div>
</template>
<template #content>
<ScrollContainer>
<CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
<template v-for="item in plainOptions" :key="item.value">
<div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)">
<DragOutlined class="table-column-drag-icon" />
<Checkbox :value="item.value">
{{ item.label }}
</Checkbox>
<Tooltip
placement="bottomLeft"
:mouseLeaveDelay="0.4"
:getPopupContainer="getPopupContainer"
>
<template #title>
{{ t('固定到左侧') }}
</template>
<Icon
icon="line-md:arrow-align-left"
:class="[
`${prefixCls}__fixed-left`,
{
active: item.fixed === 'left',
disabled: !checkedList.includes(item.value),
},
]"
@click="handleColumnFixed(item, 'left')"
/>
</Tooltip>
<Divider type="vertical" />
<Tooltip
placement="bottomLeft"
:mouseLeaveDelay="0.4"
:getPopupContainer="getPopupContainer"
>
<template #title>
{{ t('固定到右侧') }}
</template>
<Icon
icon="line-md:arrow-align-left"
:class="[
`${prefixCls}__fixed-right`,
{
active: item.fixed === 'right',
disabled: !checkedList.includes(item.value),
},
]"
@click="handleColumnFixed(item, 'right')"
/>
</Tooltip>
</div>
<a-button size="small" type="link" @click="reset">
{{ t('重置') }}
</a-button>
</div>
</template>
</CheckboxGroup>
</ScrollContainer>
</template>
<SettingOutlined />
</Popover>
</Tooltip>
<template #content>
<ScrollContainer>
<CheckboxGroup v-model:value="checkedList" @change="onChange" ref="columnListRef">
<template v-for="item in plainOptions" :key="item.value">
<div :class="`${prefixCls}__check-item`" v-if="!('ifShow' in item && !item.ifShow)">
<DragOutlined class="table-column-drag-icon" />
<Checkbox :value="item.value">
{{ item.label }}
</Checkbox>
<Tooltip placement="bottomLeft" :mouseLeaveDelay="0.4" :getPopupContainer="getPopupContainer">
<template #title>
{{ t('固定到左侧') }}
</template>
<Icon
icon="line-md:arrow-align-left"
:class="[
`${prefixCls}__fixed-left`,
{
active: item.fixed === 'left',
disabled: !checkedList.includes(item.value)
}
]"
@click="handleColumnFixed(item, 'left')"
/>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottomLeft" :mouseLeaveDelay="0.4" :getPopupContainer="getPopupContainer">
<template #title>
{{ t('固定到右侧') }}
</template>
<Icon
icon="line-md:arrow-align-left"
:class="[
`${prefixCls}__fixed-right`,
{
active: item.fixed === 'right',
disabled: !checkedList.includes(item.value)
}
]"
@click="handleColumnFixed(item, 'right')"
/>
</Tooltip>
</div>
</template>
</CheckboxGroup>
</ScrollContainer>
</template>
<SettingOutlined />
</Popover>
</Tooltip>
</template>
<script lang="ts">
import type { BasicColumn, ColumnChangeParam } from '../../types/table';
import {
defineComponent,
ref,
reactive,
toRefs,
watchEffect,
nextTick,
unref,
computed,
} from 'vue';
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
import { Icon } from '/@/components/Icon';
import { ScrollContainer } from '/@/components/Container';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { useDesign } from '/@/hooks/web/useDesign';
// import { useSortable } from '/@/hooks/web/useSortable';
import { isFunction, isNullAndUnDef } from '/@/utils/is';
import { getPopupContainer as getParentContainer } from '/@/utils';
import { cloneDeep, omit } from 'lodash-es';
import Sortablejs from 'sortablejs';
import type Sortable from 'sortablejs';
import type { BasicColumn, ColumnChangeParam } from '../../types/table';
import { defineComponent, ref, reactive, toRefs, watchEffect, nextTick, unref, computed } from 'vue';
import { Tooltip, Popover, Checkbox, Divider } from 'ant-design-vue';
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
import { Icon } from '/@/components/Icon';
import { ScrollContainer } from '/@/components/Container';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { useDesign } from '/@/hooks/web/useDesign';
// import { useSortable } from '/@/hooks/web/useSortable';
import { isFunction, isNullAndUnDef } from '/@/utils/is';
import { getPopupContainer as getParentContainer } from '/@/utils';
import { cloneDeep, omit } from 'lodash-es';
import Sortablejs from 'sortablejs';
import type Sortable from 'sortablejs';
interface State {
checkAll: boolean;
isInit?: boolean;
checkedList: string[];
defaultCheckList: string[];
}
interface State {
checkAll: boolean;
isInit?: boolean;
checkedList: string[];
defaultCheckList: string[];
}
interface Options {
label: string;
value: string;
fixed?: boolean | 'left' | 'right';
}
interface Options {
label: string;
value: string;
fixed?: boolean | 'left' | 'right';
}
export default defineComponent({
name: 'ColumnSetting',
components: {
SettingOutlined,
Popover,
Tooltip,
Checkbox,
CheckboxGroup: Checkbox.Group,
DragOutlined,
ScrollContainer,
Divider,
Icon,
},
emits: ['columns-change'],
export default defineComponent({
name: 'ColumnSetting',
components: {
SettingOutlined,
Popover,
Tooltip,
Checkbox,
CheckboxGroup: Checkbox.Group,
DragOutlined,
ScrollContainer,
Divider,
Icon
},
emits: ['columns-change'],
setup(_, { emit, attrs }) {
const { t } = useI18n();
const table = useTableContext();
setup(_, { emit, attrs }) {
const { t } = useI18n();
const table = useTableContext();
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
let inited = false;
const defaultRowSelection = omit(table.getRowSelection(), 'selectedRowKeys');
let inited = false;
const cachePlainOptions = ref<Options[]>([]);
const plainOptions = ref<Options[] | any>([]);
const cachePlainOptions = ref<Options[]>([]);
const plainOptions = ref<Options[] | any>([]);
const plainSortOptions = ref<Options[]>([]);
const plainSortOptions = ref<Options[]>([]);
const columnListRef = ref<ComponentRef>(null);
const columnListRef = ref<ComponentRef>(null);
const state = reactive<State>({
checkAll: true,
checkedList: [],
defaultCheckList: [],
});
const state = reactive<State>({
checkAll: true,
checkedList: [],
defaultCheckList: []
});
const checkIndex = ref(false);
const checkSelect = ref(false);
const checkIndex = ref(false);
const checkSelect = ref(false);
const { prefixCls } = useDesign('basic-column-setting');
const { prefixCls } = useDesign('basic-column-setting');
const getValues = computed(() => {
return unref(table?.getBindValues) || {};
});
const getValues = computed(() => {
return unref(table?.getBindValues) || {};
});
watchEffect(() => {
const columns = table.getColumns();
if (columns.length && !state.isInit) {
init();
}
});
watchEffect(() => {
const columns = table.getColumns();
if (columns.length && !state.isInit) {
init();
}
});
watchEffect(() => {
const values = unref(getValues);
checkIndex.value = !!values.showIndexColumn;
checkSelect.value = !!values.rowSelection;
});
watchEffect(() => {
const values = unref(getValues);
checkIndex.value = !!values.showIndexColumn;
checkSelect.value = !!values.rowSelection;
});
function getColumns() {
const ret: Options[] = [];
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
ret.push({
label: (item.title as string) || (item.customTitle as string),
value: (item.dataIndex || item.title) as string,
...item,
});
});
return ret;
}
function init() {
const columns = getColumns();
const checkList = table
.getColumns({ ignoreAction: true })
.map((item) => {
if (item.defaultHidden) {
return '';
function getColumns() {
const ret: Options[] = [];
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
ret.push({
label: (item.title as string) || (item.customTitle as string),
value: (item.dataIndex || item.title) as string,
...item
});
});
return ret;
}
return item.dataIndex || item.title;
})
.filter(Boolean) as string[];
if (!plainOptions.value.length) {
plainOptions.value = columns;
plainSortOptions.value = columns;
cachePlainOptions.value = columns;
state.defaultCheckList = checkList;
} else {
// const fixedColumns = columns.filter((item) =>
// Reflect.has(item, 'fixed')
// ) as BasicColumn[];
function init() {
const columns = getColumns();
unref(plainOptions).forEach((item: BasicColumn) => {
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex);
if (findItem) {
item.fixed = findItem.fixed;
const checkList = table
.getColumns({ ignoreAction: true })
.map((item) => {
if (item.defaultHidden) {
return '';
}
return item.dataIndex || item.title;
})
.filter(Boolean) as string[];
if (!plainOptions.value.length) {
plainOptions.value = columns;
plainSortOptions.value = columns;
cachePlainOptions.value = columns;
state.defaultCheckList = checkList;
} else {
// const fixedColumns = columns.filter((item) =>
// Reflect.has(item, 'fixed')
// ) as BasicColumn[];
unref(plainOptions).forEach((item: BasicColumn) => {
const findItem = columns.find((col: BasicColumn) => col.dataIndex === item.dataIndex);
if (findItem) {
item.fixed = findItem.fixed;
}
});
}
state.isInit = true;
state.checkedList = checkList;
}
});
// checkAll change
function onCheckAllChange(e: ChangeEvent) {
const checkList = plainOptions.value.map((item) => item.value);
if (e.target.checked) {
state.checkedList = checkList;
setColumns(checkList);
} else {
state.checkedList = [];
setColumns([]);
}
}
const indeterminate = computed(() => {
const len = plainOptions.value.length;
let checkedLen = state.checkedList.length;
unref(checkIndex) && checkedLen--;
return checkedLen > 0 && checkedLen < len;
});
// Trigger when check/uncheck a column
function onChange(checkedList: string[]) {
const len = plainSortOptions.value.length;
state.checkAll = checkedList.length === len;
const sortList = unref(plainSortOptions).map((item) => item.value);
checkedList.sort((prev, next) => {
return sortList.indexOf(prev) - sortList.indexOf(next);
});
setColumns(checkedList);
}
let sortable: Sortable;
let sortableOrder: string[] = [];
// reset columns
function reset() {
state.checkedList = [...state.defaultCheckList];
state.checkAll = true;
plainOptions.value = unref(cachePlainOptions);
plainSortOptions.value = unref(cachePlainOptions);
setColumns(table.getCacheColumns());
sortable.sort(sortableOrder);
}
// Open the pop-up window for drag and drop initialization
function handleVisibleChange() {
if (inited) return;
nextTick(() => {
const columnListEl = unref(columnListRef);
if (!columnListEl) return;
const el = columnListEl.$el as any;
if (!el) return;
// Drag and drop sort
sortable = Sortablejs.create(unref(el), {
animation: 500,
delay: 400,
delayOnTouchOnly: true,
handle: '.table-column-drag-icon ',
onEnd: (evt) => {
const { oldIndex, newIndex } = evt;
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
return;
}
// Sort column
const columns = cloneDeep(plainSortOptions.value);
if (oldIndex > newIndex) {
columns.splice(newIndex, 0, columns[oldIndex]);
columns.splice(oldIndex + 1, 1);
} else {
columns.splice(newIndex + 1, 0, columns[oldIndex]);
columns.splice(oldIndex, 1);
}
plainSortOptions.value = columns;
setColumns(columns);
}
});
// 记录原始order 序列
sortableOrder = sortable.toArray();
inited = true;
});
}
// Control whether the serial number column is displayed
function handleIndexCheckChange(e: ChangeEvent) {
table.setProps({
showIndexColumn: e.target.checked
});
}
// Control whether the check box is displayed
function handleSelectCheckChange(e: ChangeEvent) {
table.setProps({
rowSelection: e.target.checked ? defaultRowSelection : undefined
});
}
function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
if (!state.checkedList.includes(item.dataIndex as string)) return;
const columns = getColumns() as BasicColumn[];
const isFixed = item.fixed === fixed ? false : fixed;
const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
if (index !== -1) {
columns[index].fixed = isFixed;
}
item.fixed = isFixed;
if (isFixed && !item.width) {
item.width = 100;
}
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
setColumns(columns);
}
function setColumns(columns: BasicColumn[] | string[]) {
table.setColumns(columns);
const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
const visible = columns.findIndex((c: BasicColumn | string) => c === col.value || (typeof c !== 'string' && c.dataIndex === col.value)) !== -1;
return { dataIndex: col.value, fixed: col.fixed, visible };
});
emit('columns-change', data);
}
function getPopupContainer() {
return isFunction(attrs.getPopupContainer) ? attrs.getPopupContainer() : getParentContainer();
}
return {
t,
...toRefs(state),
indeterminate,
onCheckAllChange,
onChange,
plainOptions,
reset,
prefixCls,
columnListRef,
handleVisibleChange,
checkIndex,
checkSelect,
handleIndexCheckChange,
handleSelectCheckChange,
defaultRowSelection,
handleColumnFixed,
getPopupContainer
};
}
state.isInit = true;
state.checkedList = checkList;
}
// checkAll change
function onCheckAllChange(e: ChangeEvent) {
const checkList = plainOptions.value.map((item) => item.value);
if (e.target.checked) {
state.checkedList = checkList;
setColumns(checkList);
} else {
state.checkedList = [];
setColumns([]);
}
}
const indeterminate = computed(() => {
const len = plainOptions.value.length;
let checkedLen = state.checkedList.length;
unref(checkIndex) && checkedLen--;
return checkedLen > 0 && checkedLen < len;
});
// Trigger when check/uncheck a column
function onChange(checkedList: string[]) {
const len = plainSortOptions.value.length;
state.checkAll = checkedList.length === len;
const sortList = unref(plainSortOptions).map((item) => item.value);
checkedList.sort((prev, next) => {
return sortList.indexOf(prev) - sortList.indexOf(next);
});
setColumns(checkedList);
}
let sortable: Sortable;
let sortableOrder: string[] = [];
// reset columns
function reset() {
state.checkedList = [...state.defaultCheckList];
state.checkAll = true;
plainOptions.value = unref(cachePlainOptions);
plainSortOptions.value = unref(cachePlainOptions);
setColumns(table.getCacheColumns());
sortable.sort(sortableOrder);
}
// Open the pop-up window for drag and drop initialization
function handleVisibleChange() {
if (inited) return;
nextTick(() => {
const columnListEl = unref(columnListRef);
if (!columnListEl) return;
const el = columnListEl.$el as any;
if (!el) return;
// Drag and drop sort
sortable = Sortablejs.create(unref(el), {
animation: 500,
delay: 400,
delayOnTouchOnly: true,
handle: '.table-column-drag-icon ',
onEnd: (evt) => {
const { oldIndex, newIndex } = evt;
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) {
return;
}
// Sort column
const columns = cloneDeep(plainSortOptions.value);
if (oldIndex > newIndex) {
columns.splice(newIndex, 0, columns[oldIndex]);
columns.splice(oldIndex + 1, 1);
} else {
columns.splice(newIndex + 1, 0, columns[oldIndex]);
columns.splice(oldIndex, 1);
}
plainSortOptions.value = columns;
setColumns(columns);
},
});
// 记录原始order 序列
sortableOrder = sortable.toArray();
inited = true;
});
}
// Control whether the serial number column is displayed
function handleIndexCheckChange(e: ChangeEvent) {
table.setProps({
showIndexColumn: e.target.checked,
});
}
// Control whether the check box is displayed
function handleSelectCheckChange(e: ChangeEvent) {
table.setProps({
rowSelection: e.target.checked ? defaultRowSelection : undefined,
});
}
function handleColumnFixed(item: BasicColumn, fixed?: 'left' | 'right') {
if (!state.checkedList.includes(item.dataIndex as string)) return;
const columns = getColumns() as BasicColumn[];
const isFixed = item.fixed === fixed ? false : fixed;
const index = columns.findIndex((col) => col.dataIndex === item.dataIndex);
if (index !== -1) {
columns[index].fixed = isFixed;
}
item.fixed = isFixed;
if (isFixed && !item.width) {
item.width = 100;
}
table.setCacheColumnsByField?.(item.dataIndex as string, { fixed: isFixed });
setColumns(columns);
}
function setColumns(columns: BasicColumn[] | string[]) {
table.setColumns(columns);
const data: ColumnChangeParam[] = unref(plainSortOptions).map((col) => {
const visible =
columns.findIndex(
(c: BasicColumn | string) =>
c === col.value || (typeof c !== 'string' && c.dataIndex === col.value),
) !== -1;
return { dataIndex: col.value, fixed: col.fixed, visible };
});
emit('columns-change', data);
}
function getPopupContainer() {
return isFunction(attrs.getPopupContainer)
? attrs.getPopupContainer()
: getParentContainer();
}
return {
t,
...toRefs(state),
indeterminate,
onCheckAllChange,
onChange,
plainOptions,
reset,
prefixCls,
columnListRef,
handleVisibleChange,
checkIndex,
checkSelect,
handleIndexCheckChange,
handleSelectCheckChange,
defaultRowSelection,
handleColumnFixed,
getPopupContainer,
};
},
});
});
</script>
<style lang="less">
@prefix-cls: ~'@{namespace}-basic-column-setting';
@prefix-cls: ~'@{namespace}-basic-column-setting';
.table-column-drag-icon {
margin: 0 5px;
cursor: move;
}
.@{prefix-cls} {
&__popover-title {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
.table-column-drag-icon {
margin: 0 5px;
cursor: move;
}
&__check-item {
display: flex;
align-items: center;
min-width: 100%;
padding: 4px 16px 8px 0;
.ant-checkbox-wrapper {
width: 100%;
&:hover {
color: @primary-color;
.@{prefix-cls} {
&__popover-title {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
}
&__check-item {
display: flex;
align-items: center;
min-width: 100%;
padding: 4px 16px 8px 0;
.ant-checkbox-wrapper {
width: 100%;
&:hover {
color: @primary-color;
}
}
}
&__fixed-left,
&__fixed-right {
color: rgb(0 0 0 / 45%);
cursor: pointer;
&.active,
&:hover {
color: @primary-color;
}
&.disabled {
color: @disabled-color;
cursor: not-allowed;
}
}
&__fixed-right {
transform: rotate(180deg);
}
&__cloumn-list {
svg {
width: 1em !important;
height: 1em !important;
}
.ant-popover-inner-content {
// max-height: 360px;
padding-right: 0;
padding-left: 0;
// overflow: auto;
}
.ant-checkbox-group {
width: 100%;
min-width: 260px;
// flex-wrap: wrap;
}
.scrollbar {
height: 220px;
}
}
}
}
&__fixed-left,
&__fixed-right {
color: rgb(0 0 0 / 45%);
cursor: pointer;
&.active,
&:hover {
color: @primary-color;
}
&.disabled {
color: @disabled-color;
cursor: not-allowed;
}
}
&__fixed-right {
transform: rotate(180deg);
}
&__cloumn-list {
svg {
width: 1em !important;
height: 1em !important;
}
.ant-popover-inner-content {
// max-height: 360px;
padding-right: 0;
padding-left: 0;
// overflow: auto;
}
.ant-checkbox-group {
width: 100%;
min-width: 260px;
// flex-wrap: wrap;
}
.scrollbar {
height: 220px;
}
}
}
</style>

View File

@ -1,38 +1,38 @@
<template>
<Tooltip placement="top">
<template #title>
<span>{{ t('component.table.settingFullScreen') }}</span>
</template>
<FullscreenOutlined @click="toggle" v-if="!isFullscreen" />
<FullscreenExitOutlined @click="toggle" v-else />
</Tooltip>
<Tooltip placement="top">
<template #title>
<span>{{ t('component.table.settingFullScreen') }}</span>
</template>
<FullscreenOutlined @click="toggle" v-if="!isFullscreen" />
<FullscreenExitOutlined @click="toggle" v-else />
</Tooltip>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons-vue';
import { useFullscreen } from '@vueuse/core';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { defineComponent } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons-vue';
import { useFullscreen } from '@vueuse/core';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({
name: 'FullScreenSetting',
components: {
FullscreenExitOutlined,
FullscreenOutlined,
Tooltip,
},
export default defineComponent({
name: 'FullScreenSetting',
components: {
FullscreenExitOutlined,
FullscreenOutlined,
Tooltip
},
setup() {
const table = useTableContext();
const { t } = useI18n();
const { toggle, isFullscreen } = useFullscreen(table.wrapRef);
setup() {
const table = useTableContext();
const { t } = useI18n();
const { toggle, isFullscreen } = useFullscreen(table.wrapRef);
return {
toggle,
isFullscreen,
t,
};
},
});
return {
toggle,
isFullscreen,
t
};
}
});
</script>

View File

@ -1,33 +1,33 @@
<template>
<Tooltip placement="top">
<template #title>
<span>{{ t('刷新') }}</span>
</template>
<RedoOutlined @click="redo" />
</Tooltip>
<Tooltip placement="top">
<template #title>
<span>{{ t('刷新') }}</span>
</template>
<RedoOutlined @click="redo" />
</Tooltip>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { RedoOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { defineComponent } from 'vue';
import { Tooltip } from 'ant-design-vue';
import { RedoOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({
name: 'RedoSetting',
components: {
RedoOutlined,
Tooltip,
},
setup() {
const table = useTableContext();
const { t } = useI18n();
export default defineComponent({
name: 'RedoSetting',
components: {
RedoOutlined,
Tooltip
},
setup() {
const table = useTableContext();
const { t } = useI18n();
function redo() {
table.reload();
}
function redo() {
table.reload();
}
return { redo, t };
},
});
return { redo, t };
}
});
</script>

View File

@ -1,64 +1,64 @@
<template>
<Tooltip placement="top">
<template #title>
<span>{{ t('密度') }}</span>
</template>
<Tooltip placement="top">
<template #title>
<span>{{ t('密度') }}</span>
</template>
<Dropdown placement="bottom" :trigger="['click']" :getPopupContainer="getPopupContainer">
<ColumnHeightOutlined />
<template #overlay>
<Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef">
<MenuItem key="default">
<span>{{ t('默认') }}</span>
</MenuItem>
<MenuItem key="middle">
<span>{{ t('中等') }}</span>
</MenuItem>
<MenuItem key="small">
<span>{{ t('紧凑') }}</span>
</MenuItem>
</Menu>
</template>
</Dropdown>
</Tooltip>
<Dropdown placement="bottom" :trigger="['click']" :getPopupContainer="getPopupContainer">
<ColumnHeightOutlined />
<template #overlay>
<Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef">
<MenuItem key="default">
<span>{{ t('默认') }}</span>
</MenuItem>
<MenuItem key="middle">
<span>{{ t('中等') }}</span>
</MenuItem>
<MenuItem key="small">
<span>{{ t('紧凑') }}</span>
</MenuItem>
</Menu>
</template>
</Dropdown>
</Tooltip>
</template>
<script lang="ts">
import type { SizeType } from '../../types/table';
import { defineComponent, ref } from 'vue';
import { Tooltip, Dropdown, Menu } from 'ant-design-vue';
import { ColumnHeightOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { getPopupContainer } from '/@/utils';
import type { SizeType } from '../../types/table';
import { defineComponent, ref } from 'vue';
import { Tooltip, Dropdown, Menu } from 'ant-design-vue';
import { ColumnHeightOutlined } from '@ant-design/icons-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import { getPopupContainer } from '/@/utils';
export default defineComponent({
name: 'SizeSetting',
components: {
ColumnHeightOutlined,
Tooltip,
Dropdown,
Menu,
MenuItem: Menu.Item,
},
setup() {
const table = useTableContext();
const { t } = useI18n();
export default defineComponent({
name: 'SizeSetting',
components: {
ColumnHeightOutlined,
Tooltip,
Dropdown,
Menu,
MenuItem: Menu.Item
},
setup() {
const table = useTableContext();
const { t } = useI18n();
const selectedKeysRef = ref<SizeType[]>([table.getSize()]);
const selectedKeysRef = ref<SizeType[]>([table.getSize()]);
function handleTitleClick({ key }: { key: SizeType }) {
selectedKeysRef.value = [key];
table.setProps({
size: key,
});
}
function handleTitleClick({ key }: { key: SizeType }) {
selectedKeysRef.value = [key];
table.setProps({
size: key
});
}
return {
handleTitleClick,
selectedKeysRef,
getPopupContainer,
t,
};
},
});
return {
handleTitleClick,
selectedKeysRef,
getPopupContainer,
t
};
}
});
</script>

View File

@ -1,76 +1,72 @@
<template>
<div class="table-settings">
<RedoSetting v-if="getSetting.redo" :getPopupContainer="getTableContainer" />
<SizeSetting v-if="getSetting.size" :getPopupContainer="getTableContainer" />
<ColumnSetting
v-if="getSetting.setting"
@columns-change="handleColumnChange"
:getPopupContainer="getTableContainer"
/>
<FullScreenSetting v-if="getSetting.fullScreen" :getPopupContainer="getTableContainer" />
</div>
<div class="table-settings">
<RedoSetting v-if="getSetting.redo" :getPopupContainer="getTableContainer" />
<SizeSetting v-if="getSetting.size" :getPopupContainer="getTableContainer" />
<ColumnSetting v-if="getSetting.setting" @columns-change="handleColumnChange" :getPopupContainer="getTableContainer" />
<FullScreenSetting v-if="getSetting.fullScreen" :getPopupContainer="getTableContainer" />
</div>
</template>
<script lang="ts">
import type { PropType } from 'vue';
import type { TableSetting, ColumnChangeParam } from '../../types/table';
import { defineComponent, computed, unref } from 'vue';
import ColumnSetting from './ColumnSetting.vue';
import SizeSetting from './SizeSetting.vue';
import RedoSetting from './RedoSetting.vue';
import FullScreenSetting from './FullScreenSetting.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
import type { PropType } from 'vue';
import type { TableSetting, ColumnChangeParam } from '../../types/table';
import { defineComponent, computed, unref } from 'vue';
import ColumnSetting from './ColumnSetting.vue';
import SizeSetting from './SizeSetting.vue';
import RedoSetting from './RedoSetting.vue';
import FullScreenSetting from './FullScreenSetting.vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTableContext } from '../../hooks/useTableContext';
export default defineComponent({
name: 'TableSetting',
components: {
ColumnSetting,
SizeSetting,
RedoSetting,
FullScreenSetting,
},
props: {
setting: {
type: Object as PropType<TableSetting>,
default: () => ({}),
},
},
emits: ['columns-change'],
setup(props, { emit }) {
const { t } = useI18n();
const table = useTableContext();
export default defineComponent({
name: 'TableSetting',
components: {
ColumnSetting,
SizeSetting,
RedoSetting,
FullScreenSetting
},
props: {
setting: {
type: Object as PropType<TableSetting>,
default: () => ({})
}
},
emits: ['columns-change'],
setup(props, { emit }) {
const { t } = useI18n();
const table = useTableContext();
const getSetting = computed((): TableSetting => {
return {
redo: true,
size: true,
setting: true,
fullScreen: false,
...props.setting,
};
});
const getSetting = computed((): TableSetting => {
return {
redo: true,
size: true,
setting: true,
fullScreen: false,
...props.setting
};
});
function handleColumnChange(data: ColumnChangeParam[]) {
emit('columns-change', data);
}
function handleColumnChange(data: ColumnChangeParam[]) {
emit('columns-change', data);
}
function getTableContainer() {
return table ? unref(table.wrapRef) : document.body;
}
function getTableContainer() {
return table ? unref(table.wrapRef) : document.body;
}
return { getSetting, t, handleColumnChange, getTableContainer };
},
});
return { getSetting, t, handleColumnChange, getTableContainer };
}
});
</script>
<style lang="less">
.table-settings {
& > * {
margin-right: 12px;
}
.table-settings {
& > * {
margin-right: 12px;
}
svg {
width: 1.3em;
height: 1.3em;
svg {
width: 1.3em;
height: 1.3em;
}
}
}
</style>

View File

@ -7,79 +7,79 @@ import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
import { useI18n } from '/@/hooks/web/useI18n';
interface ItemRender {
page: number;
type: 'page' | 'prev' | 'next';
originalElement: any;
page: number;
type: 'page' | 'prev' | 'next';
originalElement: any;
}
function itemRender({ page, type, originalElement }: ItemRender) {
if (type === 'prev') {
return page === 0 ? null : <LeftOutlined />;
} else if (type === 'next') {
return page === 1 ? null : <RightOutlined />;
}
return originalElement;
if (type === 'prev') {
return page === 0 ? null : <LeftOutlined />;
} else if (type === 'next') {
return page === 1 ? null : <RightOutlined />;
}
return originalElement;
}
export function usePagination(refProps: ComputedRef<BasicTableProps>) {
const { t } = useI18n();
const { t } = useI18n();
const configRef = ref<PaginationProps>({});
const show = ref(true);
const configRef = ref<PaginationProps>({});
const show = ref(true);
watch(
() => unref(refProps).pagination,
(pagination) => {
if (!isBoolean(pagination) && pagination) {
configRef.value = {
...unref(configRef),
...(pagination ?? {}),
watch(
() => unref(refProps).pagination,
(pagination) => {
if (!isBoolean(pagination) && pagination) {
configRef.value = {
...unref(configRef),
...(pagination ?? {})
};
}
}
);
const getPaginationInfo = computed((): PaginationProps | boolean => {
const { pagination } = unref(refProps);
if (!unref(show) || (isBoolean(pagination) && !pagination)) {
return false;
}
return {
current: 1,
pageSize: PAGE_SIZE,
size: 'small',
defaultPageSize: PAGE_SIZE,
showTotal: (total) => t('共 {total} 条数据', { total }),
showSizeChanger: true,
pageSizeOptions: PAGE_SIZE_OPTIONS,
itemRender: itemRender,
showQuickJumper: true,
...(isBoolean(pagination) ? {} : pagination),
...unref(configRef)
};
}
},
);
});
const getPaginationInfo = computed((): PaginationProps | boolean => {
const { pagination } = unref(refProps);
if (!unref(show) || (isBoolean(pagination) && !pagination)) {
return false;
function setPagination(info: Partial<PaginationProps>) {
const paginationInfo = unref(getPaginationInfo);
configRef.value = {
...(!isBoolean(paginationInfo) ? paginationInfo : {}),
...info
};
}
return {
current: 1,
pageSize: PAGE_SIZE,
size: 'small',
defaultPageSize: PAGE_SIZE,
showTotal: (total) => t('共 {total} 条数据', { total }),
showSizeChanger: true,
pageSizeOptions: PAGE_SIZE_OPTIONS,
itemRender: itemRender,
showQuickJumper: true,
...(isBoolean(pagination) ? {} : pagination),
...unref(configRef),
};
});
function getPagination() {
return unref(getPaginationInfo);
}
function setPagination(info: Partial<PaginationProps>) {
const paginationInfo = unref(getPaginationInfo);
configRef.value = {
...(!isBoolean(paginationInfo) ? paginationInfo : {}),
...info,
};
}
function getShowPagination() {
return unref(show);
}
function getPagination() {
return unref(getPaginationInfo);
}
async function setShowPagination(flag: boolean) {
show.value = flag;
}
function getShowPagination() {
return unref(show);
}
async function setShowPagination(flag: boolean) {
show.value = flag;
}
return { getPagination, getPaginationInfo, setShowPagination, getShowPagination, setPagination };
return { getPagination, getPaginationInfo, setShowPagination, getShowPagination, setPagination };
}