提交 8b3a4d37 编写于 作者: V vben

feat: add table setting

上级 7a1e94c4
......@@ -74,7 +74,7 @@
const getMergePropsRef = computed(
(): FormProps => {
return deepMerge(toRaw(props), unref(propsRef));
return deepMerge(props, unref(propsRef));
}
);
// 获取表单基本配置
......
<template>
<div
ref="wrapRef"
class="basic-table"
:class="{
'table-form-container': getBindValues.useSearchForm,
......@@ -33,7 +34,7 @@
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, unref, watch, nextTick } from 'vue';
import { defineComponent, ref, computed, unref, watch, nextTick, toRaw } from 'vue';
import { Table } from 'ant-design-vue';
import { basicProps } from './props';
import type {
......@@ -41,7 +42,9 @@
FetchParams,
GetColumnsParams,
TableActionType,
SizeType,
} from './types/table';
import { isFunction, isString } from '/@/utils/is';
import renderTitle from './components/renderTitle';
......@@ -57,18 +60,20 @@
import { provideTable } from './hooks/useProvinceTable';
import { BasicForm, FormProps, useForm } from '/@/components/Form/index';
import { omit } from 'lodash-es';
import './style/index.less';
import { ROW_KEY } from './const';
import { PaginationProps } from './types/pagination';
import { deepMerge } from '/@/utils';
import { TableCustomRecord } from 'ant-design-vue/types/table/table';
import { useEvent } from '/@/hooks/event/useEvent';
import './style/index.less';
export default defineComponent({
props: basicProps,
components: { Table, BasicForm },
emits: ['fetch-success', 'fetch-error', 'selection-change', 'register'],
setup(props, { attrs, emit, slots }) {
const tableElRef = ref<any>(null);
const wrapRef = ref<Nullable<HTMLDivElement>>(null);
const innerPropsRef = ref<Partial<BasicTableProps>>();
const [registerForm, { getFieldsValue }] = useForm();
......@@ -93,6 +98,7 @@
},
emit
);
const { getScrollRef, redoHeight } = useTableScroll(getMergeProps, tableElRef);
const {
getRowSelectionRef,
......@@ -108,16 +114,26 @@
return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
});
const getBindValues = computed(() => {
const { title, titleHelpMessage, showSummary } = unref(getMergeProps);
const { title, titleHelpMessage, showSummary, showTableSetting, tableSetting } = unref(
getMergeProps
);
const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting;
const titleData: any =
!slots.tableTitle && !isString(title) && !title && !slots.toolbar
hideTitle && !isString(title)
? {}
: {
title:
!slots.tableTitle && !title && !slots.toolbar
? null
: renderTitle.bind(null, title, titleHelpMessage, slots),
title: hideTitle
? null
: renderTitle.bind(
null,
title,
titleHelpMessage,
slots,
showTableSetting,
tableSetting
),
};
const pagination = unref(getPaginationRef);
const rowSelection = unref(getRowSelectionRef);
......@@ -155,6 +171,7 @@
}
return propsData;
});
const getFormProps = computed(() => {
const { formConfig } = unref(getBindValues);
const formProps: FormProps = {
......@@ -198,6 +215,7 @@
setPagination(pagination);
fetch();
}
watch(
() => unref(getDataSourceRef),
() => {
......@@ -230,6 +248,10 @@
{ immediate: true }
);
function setProps(props: Partial<BasicTableProps>) {
innerPropsRef.value = deepMerge(unref(innerPropsRef) || {}, props);
}
const tableAction: TableActionType = {
reload: async (opt?: FetchParams) => {
await fetch(opt);
......@@ -247,11 +269,14 @@
return unref(getPaginationRef);
},
getColumns: (opt?: GetColumnsParams) => {
const { ignoreIndex } = opt || {};
let columns = unref(getColumnsRef);
const { ignoreIndex, ignoreAction } = opt || {};
let columns = toRaw(unref(getColumnsRef));
if (ignoreIndex) {
columns = columns.filter((item) => item.flag !== 'INDEX');
}
if (ignoreAction) {
columns = columns.filter((item) => item.flag !== 'ACTION');
}
return columns;
},
getDataSource: () => {
......@@ -260,12 +285,16 @@
setLoading: (loading: boolean) => {
loadingRef.value = loading;
},
setProps: (props: Partial<BasicTableProps>) => {
innerPropsRef.value = deepMerge(unref(innerPropsRef) || {}, props);
setProps,
getSize: (): SizeType => {
return unref(getBindValues).size;
},
};
provideTable(tableAction);
provideTable({
...tableAction,
wrapRef,
});
emit('register', tableAction);
return {
......@@ -278,6 +307,7 @@
getEmptyDataIsShowTable,
handleTableChange,
getRowClassName,
wrapRef,
...tableAction,
};
},
......
<template>
<div class="table-settings">
<Divider type="vertical" />
<Tooltip placement="top" v-if="getSetting.redo">
<template #title>
<span>刷新</span>
</template>
<RedoOutlined @click="redo" />
</Tooltip>
<Tooltip placement="top" v-if="getSetting.size">
<template #title>
<span>密度</span>
</template>
<Dropdown placement="bottomCenter" :trigger="['click']">
<ColumnHeightOutlined />
<template #overlay>
<Menu @click="handleTitleClick" selectable v-model:selectedKeys="selectedKeysRef">
<MenuItem key="default">
<span>默认</span>
</MenuItem>
<MenuItem key="middle">
<span>中等</span>
</MenuItem>
<MenuItem key="small">
<span>紧凑</span>
</MenuItem>
</Menu>
</template>
</Dropdown>
</Tooltip>
<Tooltip placement="top" v-if="getSetting.setting">
<template #title>
<span>列设置</span>
</template>
<Popover
placement="bottomLeft"
trigger="click"
overlayClassName="table-settings__cloumn-list"
>
<template #content>
<CheckboxGroup v-model:value="checkedList" @change="onChange">
<template v-for="item in plainOptions" :key="item.value">
<div class="table-settings__check-item">
<Checkbox :value="item.value">
{{ item.label }}
</Checkbox>
</div>
</template>
</CheckboxGroup>
</template>
<template #title>
<div class="table-settings__popover-title">
<Checkbox
:indeterminate="indeterminate"
v-model:checked="checkAll"
@change="onCheckAllChange"
>
列展示
</Checkbox>
<a-button size="small" type="link" @click="reset">重置</a-button>
</div>
</template>
<SettingOutlined />
</Popover>
</Tooltip>
<Tooltip placement="top" v-if="getSetting.fullScreen">
<template #title>
<span>全屏</span>
</template>
<FullscreenOutlined @click="handleFullScreen" v-if="!isFullscreenRef" />
<FullscreenExitOutlined @click="handleFullScreen" v-else />
</Tooltip>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs, PropType, computed } from 'vue';
import { injectTable } from '../hooks/useProvinceTable';
import { Tooltip, Divider, Dropdown, Menu, Popover, Checkbox } from 'ant-design-vue';
import {
RedoOutlined,
ColumnHeightOutlined,
FullscreenOutlined,
FullscreenExitOutlined,
SettingOutlined,
} from '@ant-design/icons-vue';
import { useFullscreen } from '/@/hooks/web/useFullScreen';
import type { SizeType, TableSetting } from '../types/table';
interface Options {
label: string;
value: string;
}
interface State {
indeterminate: boolean;
checkAll: boolean;
// defaultColumns: BasicColumn[];
// columns: BasicColumn[];
checkedList: string[];
defaultCheckList: string[];
}
export default defineComponent({
name: 'TableSetting',
components: {
RedoOutlined,
ColumnHeightOutlined,
FullscreenExitOutlined,
FullscreenOutlined,
SettingOutlined,
Popover,
Tooltip,
Divider,
Dropdown,
Checkbox,
CheckboxGroup: Checkbox.Group,
Menu,
MenuItem: Menu.Item,
},
props: {
setting: {
type: Object as PropType<TableSetting>,
default: {},
},
},
setup(props) {
const table = injectTable();
const { toggleFullscreen, isFullscreenRef } = useFullscreen(table.wrapRef);
const selectedKeysRef = ref<SizeType[]>([table.getSize()]);
let plainOptions: Options[] = [];
const state = reactive<State>({
indeterminate: false,
checkAll: true,
checkedList: [],
defaultCheckList: [],
});
function init() {
let ret: Options[] = [];
table.getColumns({ ignoreIndex: true, ignoreAction: true }).forEach((item) => {
ret.push({
label: item.title as string,
value: (item.dataIndex || item.title) as string,
});
});
plainOptions = ret;
const checkList = table
.getColumns()
.map((item) => item.dataIndex || item.title) as string[];
state.checkedList = checkList;
state.defaultCheckList = checkList;
}
function handleTitleClick({ key }: { key: SizeType }) {
selectedKeysRef.value = [key];
table.setProps({
size: key,
});
}
function handleFullScreen() {
toggleFullscreen();
}
function onCheckAllChange(e: ChangeEvent) {
state.indeterminate = false;
const checkList = plainOptions.map((item) => item.value);
if (e.target.checked) {
state.checkedList = checkList;
table.setColumns(checkList);
} else {
state.checkedList = [];
table.setColumns([]);
}
}
function onChange(checkedList: string[]) {
state.indeterminate = !!checkedList.length && checkedList.length < plainOptions.length;
state.checkAll = checkedList.length === plainOptions.length;
table.setColumns(checkedList);
}
function reset() {
if (state.checkAll) return;
state.checkedList = [...state.defaultCheckList];
state.checkAll = true;
state.indeterminate = false;
table.setColumns(state.defaultCheckList);
}
const getSetting = computed(
(): TableSetting => {
return {
redo: true,
size: true,
setting: true,
fullScreen: true,
...props.setting,
};
}
);
init();
return {
redo: () => table.reload(),
handleTitleClick,
selectedKeysRef,
handleFullScreen,
isFullscreenRef,
onCheckAllChange,
onChange,
plainOptions,
reset,
getSetting,
...toRefs(state),
};
},
});
</script>
<style lang="less">
@import (reference) '../../../../design/index.less';
.table-settings {
& > * {
margin-right: 12px;
}
svg {
width: 1.2em;
height: 1.2em;
}
&__popover-title {
display: flex;
align-items: center;
justify-content: space-between;
}
&__check-item {
width: 100%;
padding: 4px 16px 4px 16px;
.ant-checkbox-wrapper {
width: 100%;
}
&:hover {
background: fade(@primary-color, 10%);
}
}
&__cloumn-list {
.ant-popover-inner-content {
max-height: 360px;
padding-right: 0;
padding-left: 0;
overflow: auto;
}
.ant-checkbox-group {
width: 100%;
}
}
}
</style>
import { Slots } from 'vue';
import TableTitle from './TableTitle.vue';
import { getSlot } from '/@/utils/helper/tsxHelper';
export default (title: any, titleHelpMessage: string | string[], slots: Slots) => {
import TableSettingComp from './TableSetting.vue';
import type { TableSetting } from '../types/table';
export default (
title: any,
titleHelpMessage: string | string[],
slots: Slots,
showTableSetting: boolean,
tableSetting: TableSetting
) => {
return (
<>
{getSlot(slots, 'tableTitle') ||
(title && <TableTitle helpMessage={titleHelpMessage} title={title} />) || (
<span>&nbsp;</span>
)}
{slots.toolbar && <div class="basic-table-toolbar">{getSlot(slots, 'toolbar')}</div>}
{
<div class="basic-table-toolbar">
{slots.toolbar && getSlot(slots, 'toolbar')}
{showTableSetting && <TableSettingComp setting={tableSetting} />}
</div>
}
</>
);
};
......@@ -81,28 +81,19 @@ export function useColumns(
}
if (actionColumn) {
const hasIndex = columns.findIndex((column) => column.flag === 'ACTION');
if (hasIndex === -1) {
columns.push({
fixed: 'right',
...actionColumn,
flag: 'ACTION',
});
} else {
columns[hasIndex] = {
...columns[hasIndex],
fixed: 'right',
...actionColumn,
flag: 'ACTION',
};
}
columns.push({
...(hasIndex === -1 ? columns[hasIndex] : {}),
fixed: 'right',
...actionColumn,
flag: 'ACTION',
});
}
return columns;
});
function setColumns(columns: BasicColumn[] | string[]) {
if (!isArray(columns)) {
return;
}
if (!isArray(columns)) return;
if (columns.length <= 0) {
columnsRef.value = [];
return;
......
......@@ -131,6 +131,7 @@ export function useDataSource(
});
} finally {
loadingRef.value = false;
// setSearchFormLoading(false);
}
}
......
import type { Ref } from 'vue';
import { provide, inject } from 'vue';
import { TableActionType } from '../types/table';
const key = Symbol('table');
export function provideTable(instance: TableActionType) {
type Instance = TableActionType & { wrapRef: Ref<Nullable<HTMLElement>> };
export function provideTable(instance: Instance) {
provide(key, instance);
}
export function injectTable(): TableActionType {
return inject(key) as TableActionType;
export function injectTable(): Instance {
return inject(key) as Instance;
}
......@@ -82,6 +82,9 @@ export function useTable(
getPaginationRef: () => {
return getTableInstance().getPaginationRef();
},
getSize: () => {
return getTableInstance().getSize();
},
} as TableActionType;
return [register, methods];
......
import { PropType } from 'vue';
import { PaginationProps } from './types/pagination';
import { BasicColumn, FetchSetting } from './types/table';
import { TableCustomRecord, TableRowSelection } from 'ant-design-vue/types/table/table';
import { FormProps } from '/@/components/Form/index';
import type { PropType } from 'vue';
import type { PaginationProps } from './types/pagination';
import type { BasicColumn, FetchSetting, TableSetting } from './types/table';
import type { TableCustomRecord, TableRowSelection } from 'ant-design-vue/types/table/table';
import type { FormProps } from '/@/components/Form/index';
import { FETCH_SETTING } from './const';
// 注释看 types/table
export const basicProps = {
tableSetting: {
type: Object as PropType<TableSetting>,
},
showTableSetting: {
type: Boolean as PropType<boolean>,
default: false,
},
autoCreateKey: {
type: Boolean as PropType<boolean>,
default: true,
......
......@@ -27,6 +27,9 @@
}
&-toolbar {
display: flex;
align-items: center;
> * {
margin-right: 10px;
}
......@@ -132,10 +135,10 @@
border-right: 1px solid @border-color !important;
}
.ant-table-thead > tr > th,
.ant-table-tbody > tr > td {
padding: 9px 8px !important;
}
// .ant-table-thead > tr > th,
// .ant-table-tbody > tr > td {
// padding: 9px 8px !important;
// }
.ant-pagination {
margin: 10px 0 0 0;
......
......@@ -32,7 +32,11 @@ export interface FetchParams {
export interface GetColumnsParams {
ignoreIndex?: boolean;
ignoreAction?: boolean;
}
export type SizeType = 'default' | 'middle' | 'small' | 'large';
export interface TableActionType {
reload: (opt?: FetchParams) => Promise<void>;
getSelectRows: () => any[];
......@@ -41,7 +45,7 @@ export interface TableActionType {
deleteSelectRowByKey: (key: string) => void;
setPagination: (info: Partial<PaginationProps>) => void;
setTableData: (values: any[]) => void;
getColumns: ({ ignoreIndex }?: GetColumnsParams) => BasicColumn[];
getColumns: (opt?: GetColumnsParams) => BasicColumn[];
setColumns: (columns: BasicColumn[] | string[]) => void;
getDataSource: () => any[];
setLoading: (loading: boolean) => void;
......@@ -49,6 +53,7 @@ export interface TableActionType {
redoHeight: () => void;
setSelectedRowKeys: (rowKeys: string[] | number[]) => void;
getPaginationRef: () => PaginationProps | boolean;
getSize: () => SizeType;
}
export interface FetchSetting {
......@@ -61,7 +66,18 @@ export interface FetchSetting {
// 请求结果总数字段 支持 a.b.c
totalField: string;
}
export interface TableSetting {
redo?: boolean;
size?: boolean;
setting?: boolean;
fullScreen?: boolean;
}
export interface BasicTableProps<T = any> {
// 显示表格设置
showTableSetting?: boolean;
tableSetting?: TableSetting;
// 斑马纹
striped?: boolean;
// 是否自动生成key
......@@ -234,7 +250,7 @@ export interface BasicTableProps<T = any> {
* @default 'default'
* @type string
*/
size?: 'default' | 'middle' | 'small' | 'large';
size?: SizeType;
/**
* Table title renderer
......
......@@ -42,15 +42,17 @@ export function useFullscreen(
RFC_METHOD_NAME = 'mozRequestFullScreen';
EFS_METHOD_NAME = 'mozCancelFullScreen';
FSE_PROP_NAME = 'mozFullScreenElement';
ON_FSC_PROP_NAME = 'onmozfullscreenchange';
// ON_FSC_PROP_NAME = 'onmozfullscreenchange';
} else if (!('requestFullscreen' in DOC_EL)) {
throw new Error('当前浏览器不支持Fullscreen API !');
}
function enterFullscreen(): Promise<void> {
isFullscreenRef.value = true;
return (target.value as any)[RFC_METHOD_NAME](options);
}
function exitFullscreen(): Promise<void> {
isFullscreenRef.value = false;
return (document as any)[EFS_METHOD_NAME]();
}
......@@ -89,6 +91,7 @@ export function useFullscreen(
watchFullscreen((isFull: boolean) => {
isFullscreenRef.value = isFull;
});
return {
watchFullscreen,
toggleFullscreen,
......
......@@ -65,7 +65,7 @@ export default defineComponent({
return () => (
<>
<Breadcrumb class="layout-breadcrumb ">
<Breadcrumb class="layout-breadcrumb">
{() => (
<>
<TransitionGroup name="breadcrumb">
......
......@@ -8,16 +8,16 @@ const NAME = 'menu';
hotModuleUnregisterModule(NAME);
@Module({ namespaced: true, name: NAME, dynamic: true, store })
class Menu extends VuexModule {
// 默认展开
private collapsedState: boolean = appStore.getProjectConfig.menuSetting.collapsed;
// // 默认展开
// private collapsedState: boolean = appStore.getProjectConfig.menuSetting.collapsed;
// 菜单宽度
private menuWidthState: number = appStore.getProjectConfig.menuSetting.menuWidth;
// // 菜单宽度
// private menuWidthState: number = appStore.getProjectConfig.menuSetting.menuWidth;
// 是否开始拖拽
private dragStartState: boolean = false;
private dragStartState = false;
private currentTopSplitMenuPathState: string = '';
private currentTopSplitMenuPathState = '';
/**
* @description: 获取窗口名称
......@@ -51,7 +51,7 @@ class Menu extends VuexModule {
// 改变菜单展开状态
@Mutation
commitCollapsedState(collapsed: boolean): void {
this.collapsedState = collapsed;
// this.collapsedState = collapsed;
appStore.commitProjectConfigState({
menuSetting: {
collapsed: collapsed,
......@@ -61,7 +61,7 @@ class Menu extends VuexModule {
@Mutation
commitMenuWidthState(menuWidth: number): void {
this.menuWidthState = menuWidth;
// this.menuWidthState = menuWidth;
appStore.commitProjectConfigState({
menuSetting: {
menuWidth: menuWidth,
......
......@@ -17,6 +17,7 @@
columns: getBasicColumns(),
useSearchForm: true,
formConfig: getFormConfig(),
showTableSetting: true,
});
return {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册