提交 faf3f460 编写于 作者: 陈文彬

feat(table): add table component

上级 5b0a21ec
......@@ -30,5 +30,5 @@ categories:
- 'workflow'
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
template: |
# Changes
# What's Changed
$CHANGES
......@@ -7,20 +7,18 @@
**中文**
该分支为2.0新分支,使用vue3进行开发。
该分支为 2.0 新分支,使用 vue3 进行开发。
1.0分支请切换到`master`分支。1.0采用`vue2.6`+`vue-composition-api`+`vue-cli`开发
1.0 分支请切换到`master`分支。1.0 采用`vue2.6`+`vue-composition-api`+`vue-cli`开发
一个适合开发中大型项目的基础框架,需要对`vue`,`typescript`有一定的了解,也可以作为了解新写法的一个例子来看,提前适应后续新版本的开发方式
项目基于`ant-design-vue`,`typescript`,`vue3.0`,`vite`,`tailwindcss`,`tsx`实现的 vue3 风格的后台管理系统,
### gitHub 地址
[vue-vben-admin2.0](https://github.com/anncwb/vue-vben-admin)
<p align="center">
<img alt="VbenAdmin Logo" width="100%" src="./build/docs/imgs/preview1.png">
<img alt="VbenAdmin Logo" width="100%" src="./build/docs/imgs/preview2.png">
......@@ -29,11 +27,7 @@
### 文档
2.0文档还没开始写。后续补上。。
2.0 文档还没开始写。后续补上。。
## 使用到的技术
......@@ -84,7 +78,6 @@ VSCode 插件
- `stylelint`: 样式代码检查
- `Prettier - Code formatter`:代码格式化
## 安装
```js
......@@ -122,7 +115,6 @@ yarn build:no-cache # 打包 不会使用hardSource进行打包
yarn report # 生成构建包表表预览
```
### 格式化
```bash
......@@ -159,7 +151,6 @@ yarn log # 生成CHANGELOG
- `mod` 不确定分类的修改
- `wip` 删除文件
## 代码贡献
1. Fork 代码!
......@@ -205,20 +196,19 @@ yarn log # 生成CHANGELOG
- [x] 树组件
- [x] 系统性能优化
- [x] 兼容最新`vuex`,`vue-router`
- [] 图片预览组件
- [x] 图片预览组件
- [ ] 表格组件
- [ ] 可编辑表格
- [ ] 图表库
- [ ] 数字动画
- [ ] 主题配置
- [ ] 表格组件
- [ ] 富文本组件
- [ ] 首屏加载等待动画
- [ ] 上传组件
- [ ] 可编辑表格
- [ ] 数据导入导出
- [ ] 搭建`vite`版本
- [ ] 懒加载组件
- [ ] 黑暗主题
- [ ] 更多组件/功能/建议/bug/欢迎提交 pr 或者 issue
更多组件/功能/建议/bug/欢迎提交 pr 或者 issue
## 加入我们
......
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
import userMock from './sys/user';
import menuMock from './sys/menu';
import tableDemoMock from './demo/table-demo';
export function setupProdMockServer() {
createProdMockServer([...userMock, ...menuMock]);
createProdMockServer([...userMock, ...menuMock, ...tableDemoMock]);
}
......@@ -9,12 +9,19 @@ export function resultSuccess<T = any>(result: T, { message = 'ok' } = {}) {
};
}
export function resultPageSuccess<T = any>(items: T[], total: number, { message = 'ok' } = {}) {
export function resultPageSuccess<T = any>(
page: number,
pageSize: number,
list: T[],
{ message = 'ok' } = {}
) {
const pageData = pagination(page, pageSize, list);
return {
code: 0,
result: {
items,
total,
items: pageData,
total: list.length,
},
message,
type: 'success',
......
import { MockMethod } from 'vite-plugin-mock';
import { resultPageSuccess } from '../_util';
const demoList = (() => {
const result: any[] = [];
for (let index = 0; index < 60; index++) {
result.push({
id: `${index}`,
beginTime: '@datetime',
endTime: '@datetime',
address: '@city()',
name: '@cname()',
'no|100000-10000000': 100000,
'status|1': ['正常', '启用', '停用'],
});
}
return result;
})();
export default [
{
url: '/api/table/getDemoList',
timeout: 1000,
method: 'get',
response: ({ query }) => {
const { page = 1, pageSize = 20 } = query;
return resultPageSuccess(page, pageSize, demoList);
},
},
] as MockMethod[];
<template>
<ConfigProvider
:locale="zhCN"
:renderEmpty="renderEmpty"
:transformCellText="transformCellText"
v-bind="lockOn"
>
<ConfigProvider :locale="zhCN" :transformCellText="transformCellText" v-bind="lockOn">
<router-view />
</ConfigProvider>
</template>
......@@ -28,10 +23,9 @@
useInitAppConfigStore();
useListenerNetWork();
createBreakpointListen();
const { renderEmpty, transformCellText } = useConfigProvider();
const { transformCellText } = useConfigProvider();
const { on: lockOn } = useLockPage();
return {
renderEmpty,
transformCellText,
zhCN,
lockOn,
......
import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel';
/**
* @description: 请求列表接口参数
*/
export type DemoParams = BasicPageParams;
export interface DemoListItem {
id: string;
beginTime: string;
endTime: string;
address: string;
name: string;
no: number;
status: number;
}
/**
* @description: 请求列表返回值
*/
export type DemoListGetResultModel = BasicFetchResult<DemoListItem>;
import { defHttp } from '/@/utils/http/axios';
import { DemoParams, DemoListGetResultModel } from './model/tableModel';
enum Api {
DEMO_LIST = '/table/getDemoList',
}
/**
* @description: 获取示例列表值
*/
export function demoListApi(params: DemoParams) {
return defHttp.request<DemoListGetResultModel>({
url: Api.DEMO_LIST,
method: 'GET',
params,
});
}
export interface BasicPageParams {
page: number;
pageSize: number;
}
export interface BasicFetchResult<T extends any> {
items: T;
total: number;
}
export { default as BasicArrow } from './src/BasicArrow.vue';
export { default as BasicHelp } from './src/BasicHelp';
export { default as BasicTitle } from './src/BasicTitle.vue';
export { default as BasicEmpty } from './src/BasicEmpty.vue';
......@@ -43,11 +43,16 @@
&.right {
transform: rotate(0deg);
> span {
transition: all 0.3s ease 0.1s !important;
}
}
&__active {
transform: rotate(90deg) !important;
transition: all 0.3s ease 0.1s !important;
> span {
transform: rotate(90deg) !important;
}
}
}
</style>
<template>
<Empty :image="image" :description="description" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { Empty } from 'ant-design-vue';
import emptySrc from '/@/assets/images/page_null.png';
export default defineComponent({
extends: Empty as any,
components: { Empty },
props: {
description: {
type: String,
default: '暂无内容',
},
image: {
type: String,
default: emptySrc,
required: false,
},
},
setup() {
return {};
},
});
</script>
<template>
<Button v-bind="getBindValue" :class="[getColor, $attrs.class]">
<template v-slot:[item] v-for="item in Object.keys($slots)">
<slot :name="item" />
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data" />
</template>
</Button>
</template>
......
......@@ -6,7 +6,7 @@
<div class="collapse-container__body" v-else v-show="show">
<LazyContainer :timeout="lazyTime" v-if="lazy">
<slot />
<template v-slot:skeleton>
<template #skeleton>
<slot name="lazySkeleton" />
</template>
</LazyContainer>
......
......@@ -9,8 +9,8 @@
:allDefaultValues="getAllDefaultValues"
:formModel="formModel"
>
<template v-slot:[item] v-for="item in Object.keys($slots)">
<slot :name="item" />
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data" />
</template>
</FormItem>
</template>
......
export { default as BasicTable } from './src/BasicTable.vue';
export { default as TableAction } from './src/components/TableAction';
export { default as TableImg } from './src/components/TableImg.vue';
export { renderEditableCell } from './src/components/renderEditableCell';
export { default as EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue';
export * from './src/types/table';
export * from './src/types/pagination';
export * from './src/types/tableAction';
export { useTable } from './src/hooks/useTable';
export type { FormSchema, FormProps } from '/@/components/Form/src/types/form';
<template>
<div
class="basic-table"
:class="{
'table-form-container': getBindValues.useSearchForm,
}"
>
<BasicForm
v-bind="getFormProps"
v-if="getBindValues.useSearchForm"
:submitButtonOptions="{ loading }"
@register="registerForm"
@submit="handleSearchInfoChange"
@advanced-change="redoHeight"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="`form-${item}`" v-bind="data" />
</template>
</BasicForm>
<Table
ref="tableElRef"
v-bind="getBindValues"
:rowClassName="getRowClassName"
:class="{
hidden: !getEmptyDataIsShowTable,
}"
@change="handleTableChange"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data" />
</template>
</Table>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, computed, unref, watch, nextTick } from 'vue';
import { Table } from 'ant-design-vue';
import { basicProps } from './props';
import type {
BasicTableProps,
FetchParams,
GetColumnsParams,
TableActionType,
} from './types/table';
import { isFunction, isString } from '/@/utils/is';
import renderTitle from './components/renderTitle';
import renderFooter from './components/renderFooter';
import renderExpandIcon from './components/renderExpandIcon';
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 { 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';
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 innerPropsRef = ref<Partial<BasicTableProps>>();
const [registerForm, { getFieldsValue }] = useForm();
const getMergeProps = computed(
(): BasicTableProps => {
return {
...props,
...unref(innerPropsRef),
} as BasicTableProps;
}
);
const { loadingRef } = useLoading(getMergeProps);
const { getPaginationRef, setPagination } = usePagination(getMergeProps);
const { getColumnsRef, setColumns } = useColumns(getMergeProps, getPaginationRef);
const { getDataSourceRef, setTableData, fetch, getAutoCreateKey } = useDataSource(
getMergeProps,
{
getPaginationRef,
loadingRef,
setPagination,
getFieldsValue,
},
emit
);
const { getScrollRef, redoHeight } = useTableScroll(getMergeProps, tableElRef);
const {
getRowSelectionRef,
getSelectRows,
clearSelectedRowKeys,
getSelectRowKeys,
deleteSelectRowByKey,
setSelectedRowKeys,
} = useRowSelection(getMergeProps, emit);
const getRowKey = computed(() => {
const { rowKey } = unref(getMergeProps);
return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
});
const getBindValues = computed(() => {
const { title, titleHelpMessage, showSummary } = unref(getMergeProps);
const titleData: any =
!slots.tableTitle && !isString(title) && !title && !slots.toolbar
? {}
: {
title:
!slots.tableTitle && !title && !slots.toolbar
? null
: renderTitle.bind(null, title, titleHelpMessage, slots),
};
const pagination = unref(getPaginationRef);
const rowSelection = unref(getRowSelectionRef);
const scroll = unref(getScrollRef);
const loading = unref(loadingRef);
const rowKey = unref(getRowKey);
const columns = unref(getColumnsRef);
const dataSource = unref(getDataSourceRef);
let propsData = {
size: 'middle',
...(slots.expandedRowRender ? { expandIcon: renderExpandIcon() } : {}),
...attrs,
...unref(getMergeProps),
...titleData,
scroll,
loading,
tableLayout: 'fixed',
rowSelection,
rowKey,
columns,
pagination,
dataSource,
};
if (slots.expandedRowRender) {
propsData = omit(propsData, 'scroll');
}
if (showSummary) {
propsData.footer = renderFooter.bind(null, {
scroll,
columnsRef: getColumnsRef,
summaryFunc: unref(getMergeProps).summaryFunc,
dataSourceRef: getDataSourceRef,
rowSelectionRef: getRowSelectionRef,
});
}
return propsData;
});
const getFormProps = computed(() => {
const { formConfig } = unref(getBindValues);
const formProps: FormProps = {
showAdvancedButton: true,
...(formConfig as FormProps),
compact: true,
};
return formProps;
});
const getEmptyDataIsShowTable = computed(() => {
const { emptyDataIsShowTable, useSearchForm } = unref(getMergeProps);
if (emptyDataIsShowTable || !useSearchForm) {
return true;
}
return !!unref(getDataSourceRef).length;
});
function getRowClassName(record: TableCustomRecord<any>, index: number) {
const { striped, rowClassName } = unref(getMergeProps);
if (!striped) return;
if (rowClassName && isFunction(rowClassName)) {
return rowClassName(record);
}
return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : '';
}
function handleSearchInfoChange(info: any) {
const { handleSearchInfoFn } = unref(getMergeProps);
if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) {
info = handleSearchInfoFn(info) || info;
}
fetch({ searchInfo: info, page: 1 });
}
function handleTableChange(pagination: PaginationProps) {
const { clearSelectOnPageChange } = unref(getMergeProps);
if (clearSelectOnPageChange) {
clearSelectedRowKeys();
}
setPagination(pagination);
fetch();
}
watch(
() => unref(getDataSourceRef),
() => {
if (unref(getMergeProps).showSummary) {
nextTick(() => {
const tableEl = unref(tableElRef);
if (!tableEl) {
return;
}
const bodyDomList = tableEl.$el.querySelectorAll(
'.ant-table-body'
) as HTMLDivElement[];
const bodyDom = bodyDomList[0];
useEvent({
el: bodyDom,
name: 'scroll',
listener: () => {
const footerBodyDom = tableEl.$el.querySelector(
'.ant-table-footer .ant-table-body'
) as HTMLDivElement;
if (!footerBodyDom || !bodyDom) return;
footerBodyDom.scrollLeft = bodyDom.scrollLeft;
},
wait: 0,
options: true,
});
});
}
},
{ immediate: true }
);
const tableAction: TableActionType = {
reload: async (opt?: FetchParams) => {
await fetch(opt);
},
getSelectRows,
clearSelectedRowKeys,
getSelectRowKeys,
deleteSelectRowByKey,
setPagination,
setTableData,
redoHeight,
setSelectedRowKeys,
setColumns,
getPaginationRef: () => {
return unref(getPaginationRef);
},
getColumns: (opt?: GetColumnsParams) => {
const { ignoreIndex } = opt || {};
let columns = unref(getColumnsRef);
if (ignoreIndex) {
columns = columns.filter((item) => item.flag !== 'INDEX');
}
return columns;
},
getDataSource: () => {
return unref(getDataSourceRef);
},
setLoading: (loading: boolean) => {
loadingRef.value = loading;
},
setProps: (props: Partial<BasicTableProps>) => {
innerPropsRef.value = deepMerge(unref(innerPropsRef) || {}, props);
},
};
provideTable(tableAction);
emit('register', tableAction);
return {
tableElRef,
getBindValues,
loading: loadingRef,
registerForm,
handleSearchInfoChange,
getFormProps,
getEmptyDataIsShowTable,
handleTableChange,
getRowClassName,
...tableAction,
};
},
});
</script>
import { Component } from 'vue';
import { Input, Select, Checkbox, InputNumber, Switch } from 'ant-design-vue';
import { ComponentType } from './types/componentType';
const componentMap = new Map<ComponentType, Component>();
componentMap.set('Input', Input);
componentMap.set('InputPassword', Input.Password);
componentMap.set('InputNumber', InputNumber);
componentMap.set('Select', Select);
componentMap.set('Switch', Switch);
componentMap.set('Checkbox', Checkbox);
componentMap.set('CheckboxGroup', Checkbox.Group);
export function add(compName: ComponentType, component: Component) {
componentMap.set(compName, component);
}
export function del(compName: ComponentType) {
componentMap.delete(compName);
}
export { componentMap };
import { defineComponent, ref, computed, unref } from 'vue';
import { injectTable } from '../hooks/useProvinceTable';
import { getSlot } from '/@/utils/helper/tsxHelper';
import VueDraggableResizable from 'vue-draggable-resizable';
export default defineComponent({
name: 'DragResize',
setup(props, { slots, attrs }) {
const elRef = ref<HTMLTableRowElement | null>(null);
const draggingMapRef = ref<{ [key in string]: number | string }>({});
const tableInstance = injectTable();
const getColumnsRef = computed(() => {
const columns = tableInstance.getColumns();
columns.forEach((col) => {
const { key } = col;
if (key) {
draggingMapRef.value[key] = col.width as number;
}
});
return columns;
});
return () => {
const { key = '', ...restProps } = { ...attrs };
const col = unref(getColumnsRef).find((col) => {
const k = col.dataIndex || col.key;
return k === key;
});
if (!col || !col.width) {
return <th {...restProps}>{getSlot(slots, 'default')}</th>;
}
const onDrag = (x: number) => {
draggingMapRef.value[key] = 0;
col.width = Math.max(x, 1);
};
const onDragstop = () => {
const el = unref(elRef);
if (!el) {
return;
}
draggingMapRef.value[key] = el.getBoundingClientRect().width;
};
return (
<th
{...restProps}
class="resize-table-th"
ref={elRef}
style={{
width: `${col.width}px`,
}}
>
{getSlot(slots, 'default')}
<VueDraggableResizable
key={col.key}
class="table-draggable-handle"
w={10}
x={draggingMapRef.value[key] || col.width}
z={1}
axis="x"
draggable={true}
resizable={false}
onDragging={onDrag}
onDragstop={onDragstop}
/>
</th>
);
};
},
});
<template>
<span>
{{ title }}
<FormOutlined class="ml-2" />
</span>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { FormOutlined } from '@ant-design/icons-vue';
export default defineComponent({
name: 'EditTableHeaderIcon',
components: { FormOutlined },
props: {
title: {
type: String as PropType<string>,
default: '',
},
},
setup() {},
});
</script>
import { defineComponent, PropType } from 'vue';
import { Dropdown, Menu, Popconfirm } from 'ant-design-vue';
import Icon from '/@/components/Icon/index';
import { DownOutlined } from '@ant-design/icons-vue';
import { ActionItem } from '../types/tableAction';
import Button from '/@/components/Button/index.vue';
const prefixCls = 'basic-table-action';
export default defineComponent({
name: 'TableAction',
props: {
actions: {
type: Array as PropType<ActionItem[]>,
default: null,
},
dropDownActions: {
type: Array as PropType<ActionItem[]>,
default: null,
},
},
setup(props) {
// 增加按钮的TYPE和COLOR
return () => {
const { dropDownActions = [], actions } = props;
return (
<div class={prefixCls}>
{actions &&
actions.length &&
actions.map((action, index) => {
const {
disabled = false,
label,
props,
icon,
color = '',
type = 'link',
popConfirm = null,
} = action;
const button = (
<Button
type={type}
size="small"
disabled={disabled}
color={color}
{...props}
key={index}
>
{() => (
<>
{label}
{icon && <Icon icon={icon} />}
</>
)}
</Button>
);
if (popConfirm !== null) {
const {
title,
okText = '确定',
cancelText = '取消',
confirm = () => {},
cancel = () => {},
icon = '',
} = popConfirm;
return (
<Popconfirm
key={`P-${index}`}
title={title}
onConfirm={confirm}
onCancel={cancel}
okText={okText}
cancelText={cancelText}
icon={icon}
>
{() => button}
</Popconfirm>
);
}
return button;
})}
{dropDownActions && dropDownActions.length && (
<Dropdown>
{{
default: () => (
<Button type="link" size="small">
{{
default: () => (
<>
更多
<DownOutlined />
</>
),
}}
</Button>
),
overlay: () => {
return (
<Menu>
{{
default: () => {
return dropDownActions.map((action, index) => {
const {
disabled = false,
label,
props,
icon,
color = '',
type = 'link',
} = action;
return (
<Menu.Item key={`${index}`} disabled={disabled}>
{() => (
<Button
type={type}
size="small"
{...props}
disabled={disabled}
color={color}
>
{{
default: () => (
<>
{label}
{icon && <Icon icon={icon} />}
</>
),
}}
</Button>
)}
</Menu.Item>
);
});
},
}}
</Menu>
);
},
}}
</Dropdown>
)}
</div>
);
};
},
});
<template>
<div class="basic-table-img__preview" v-if="imgList && imgList.length">
<template v-for="(img, index) in imgList" :key="img">
<img :width="size" @click="handlePreview(index)" :src="img" />
</template>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { createImgPreview } from '/@/components/Preview/index';
export default defineComponent({
name: 'TableAction',
props: {
imgList: {
type: Array as PropType<string[]>,
default: null,
},
size: {
type: Number as PropType<number>,
default: 40,
},
},
setup(props) {
function handlePreview(index: number) {
const { imgList } = props;
createImgPreview({
imageList: imgList as string[],
index: index,
});
}
return { handlePreview };
},
});
</script>
<template>
<BasicTitle class="basic-table-title" v-if="tableTitle" :helpMessage="helpMessage">
{{ tableTitle }}
</BasicTitle>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
import { BasicTitle } from '/@/components/Basic/index';
import { isFunction } from '/@/utils/is';
export default defineComponent({
name: 'TableTitle',
components: { BasicTitle },
props: {
title: {
type: [Function, String] as PropType<string | ((data: any) => string)>,
},
getSelectRows: {
type: Function as PropType<() => any[]>,
},
helpMessage: {
type: [String, Array] as PropType<string | string[]>,
},
},
setup(props) {
const tableTitle = computed(() => {
const { title, getSelectRows = () => {} } = props;
let tit = title;
if (isFunction(title)) {
tit = title({
selectRows: getSelectRows(),
});
}
return tit;
});
return { tableTitle };
},
});
</script>
import { defineComponent, PropType, ref, unref, nextTick } from 'vue';
import { injectTable } from '../hooks/useProvinceTable';
import ClickOutSide from '/@/components/ClickOutSide/index.vue';
import { RenderEditableCellParams } from '../types/table';
import { ComponentType } from '../types/componentType';
import { componentMap } from '../componentMap';
import '../style/editable-cell.less';
import { isString, isBoolean } from '/@/utils/is';
import { FormOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue';
const prefixCls = 'editable-cell';
const EditableCell = defineComponent({
name: 'EditableCell',
props: {
value: {
type: String as PropType<string>,
default: '',
},
componentProps: {
type: Object as PropType<any>,
default: null,
},
dataKey: {
type: String as PropType<string>,
default: '',
},
dataIndex: {
type: String as PropType<string>,
default: '',
},
component: {
type: String as PropType<ComponentType>,
default: 'Input',
},
},
setup(props, { attrs }) {
const table = injectTable();
const elRef = ref<any>(null);
const isEditRef = ref(false);
const currentValueRef = ref<string | boolean>('');
function handleChange(e: ChangeEvent | string | boolean) {
if ((e as ChangeEvent).target && Reflect.has((e as ChangeEvent).target, 'value')) {
currentValueRef.value = (e as ChangeEvent).target.value;
}
if (isString(e) || isBoolean(e)) {
currentValueRef.value = e;
}
}
function handleEdit() {
isEditRef.value = true;
nextTick(() => {
const el = unref(elRef);
el && el.focus && el.focus();
});
}
function handleCancel() {
isEditRef.value = false;
}
function handleSubmit() {
const { dataKey, dataIndex } = props;
if (!dataKey || !dataIndex) {
return;
}
isEditRef.value = false;
const { getDataSource } = table;
const dataSource = getDataSource();
const target = dataSource.find((item) => item.key === dataKey);
if (target) {
target[dataIndex] = unref(currentValueRef);
}
}
function onClickOutside() {
const { component } = props;
if (component?.includes('Input')) {
handleCancel();
}
}
return () => {
const { value, component, componentProps = {} } = props;
const Comp = componentMap.get(component!) as any;
// const propsData: any = {};
return (
<div class={prefixCls}>
{unref(isEditRef) && (
<ClickOutSide onClickOutside={onClickOutside}>
{() => (
<div class={`${prefixCls}__wrapper`}>
<Comp
{...{
...attrs,
...componentProps,
}}
style={{ width: 'calc(100% - 48px)' }}
ref={elRef}
value={value}
size="small"
onChange={handleChange}
onPressEnter={handleSubmit}
/>
<div class={`${prefixCls}__action`}>
<CheckOutlined class={[`${prefixCls}__icon`, 'mx-2']} onClick={handleSubmit} />
<CloseOutlined class={[`${prefixCls}__icon `]} onClick={handleCancel} />
</div>
</div>
)}
</ClickOutSide>
)}
{!unref(isEditRef) && (
<div class={`${prefixCls}__normal`} onClick={handleEdit}>
{value}
<FormOutlined class={`${prefixCls}__normal-icon`} />
</div>
)}
</div>
);
};
},
});
export function renderEditableCell({
dataIndex,
component,
componentOn = {},
componentProps = {},
}: RenderEditableCellParams) {
return ({ text, record }: { text: string; record: any }) => {
return (
<EditableCell
value={text}
dataKey={record.key}
dataIndex={dataIndex}
component={component}
on={componentOn}
componentProps={componentProps}
/>
);
};
}
import { BasicArrow } from '/@/components/Basic';
export default () => {
return (props: any) => {
return (
<BasicArrow
onClick={(e: Event) => {
props.onExpand(props.record, e);
}}
expand={props.expanded}
class="right"
/>
);
};
};
import { Table } from 'ant-design-vue';
import { TableRowSelection } from 'ant-design-vue/types/table/table';
import { cloneDeep } from 'lodash-es';
import { unref, ComputedRef } from 'vue';
import { BasicColumn } from '../types/table';
import { isFunction } from '/@/utils/is';
export default ({
scroll = {},
columnsRef,
summaryFunc,
rowKey = 'key',
dataSourceRef,
rowSelectionRef,
}: {
scroll: { x?: number | true; y?: number };
columnsRef: ComputedRef<BasicColumn[]>;
summaryFunc: any;
rowKey?: string;
dataSourceRef: ComputedRef<any[]>;
rowSelectionRef: ComputedRef<TableRowSelection<any> | null>;
}) => {
if (!summaryFunc) {
return;
}
const dataSource: any[] = isFunction(summaryFunc) ? summaryFunc(unref(dataSourceRef)) : [];
const columns: BasicColumn[] = cloneDeep(unref(columnsRef));
const index = columns.findIndex((item) => item.flag === 'INDEX');
const hasRowSummary = dataSource.some((item) => Reflect.has(item, '_row'));
const hasIndexSummary = dataSource.some((item) => Reflect.has(item, '_index'));
if (index !== -1) {
if (hasIndexSummary) {
columns[index].customRender = ({ record }) => record._index;
columns[index].ellipsis = false;
} else {
Reflect.deleteProperty(columns[index], 'customRender');
}
}
if (unref(rowSelectionRef) && hasRowSummary) {
columns.unshift({
width: 60,
title: 'selection',
key: 'selectionKey',
align: 'center',
customRender: ({ record }) => record._row,
});
}
dataSource.forEach((item, i) => {
item[rowKey] = i;
});
return (
<Table
showHeader={false}
bordered={false}
pagination={false}
dataSource={dataSource}
rowKey={rowKey}
columns={columns}
tableLayout="fixed"
scroll={scroll as any}
/>
);
};
import { Slots } from 'vue';
import TableTitle from './TableTitle.vue';
import { getSlot } from '/@/utils/helper/tsxHelper';
export default (title: any, titleHelpMessage: string | string[], slots: Slots) => {
return (
<>
{getSlot(slots, 'tableTitle') ||
(title && <TableTitle helpMessage={titleHelpMessage} title={title} />) || (
<span>&nbsp;</span>
)}
{slots.toolbar && <div class="basic-table-toolbar">{getSlot(slots, 'toolbar')}</div>}
</>
);
};
export const ROW_KEY = 'key';
export const PAGE_SIZE_OPTIONS = ['10', '50', '80', '100'];
export const PAGE_SIZE = ~~PAGE_SIZE_OPTIONS[0];
export const FETCH_SETTING = {
pageField: 'page',
sizeField: 'pageSize',
listField: 'items',
totalField: 'total',
};
import { BasicColumn, BasicTableProps } from '../types/table';
import { PaginationProps } from '../types/pagination';
import { unref, ComputedRef, Ref, computed, watch, ref } from 'vue';
import { isBoolean, isArray, isObject } from '/@/utils/is';
import { PAGE_SIZE } from '../const';
import { useProps } from './useProps';
export function useColumns(
refProps: ComputedRef<BasicTableProps>,
getPaginationRef: ComputedRef<false | PaginationProps>
) {
const { propsRef } = useProps(refProps);
const columnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
const cacheColumnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
watch(
() => unref(propsRef).columns,
(columns) => {
columnsRef.value = columns;
cacheColumnsRef.value = columns;
},
{
immediate: true,
}
);
const getColumnsRef = computed(() => {
const props = unref(propsRef);
const { showIndexColumn, indexColumnProps, ellipsis, actionColumn, isTreeTable } = props;
const columns = unref(columnsRef);
if (!columns) {
return [];
}
let pushIndexColumns = false;
columns.forEach((item) => {
const { key, dataIndex } = item;
item.align = item.align || 'center';
if (ellipsis) {
if (!key) {
item.key = dataIndex;
}
if (!isBoolean(item.ellipsis)) {
Object.assign(item, {
ellipsis,
});
}
}
const indIndex = columns.findIndex((column) => column.flag === 'INDEX');
if (showIndexColumn && !isTreeTable) {
pushIndexColumns = indIndex === -1;
} else if (!showIndexColumn && !isTreeTable && indIndex !== -1) {
columns.splice(indIndex, 1);
}
});
if (pushIndexColumns) {
const isFixedLeft = columns.some((item) => item.fixed === 'left');
columns.unshift({
flag: 'INDEX',
width: 50,
title: '序号',
align: 'center',
customRender: ({ index }) => {
const getPagination = unref(getPaginationRef);
if (isBoolean(getPagination)) {
return `${index + 1}`;
}
const { current = 1, pageSize = PAGE_SIZE } = getPagination;
const currentIndex = (current - 1) * pageSize + index + 1;
return currentIndex;
},
...(isFixedLeft
? {
fixed: 'left',
}
: {}),
...indexColumnProps,
});
}
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',
};
}
}
return columns;
});
function setColumns(columns: BasicColumn[] | string[]) {
if (!isArray(columns)) {
return;
}
if (columns.length <= 0) {
columnsRef.value = [];
return;
}
const firstColumn = columns[0];
if (isObject(firstColumn)) {
columnsRef.value = columns as any;
} else {
const newColumns = unref(cacheColumnsRef).filter((item) =>
(columns as string[]).includes(item.key! || item.dataIndex!)
);
columnsRef.value = newColumns;
}
}
return { getColumnsRef, setColumns };
}
import { useTimeout } from '/@/hooks/core/useTimeout';
import { BasicTableProps, FetchParams } from '../types/table';
import { PaginationProps } from '../types/pagination';
import { watch, ref, unref, ComputedRef, computed, onMounted, Ref } from 'vue';
import { buildUUID } from '/@/utils/uuid';
import { isFunction, isBoolean } from '/@/utils/is';
import { FETCH_SETTING, ROW_KEY } from '../const';
import { get } from 'lodash-es';
import { useProps } from './useProps';
interface ActionType {
getPaginationRef: ComputedRef<false | PaginationProps>;
setPagination: (info: Partial<PaginationProps>) => void;
loadingRef: Ref<boolean | undefined>;
getFieldsValue: () => {
[field: string]: any;
};
}
export function useDataSource(
refProps: ComputedRef<BasicTableProps>,
{ getPaginationRef, setPagination, loadingRef, getFieldsValue }: ActionType,
emit: EmitType
) {
const { propsRef } = useProps(refProps);
const dataSourceRef = ref<any[]>([]);
watch(
() => unref(propsRef).dataSource,
(data: any[]) => {
const { api } = unref(propsRef);
!api && (dataSourceRef.value = data);
},
{ immediate: true }
);
function setTableKey(items: any[]) {
if (!items || !Array.isArray(items)) {
return;
}
items.forEach((item) => {
if (!item[ROW_KEY]) {
item[ROW_KEY] = buildUUID();
}
if (item.children && item.children.length) {
setTableKey(item.children);
}
});
}
const getAutoCreateKey = computed(() => {
return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey;
});
const getDataSourceRef = computed(() => {
const dataSource = unref(dataSourceRef);
if (!dataSource || dataSource.length === 0) {
return [];
}
if (unref(getAutoCreateKey)) {
const firstItem = dataSource[0];
const lastItem = dataSource[dataSource.length - 1];
if (firstItem && lastItem) {
if (!firstItem[ROW_KEY] || !lastItem[ROW_KEY]) {
unref(dataSourceRef).forEach((item) => {
if (!item[ROW_KEY]) {
item[ROW_KEY] = buildUUID();
}
if (item.children && item.children.length) {
setTableKey(item.children);
}
});
}
}
}
return unref(dataSourceRef);
});
async function fetch(opt?: FetchParams) {
const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm } = unref(
propsRef
);
if (!api && !isFunction(api)) return;
try {
loadingRef.value = true;
const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING;
let pageParams: any = {};
if (isBoolean(getPaginationRef)) {
pageParams = {};
} else {
const { current, pageSize } = unref(getPaginationRef) as PaginationProps;
pageParams[pageField] = opt?.page || current;
pageParams[sizeField] = pageSize;
}
let params: any = {
...pageParams,
...(useSearchForm ? getFieldsValue() : {}),
...searchInfo,
...(opt ? opt.searchInfo : {}),
};
if (beforeFetch && isFunction(beforeFetch)) {
params = beforeFetch(params) || params;
}
const res = await api(params);
let resultItems: any[] = get(res, listField);
const resultTotal: number = get(res, totalField);
if (afterFetch && isFunction(afterFetch)) {
resultItems = afterFetch(resultItems) || resultItems;
}
dataSourceRef.value = resultItems;
setPagination({
total: resultTotal || 0,
});
if (opt && opt.page) {
setPagination({
current: opt.page || 1,
});
}
emit('fetch-success', {
items: unref(resultItems),
total: resultTotal,
});
} catch (error) {
emit('fetch-error', error);
dataSourceRef.value = [];
setPagination({
total: 0,
});
} finally {
loadingRef.value = false;
}
}
function setTableData(values: any[]) {
dataSourceRef.value = values;
}
onMounted(() => {
// 转异步任务
useTimeout(() => {
unref(propsRef).immediate && fetch();
}, 0);
});
return { getDataSourceRef, setTableData, getAutoCreateKey, fetch: fetch };
}
import { watch, ref, ComputedRef, unref } from 'vue';
import { BasicTableProps } from '../types/table';
import { useProps } from './useProps';
export function useLoading(refProps: ComputedRef<BasicTableProps>) {
const { propsRef } = useProps(refProps);
const loadingRef = ref(unref(propsRef).loading);
watch(
() => unref(propsRef).loading,
(v: boolean) => {
loadingRef.value = v;
}
);
return { loadingRef };
}
import { computed, unref, ref, ComputedRef } from 'vue';
import { PaginationProps } from '../types/pagination';
import { isBoolean } from '/@/utils/is';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
import { useProps } from './useProps';
import { BasicTableProps } from '../..';
export function usePagination(refProps: ComputedRef<BasicTableProps>) {
const configRef = ref<PaginationProps>({});
const { propsRef } = useProps(refProps);
const getPaginationRef = computed((): PaginationProps | false => {
const { pagination } = unref(propsRef);
if (isBoolean(pagination) && !pagination) {
return false;
}
return {
current: 1,
pageSize: PAGE_SIZE,
size: 'small',
defaultPageSize: PAGE_SIZE,
showTotal: (total) => `共 ${total} 条数据`,
showSizeChanger: true,
pageSizeOptions: PAGE_SIZE_OPTIONS,
itemRender: ({ page, type, originalElement }) => {
if (type === 'prev') {
if (page === 0) {
return null;
}
return <LeftOutlined />;
} else if (type === 'next') {
if (page === 1) {
return null;
}
return <RightOutlined />;
}
return originalElement;
},
showQuickJumper: true,
...(isBoolean(pagination) ? {} : pagination),
...unref(configRef),
};
});
function setPagination(info: Partial<PaginationProps>) {
configRef.value = {
...unref(getPaginationRef),
...info,
};
}
return { getPaginationRef, setPagination };
}
/*
* @description:
* @author: wenbin.chen
* @Date: 2020-05-12 13:20:26
* @LastEditors: vben
* @LastEditTime: 2020-10-07 14:52:34
* @email: 190848757@qq.com
*/
import { Ref, ref, watch, unref } from 'vue';
import { BasicTableProps } from '../types/table';
/**
* @description:
* @Date: 2020-05-12 13:20:37
*/
export function useProps(props: Readonly<Ref<BasicTableProps>>) {
const propsRef = (ref<BasicTableProps>(unref(props)) as unknown) as Ref<BasicTableProps>;
watch(
() => props.value,
(v) => {
propsRef.value = unref(v);
},
{
immediate: false,
}
);
return { propsRef };
}
import { provide, inject } from 'vue';
import { TableActionType } from '../types/table';
const key = Symbol('table');
export function provideTable(instance: TableActionType) {
provide(key, instance);
}
export function injectTable(): TableActionType {
return inject(key) as TableActionType;
}
import { computed, ref, unref, ComputedRef } from 'vue';
import { BasicTableProps } from '../types/table';
import { TableRowSelection } from 'ant-design-vue/types/table/table';
import { useProps } from './useProps';
/* eslint-disable */
export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: EmitType) {
const { propsRef } = useProps(refProps);
const selectedRowKeysRef = ref<string[]>([]);
const selectedRowRef = ref<any[]>([]);
const getRowSelectionRef = computed((): TableRowSelection<any> | null => {
const rowSelection = unref(propsRef).rowSelection;
if (!rowSelection) {
return null;
}
return {
selectedRowKeys: unref(selectedRowKeysRef),
hideDefaultSelections: false,
onChange: (selectedRowKeys: string[], selectedRows: any[]) => {
selectedRowKeysRef.value = selectedRowKeys;
selectedRowRef.value = selectedRows;
emit('selection-change', {
keys: selectedRowKeys,
rows: selectedRows,
});
},
...rowSelection,
};
});
function setSelectedRowKeys(rowKeys: string[]) {
selectedRowKeysRef.value = rowKeys;
}
function clearSelectedRowKeys() {
selectedRowRef.value = [];
selectedRowKeysRef.value = [];
}
function deleteSelectRowByKey(key: string) {
const selectedRowKeys = unref(selectedRowKeysRef);
const index = selectedRowKeys.findIndex((item) => item === key);
if (index !== -1) {
unref(selectedRowKeysRef).splice(index, 1);
}
}
function getSelectRowKeys() {
return unref(selectedRowKeysRef);
}
function getSelectRows() {
return unref(selectedRowRef);
}
return {
getRowSelectionRef,
getSelectRows,
getSelectRowKeys,
setSelectedRowKeys,
clearSelectedRowKeys,
deleteSelectRowByKey,
};
}
import type { BasicTableProps, TableActionType, FetchParams, BasicColumn } from '../types/table';
import type { PaginationProps } from '../types/pagination';
import { ref, getCurrentInstance, onUnmounted, unref } from 'vue';
import { isProdMode } from '/@/utils/env';
export function useTable(
tableProps?: Partial<BasicTableProps>
): [(instance: TableActionType) => void, TableActionType] {
if (!getCurrentInstance()) {
throw new Error('Please put useTable function in the setup function!');
}
const tableRef = ref<TableActionType | null>(null);
const loadedRef = ref<boolean | null>(false);
function register(instance: TableActionType) {
onUnmounted(() => {
tableRef.value = null;
loadedRef.value = null;
});
if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) {
return;
}
tableRef.value = instance;
tableProps && instance.setProps(tableProps);
loadedRef.value = true;
}
function getTableInstance(): TableActionType {
const table = unref(tableRef);
if (!table) {
throw new Error('table is undefined!');
}
return table;
}
const methods: TableActionType = {
reload: (opt?: FetchParams) => {
getTableInstance().reload(opt);
},
setProps: (props: Partial<BasicTableProps>) => {
getTableInstance().setProps(props);
},
redoHeight: () => {
getTableInstance().redoHeight();
},
setLoading: (loading: boolean) => {
getTableInstance().setLoading(loading);
},
getDataSource: () => {
return getTableInstance().getDataSource();
},
getColumns: ({ ignoreIndex = false }: { ignoreIndex?: boolean } = {}) => {
const columns = getTableInstance().getColumns({ ignoreIndex }) || [];
return columns;
},
setColumns: (columns: BasicColumn[]) => {
getTableInstance().setColumns(columns);
},
setTableData: (values: any[]) => {
return getTableInstance().setTableData(values);
},
setPagination: (info: Partial<PaginationProps>) => {
return getTableInstance().setPagination(info);
},
deleteSelectRowByKey: (key: string) => {
getTableInstance().deleteSelectRowByKey(key);
},
getSelectRowKeys: () => {
return getTableInstance().getSelectRowKeys();
},
getSelectRows: () => {
return getTableInstance().getSelectRows();
},
clearSelectedRowKeys: () => {
getTableInstance().clearSelectedRowKeys();
},
setSelectedRowKeys: (keys: string[] | number[]) => {
getTableInstance().setSelectedRowKeys(keys);
},
getPaginationRef: () => {
return getTableInstance().getPaginationRef();
},
} as TableActionType;
return [register, methods];
}
import { BasicTableProps } from '../types/table';
import { computed, Ref, onMounted, unref, ref, nextTick, ComputedRef, watch } from 'vue';
import { getViewportOffset } from '/@/utils/domUtils';
import { triggerWindowResize } from '/@/utils/event/triggerWindowResizeEvent';
import { isBoolean } from '/@/utils/is';
import { useTimeout } from '/@/hooks/core/useTimeout';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSize';
import { useProps } from './useProps';
export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRef: Ref<any>) {
const { propsRef } = useProps(refProps);
const tableHeightRef: Ref<number | null> = ref(null);
watch(
() => unref(propsRef).canResize,
() => {
redoHeight();
}
);
function redoHeight() {
const { canResize } = unref(propsRef);
if (!canResize) {
return;
}
calcTableHeight();
}
async function calcTableHeight(cb?: () => void) {
const { canResize, resizeHeightOffset, pagination, maxHeight } = unref(propsRef);
if (!canResize) {
return;
}
await nextTick();
const table = unref(tableElRef) as any;
if (!table) {
return;
}
const tableEl: Element = table.$el;
if (!tableEl) {
return;
}
const el: HTMLElement | null = tableEl.querySelector('.ant-table-thead ');
// const layoutMain: Element | null = document.querySelector('.default-layout__main ');
if (!el) {
return;
}
// 表格距离底部高度
const { bottomIncludeBody } = getViewportOffset(el);
// 表格高度+距离底部高度-自定义偏移量
const paddingHeight = 32;
const borderHeight = 2 * 2;
// 分页器高度
// TODO 先固定20
const paginationHeight = 20;
// if (!isBoolean(pagination)) {
// const paginationDom = tableEl.querySelector('.ant-pagination') as HTMLElement;
// if (paginationDom) {
// const offsetHeight = paginationDom.offsetHeight;
// paginationHeight += offsetHeight || 0;
// }
// }
let footerHeight = 0;
if (!isBoolean(pagination)) {
const footerEl = tableEl.querySelector('.ant-table-footer') as HTMLElement;
if (footerEl) {
const offsetHeight = footerEl.offsetHeight;
footerHeight += offsetHeight || 0;
}
}
let headerHeight = 0;
if (el) {
headerHeight = (el as HTMLElement).offsetHeight;
}
tableHeightRef.value =
bottomIncludeBody -
(resizeHeightOffset || 0) -
paddingHeight -
borderHeight -
paginationHeight -
footerHeight -
headerHeight;
useTimeout(() => {
tableHeightRef.value =
tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value;
cb && cb();
}, 0);
}
const getCanResize = computed(() => {
const { canResize, scroll } = unref(propsRef);
return canResize && !(scroll || {}).y;
});
useWindowSizeFn(calcTableHeight, 100);
// function clear() {
// window.clearInterval(timer);
// }
onMounted(() => {
if (unref(getCanResize)) {
calcTableHeight();
const hasFixedLeft = (unref(propsRef).columns || []).some((item) => item.fixed === 'left');
// TODO antv table问题情况太多,只能先用下面方式定时器hack
useTimeout(() => {
calcTableHeight(() => {
// 有左侧固定列的时候才有问题
hasFixedLeft &&
useTimeout(() => {
triggerWindowResize();
}, 300);
});
}, 200);
}
});
const getScrollRef = computed(() => {
const tableHeight = unref(tableHeightRef);
const { canResize, scroll } = unref(propsRef);
return {
x: '100%',
y: canResize ? tableHeight : null,
scrollToFirstRowOnChange: false,
...scroll,
};
});
return { getScrollRef, redoHeight };
}
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 { FETCH_SETTING } from './const';
// 注释看 types/table
export const basicProps = {
autoCreateKey: {
type: Boolean as PropType<boolean>,
default: true,
},
striped: {
type: Boolean as PropType<boolean>,
default: true,
},
showSummary: {
type: Boolean as PropType<boolean>,
default: false,
},
summaryFunc: {
type: [Function, Array] as PropType<(...arg: any[]) => any[]>,
default: null,
},
canColDrag: {
type: Boolean as PropType<boolean>,
default: true,
},
isTreeTable: {
type: Boolean as PropType<boolean>,
default: false,
},
api: {
type: Function as PropType<(...arg: any[]) => Promise<any>>,
default: null,
},
beforeFetch: {
type: Function as PropType<Fn>,
default: null,
},
afterFetch: {
type: Function as PropType<Fn>,
default: null,
},
handleSearchInfoFn: {
type: Function as PropType<Fn>,
default: null,
},
fetchSetting: {
type: Object as PropType<FetchSetting>,
default: () => {
return FETCH_SETTING;
},
},
// 立即请求接口
immediate: { type: Boolean as PropType<boolean>, default: true },
emptyDataIsShowTable: {
type: Boolean as PropType<boolean>,
default: true,
},
// 额外的请求参数
searchInfo: {
type: Object as PropType<any>,
default: null,
},
// 使用搜索表单
useSearchForm: {
type: Boolean as PropType<boolean>,
default: false,
},
// 表单配置
formConfig: {
type: Object as PropType<Partial<FormProps>>,
default: null,
},
columns: {
type: [Array] as PropType<BasicColumn[]>,
default: null,
},
showIndexColumn: {
type: Boolean as PropType<boolean>,
default: true,
},
indexColumnProps: {
type: Object as PropType<BasicColumn>,
default: null,
},
actionColumn: {
type: Object as PropType<BasicColumn>,
default: null,
},
ellipsis: {
type: Boolean as PropType<boolean>,
default: true,
},
canResize: {
type: Boolean as PropType<boolean>,
default: true,
},
clearSelectOnPageChange: {
type: Boolean as PropType<boolean>,
default: false,
},
resizeHeightOffset: {
type: Number as PropType<number>,
default: 0,
},
rowSelection: {
type: Object as PropType<TableRowSelection<any> | null>,
default: null,
},
title: {
type: [String, Function] as PropType<string | ((data: any) => any)>,
default: null,
},
titleHelpMessage: {
type: [String, Array] as PropType<string | string[]>,
},
maxHeight: {
type: Number as PropType<number>,
},
dataSource: {
type: Array as PropType<any[]>,
default: null,
},
rowKey: {
type: [String, Function] as PropType<string | ((record: any) => string)>,
default: '',
},
bordered: {
type: Boolean as PropType<boolean>,
default: true,
},
pagination: {
type: [Object, Boolean] as PropType<PaginationProps | boolean>,
default: null,
},
loading: {
type: Boolean as PropType<boolean>,
default: false,
},
rowClassName: {
type: Function as PropType<(record: TableCustomRecord<any>, index: number) => string>,
},
scroll: {
type: Object as PropType<{ x: number | true; y: number }>,
default: null,
},
};
@import (reference) '../../../../design/index.less';
@prefix-cls: ~'editable-cell';
.@{prefix-cls} {
position: relative;
&__wrapper {
display: flex;
align-items: center;
}
&__icon {
&:hover {
transform: scale(1.2);
svg {
color: @primary-color;
}
}
}
&__normal {
padding-right: 48px;
&-icon {
position: absolute;
top: 4px;
right: 0;
display: none;
width: 20px;
cursor: pointer;
}
}
&:hover {
.@{prefix-cls}__normal-icon {
display: inline-block;
}
}
}
@import (reference) '../../../../design/index.less';
@border-color: hsla(0, 0%, 80.8%, 0.25);
.basic-table {
&-title {
display: flex;
justify-content: space-between;
align-items: center;
}
&-row__striped {
td {
background: #fafafa;
}
}
&-img__preview {
display: flex;
img {
margin-right: 4px;
}
}
&-action {
display: flex;
}
&-toolbar {
> * {
margin-right: 10px;
}
}
.resize-table-th {
position: relative !important;
.table-draggable-handle {
position: absolute;
right: -5px;
bottom: 0;
left: auto !important;
height: 100% !important;
cursor: col-resize;
transform: none !important;
touch-action: none;
}
}
&-drag-body {
position: relative;
cursor: move;
}
.drag-line td {
border-top: 2px dashed @primary-color;
}
.ant-table-wrapper {
padding: 8px;
background: #fff;
border-radius: 2px;
.ant-table-title {
padding: 0 0 10px 0 !important;
}
.ant-table.ant-table-bordered .ant-table-title {
border: none !important;
}
}
//
.ant-table {
&-title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 6px;
}
.ant-table-thead > tr > th,
.ant-table-header {
background: #f1f3f4;
}
.ant-table-tbody > tr.ant-table-row-selected td {
background: fade(@primary-color, 8%) !important;
}
}
.ant-table-bordered .ant-table-header > table,
.ant-table-bordered .ant-table-body > table,
.ant-table-bordered .ant-table-fixed-left table,
.ant-table-bordered .ant-table-fixed-right table {
border: 1px solid @border-color;
}
.ant-table-thead {
th {
border: none;
}
}
.ant-table-bordered .ant-table-tbody > tr > td {
border-bottom: 1px solid @border-color;
&:last-child {
border-right: none !important;
}
}
.ant-table.ant-table-bordered .ant-table-footer,
.ant-table.ant-table-bordered .ant-table-title {
border: 1px solid @border-color !important;
}
.ant-table-bordered.ant-table-empty .ant-table-placeholder {
border: 1px solid @border-color !important;
}
.ant-table td {
white-space: nowrap;
}
.ant-table-row-cell-last {
border-right: none !important;
}
.ant-table-bordered .ant-table-thead > tr > th,
.ant-table-bordered .ant-table-tbody > tr > td {
border-right: 1px solid @border-color;
}
.ant-table-thead > tr > th,
.ant-table-tbody > tr > td {
padding: 9px 8px !important;
}
.ant-pagination {
margin: 10px 0 0 0;
}
.ant-table-body {
overflow-x: auto !important;
overflow-y: scroll !important;
}
.ant-table-header {
margin-bottom: 0 !important;
overflow-x: hidden !important;
overflow-y: scroll !important;
}
.ant-table-fixed-right .ant-table-header {
border-left: 1px solid @border-color;
.ant-table-fixed {
border-bottom: none;
}
}
.ant-table-fixed-left {
.ant-table-header {
overflow-y: hidden !important;
}
.ant-table-fixed {
border-bottom: none;
}
}
.ant-radio {
&-inner {
border-color: @text-color-base;
}
}
.ant-checkbox {
&:not(.ant-checkbox-checked) {
.ant-checkbox-inner {
border-color: @text-color-base;
}
}
}
.ant-table-bordered .ant-table-thead > tr:not(:last-child) > th,
.ant-table-tbody > tr > td {
word-break: break-word;
border-color: @border-color;
}
.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;
}
}
}
.table-form-container {
padding: 16px;
.ant-form {
padding: 12px 12px 4px 12px;
margin-bottom: 12px;
background: #fff;
border-radius: 2px;
}
.ant-table-wrapper {
border-radius: 2px;
}
}
export type ComponentType =
| 'Input'
| 'InputPassword'
| 'InputNumber'
| 'Select'
| 'Checkbox'
| 'CheckboxGroup'
| 'Switch';
import { VNodeChild } from 'vue';
import { PaginationRenderProps } from 'ant-design-vue/types/pagination';
export interface PaginationProps {
/**
* total number of data items
* @default 0
* @type number
*/
total?: number;
/**
* default initial page number
* @default 1
* @type number
*/
defaultCurrent?: number;
/**
* current page number
* @type number
*/
current?: number;
/**
* default number of data items per page
* @default 10
* @type number
*/
defaultPageSize?: number;
/**
* number of data items per page
* @type number
*/
pageSize?: number;
/**
* Whether to hide pager on single page
* @default false
* @type boolean
*/
hideOnSinglePage?: boolean;
/**
* determine whether pageSize can be changed
* @default false
* @type boolean
*/
showSizeChanger?: boolean;
/**
* specify the sizeChanger options
* @default ['10', '20', '30', '40']
* @type string[]
*/
pageSizeOptions?: string[];
/**
* determine whether you can jump to pages directly
* @default false
* @type boolean
*/
showQuickJumper?: boolean | object;
/**
* to display the total number and range
* @type Function
*/
showTotal?: (total: number, range: [number, number]) => any;
/**
* specify the size of Pagination, can be set to small
* @default ''
* @type string
*/
size?: string;
/**
* whether to use simple mode
* @type boolean
*/
simple?: boolean;
/**
* to customize item innerHTML
* @type Function
*/
itemRender?: (props: PaginationRenderProps) => VNodeChild | JSX.Element;
}
import { VNodeChild } from 'vue';
import { PaginationProps } from './pagination';
import { FormProps } from '/@/components/Form/index';
import {
ExpandedRowRenderRecord,
PaginationConfig,
SorterResult,
TableCurrentDataSource,
TableCustomRecord,
TableRowSelection,
} from 'ant-design-vue/types/table/table';
import { ColumnProps } from 'ant-design-vue/types/table/column';
import { ComponentType } from './componentType';
export declare type SortOrder = 'ascend' | 'descend';
export interface ColumnFilterItem {
text?: string;
value?: string;
children?: any;
}
export interface RenderEditableCellParams {
dataIndex: string;
component?: ComponentType;
componentOn?: { [key: string]: Fn };
componentProps?: any;
}
export interface FetchParams {
searchInfo?: any;
page?: number;
}
export interface GetColumnsParams {
ignoreIndex?: boolean;
}
export interface TableActionType {
reload: (opt?: FetchParams) => Promise<void>;
getSelectRows: () => any[];
clearSelectedRowKeys: () => void;
getSelectRowKeys: () => string[];
deleteSelectRowByKey: (key: string) => void;
setPagination: (info: Partial<PaginationProps>) => void;
setTableData: (values: any[]) => void;
getColumns: ({ ignoreIndex }?: GetColumnsParams) => BasicColumn[];
setColumns: (columns: BasicColumn[] | string[]) => void;
getDataSource: () => any[];
setLoading: (loading: boolean) => void;
setProps: (props: Partial<BasicTableProps>) => void;
redoHeight: () => void;
setSelectedRowKeys: (rowKeys: string[] | number[]) => void;
getPaginationRef: () => PaginationProps | boolean;
}
export interface FetchSetting {
// 请求接口当前页数
pageField: string;
// 每页显示多少条
sizeField: string;
// 请求结果列表字段 支持 a.b.c
listField: string;
// 请求结果总数字段 支持 a.b.c
totalField: string;
}
export interface BasicTableProps<T = any> {
// 斑马纹
striped?: boolean;
// 是否自动生成key
autoCreateKey?: boolean;
// 计算合计行的方法
summaryFunc?: (...arg: any) => any[];
// 是否显示合计行
showSummary?: boolean;
// 是否可拖拽列
canColDrag?: boolean;
// 是否树表
isTreeTable?: boolean;
// 接口请求对象
api?: (...arg: any) => Promise<any>;
// 请求之前处理参数
beforeFetch?: Fn;
// 自定义处理接口返回参数
afterFetch?: Fn;
// 查询条件请求之前处理
handleSearchInfoFn?: Fn;
// 请求接口配置
fetchSetting?: FetchSetting;
// 立即请求接口
immediate?: boolean;
// 在开起搜索表单的时候,如果没有数据是否显示表格
emptyDataIsShowTable?: boolean;
// 额外的请求参数
searchInfo?: any;
// 使用搜索表单
useSearchForm?: boolean;
// 表单配置
formConfig?: FormProps;
// 列配置
columns: BasicColumn[];
// 是否显示序号列
showIndexColumn?: boolean;
// 序号列配置
indexColumnProps?: BasicColumn;
actionColumn?: BasicColumn;
// 文本超过宽度是否显示。。。
ellipsis?: boolean;
// 是否可以自适应高度
canResize?: boolean;
// 自适应高度偏移, 计算结果-偏移量
resizeHeightOffset?: number;
// 在分页改变的时候清空选项
clearSelectOnPageChange?: boolean;
//
rowKey?: string | ((record: any) => string);
// 数据
dataSource?: any[];
// 标题右侧提示
titleHelpMessage?: string | string[];
// 表格滚动最大高度
maxHeight?: number;
// 是否显示边框
bordered?: boolean;
// 分页配置
pagination?: PaginationProps | boolean;
// loading加载
loading?: boolean;
/**
* The column contains children to display
* @default 'children'
* @type string | string[]
*/
childrenColumnName?: string | string[];
/**
* Override default table elements
* @type object
*/
components?: object;
/**
* Expand all rows initially
* @default false
* @type boolean
*/
defaultExpandAllRows?: boolean;
/**
* Initial expanded row keys
* @type string[]
*/
defaultExpandedRowKeys?: string[];
/**
* Current expanded row keys
* @type string[]
*/
expandedRowKeys?: string[];
/**
* Expanded container render for each row
* @type Function
*/
expandedRowRender?: (record?: ExpandedRowRenderRecord<T>) => VNodeChild | JSX.Element;
/**
* Customize row expand Icon.
* @type Function | VNodeChild
*/
expandIcon?: Function | VNodeChild | JSX.Element;
/**
* Whether to expand row by clicking anywhere in the whole row
* @default false
* @type boolean
*/
expandRowByClick?: boolean;
/**
* The index of `expandIcon` which column will be inserted when `expandIconAsCell` is false. default 0
*/
expandIconColumnIndex?: number;
/**
* Table footer renderer
* @type Function | VNodeChild
*/
footer?: Function | VNodeChild | JSX.Element;
/**
* Indent size in pixels of tree data
* @default 15
* @type number
*/
indentSize?: number;
/**
* i18n text including filter, sort, empty text, etc
* @default { filterConfirm: 'Ok', filterReset: 'Reset', emptyText: 'No Data' }
* @type object
*/
locale?: object;
/**
* Row's className
* @type Function
*/
rowClassName?: (record: TableCustomRecord<T>) => string;
/**
* Row selection config
* @type object
*/
rowSelection?: TableRowSelection<T>;
/**
* Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area.
* It is recommended to set a number for x, if you want to set it to true,
* you need to add style .ant-table td { white-space: nowrap; }.
* @type object
*/
scroll?: { x?: number | true; y?: number };
/**
* Whether to show table header
* @default true
* @type boolean
*/
showHeader?: boolean;
/**
* Size of table
* @default 'default'
* @type string
*/
size?: 'default' | 'middle' | 'small' | 'large';
/**
* Table title renderer
* @type Function | ScopedSlot
*/
title?: VNodeChild | JSX.Element;
/**
* Set props on per header row
* @type Function
*/
customHeaderRow?: (column: ColumnProps<T>, index: number) => object;
/**
* Set props on per row
* @type Function
*/
customRow?: (record: T, index: number) => object;
/**
* `table-layout` attribute of table element
* `fixed` when header/columns are fixed, or using `column.ellipsis`
*
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout
* @version 1.5.0
*/
tableLayout?: 'auto' | 'fixed' | string;
/**
* the render container of dropdowns in table
* @param triggerNode
* @version 1.5.0
*/
getPopupContainer?: (triggerNode?: HTMLElement) => HTMLElement;
/**
* Data can be changed again before rendering.
* The default configuration of general user empty data.
* You can configured globally through [ConfigProvider](https://antdv.com/components/config-provider-cn/)
*
* @version 1.5.4
*/
transformCellText?: Function;
/**
* Callback executed when pagination, filters or sorter is changed
* @param pagination
* @param filters
* @param sorter
* @param currentDataSource
*/
onChange?: (
pagination: PaginationConfig,
filters: Partial<Record<keyof T, string[]>>,
sorter: SorterResult<T>,
extra: TableCurrentDataSource<T>
) => void;
/**
* Callback executed when the row expand icon is clicked
*
* @param expanded
* @param record
*/
onExpand?: (expande: boolean, record: T) => void;
/**
* Callback executed when the expanded rows change
* @param expandedRows
*/
onExpandedRowsChange?: (expandedRows: string[] | number[]) => void;
}
export interface BasicColumn<T = any> extends ColumnProps<T> {
children?: BasicColumn[];
//
flag?: 'INDEX' | 'DEFAULT' | 'CHECKBOX' | 'RADIO' | 'ACTION';
}
export interface ActionItem {
on?: any;
label: string;
disabled?: boolean;
color?: 'success' | 'error' | 'warning';
type?: string;
props?: any;
icon?: string;
popConfirm?: PopConfirm;
}
export interface PopConfirm {
title: string;
okText?: string;
cancelText?: string;
confirm: any;
cancel?: any;
icon?: string;
}
body {
.ant-pagination {
&.mini {
.ant-pagination {
&.mini {
height: 20px;
font-size: 13px;
.ant-pagination-prev,
.ant-pagination-next {
width: 20px;
height: 20px;
font-size: 13px;
.ant-pagination-prev,
.ant-pagination-next {
width: 20px;
height: 20px;
min-width: 20px;
line-height: 17px;
color: @border-color-shallow-dark;
border: 1px solid;
}
min-width: 20px;
line-height: 20px;
color: @border-color-shallow-dark;
border: 1px solid;
}
.ant-pagination-prev:hover,
.ant-pagination-next:hover,
.ant-pagination-item:focus,
.ant-pagination-item:hover {
color: @primary-color;
border: 1px solid @primary-color;
}
.ant-pagination-prev:hover,
.ant-pagination-next:hover,
.ant-pagination-item:focus,
.ant-pagination-item:hover {
color: @primary-color;
border: 1px solid @primary-color;
}
.ant-pagination-item {
height: 20px;
min-width: 20px;
margin: 0 3px;
line-height: 20px;
.ant-pagination-item {
height: 20px;
min-width: 20px;
margin: 0 3px;
line-height: 20px;
&:last-child {
margin-right: 0 !important;
}
&:last-child {
margin-right: 0 !important;
}
}
.ant-pagination-item-active {
background: @primary-color;
.ant-pagination-item-active {
background: @primary-color;
a {
color: @white;
}
}
.ant-pagination-options {
margin-left: 20px;
a {
color: @white;
}
}
.ant-select-sm .ant-select-selection--single {
height: 20px;
}
.ant-pagination-options {
margin-left: 20px;
}
.ant-pagination-options,
.ant-pagination-total-text,
.ant-pagination-options-quick-jumper {
height: 20px;
line-height: 20px;
}
.ant-select-sm .ant-select-selection--single {
height: 20px;
}
.ant-select-selection__rendered {
height: 18px;
line-height: 18px;
}
.ant-pagination-options,
.ant-pagination-total-text,
.ant-pagination-options-quick-jumper {
height: 20px;
line-height: 20px;
}
.ant-pagination-total-text,
.ant-select-selection__rendered,
.ant-select-dropdown-menu-item,
.ant-pagination-options-quick-jumper {
font-size: 13px;
}
.ant-select-selection__rendered {
height: 18px;
line-height: 18px;
}
.ant-pagination-options-quick-jumper input {
width: 40px;
height: 20px;
margin: 0 6px;
line-height: 20px;
text-align: center;
}
.ant-pagination-total-text,
.ant-select-selection__rendered,
.ant-select-dropdown-menu-item,
.ant-pagination-options-quick-jumper {
font-size: 13px;
}
.ant-pagination-jump-prev,
.ant-pagination-jump-next {
height: 20px;
line-height: 20px;
}
.ant-pagination-options-quick-jumper input {
width: 40px;
height: 20px;
margin: 0 6px;
line-height: 20px;
text-align: center;
}
.ant-pagination-options-size-changer.ant-select {
margin-right: 20px;
}
.ant-pagination-jump-prev,
.ant-pagination-jump-next {
height: 20px;
line-height: 20px;
}
.ant-select-arrow {
color: @border-color-shallow-dark;
}
.ant-pagination-options-size-changer.ant-select {
margin-right: 20px;
}
&-disabled {
display: none;
.ant-select-arrow {
color: @border-color-shallow-dark;
}
}
&-disabled {
display: none;
}
}
......@@ -6,17 +6,23 @@ import { ContentEnum } from '/@/enums/appEnum';
import { appStore } from '/@/store/modules/app';
// import { RouterView } from 'vue-router';
import PageLayout from '/@/layouts/page/index';
import FrameLayout from '/@/layouts/iframe/index.vue';
import { useSetting } from '/@/hooks/core/useSetting';
export default defineComponent({
name: 'DefaultLayoutContent',
setup() {
const { projectSetting } = useSetting();
return () => {
const { getProjectConfig } = appStore;
const { contentMode } = getProjectConfig;
const wrapClass = contentMode === ContentEnum.FULL ? 'full' : 'fixed';
return (
<Layout.Content class={`layout-content ${wrapClass} `}>
{{
default: () => <PageLayout />,
default: () => [<PageLayout />, projectSetting.canEmbedIFramePage && <FrameLayout />],
}}
</Layout.Content>
);
......
......@@ -6,9 +6,7 @@ import { useTransition } from './useTransition';
import { RouterView, RouteLocation } from 'vue-router';
import { tabStore } from '/@/store/modules/tab';
import FrameLayout from '/@/layouts/iframe/index.vue';
import { useSetting } from '/@/hooks/core/useSetting';
// import { useRouter } from 'vue-router';
export default defineComponent({
name: 'PageLayout',
......@@ -24,7 +22,6 @@ export default defineComponent({
const { on: transitionOn } = useTransition();
on = transitionOn;
}
const { projectSetting } = useSetting();
return () => {
const {
routerTransition,
......@@ -35,35 +32,32 @@ export default defineComponent({
const openCache = openKeepAlive && show;
const cacheTabs = toRaw(tabStore.getKeepAliveTabsState) as string[];
return (
<div>
<RouterView>
{{
default: ({ Component, route }: { Component: any; route: RouteLocation }) => {
const Content = openCache ? (
<KeepAlive max={max} include={cacheTabs}>
<Component {...route.params} />
</KeepAlive>
) : (
return [
<RouterView>
{{
default: ({ Component, route }: { Component: any; route: RouteLocation }) => {
const Content = openCache ? (
<KeepAlive max={max} include={cacheTabs}>
<Component {...route.params} />
);
return openRouterTransition ? (
<Transition
{...on}
name={route.meta.transitionName || routerTransition}
mode="out-in"
>
{() => Content}
</Transition>
) : (
Content
);
},
}}
</RouterView>
{projectSetting.canEmbedIFramePage && <FrameLayout />}
</div>
);
</KeepAlive>
) : (
<Component {...route.params} />
);
return openRouterTransition ? (
<Transition
{...on}
name={route.meta.transitionName || routerTransition}
mode="out-in"
>
{() => Content}
</Transition>
) : (
Content
);
},
}}
</RouterView>,
];
};
},
});
......@@ -3,10 +3,10 @@ import type { Router } from 'vue-router';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
NProgress.inc(0.4);
NProgress.configure({ easing: 'ease', speed: 1000, showSpinner: false });
export function createProgressGuard(router: Router) {
NProgress.inc(0.1);
NProgress.configure({ easing: 'ease', speed: 200, showSpinner: false });
router.beforeEach(async () => {
NProgress.start();
return true;
......
......@@ -17,6 +17,68 @@ const menu: MenuModule = {
path: '/click-out-side',
name: 'ClickOutSide组件',
},
{
path: '/table',
name: '表格组件',
children: [
{
path: '/basic',
name: '基础表格',
},
{
path: '/treeTable',
name: '树形表格',
},
{
path: '/fetchTable',
name: '远程加载',
},
{
path: '/fixedColumn',
name: '固定列',
},
{
path: '/customerCell',
name: '自定义列',
},
{
path: '/formTable',
name: '开启搜索区域',
},
{
path: '/useTable',
name: 'UseTable',
},
{
path: '/refTable',
name: 'RefTable',
},
{
path: '/multipleHeader',
name: '多级表头',
},
{
path: '/mergeHeader',
name: '合并表头',
},
{
path: '/expandTable',
name: '可展开表格',
},
{
path: '/fixedHeight',
name: '定高/头部自定义',
},
{
path: '/footerTable',
name: '表尾行合计',
},
{
path: '/editCellTable',
name: '可编辑单元格',
},
],
},
{
path: '/form',
name: '表单组件',
......
......@@ -98,6 +98,128 @@ export default {
},
],
},
{
path: '/table',
name: 'TableDemo',
redirect: '/comp/table/basic',
meta: {
title: '表格组件',
},
children: [
{
path: 'basic',
name: 'TableBasicDemo',
component: () => import('/@/views/demo/table/Basic.vue'),
meta: {
title: '基础表格',
},
},
{
path: 'treeTable',
name: 'TreeTableDemo',
component: () => import('/@/views/demo/table/TreeTable.vue'),
meta: {
title: '树形表格',
},
},
{
path: 'fetchTable',
name: 'FetchTableDemo',
component: () => import('/@/views/demo/table/FetchTable.vue'),
meta: {
title: '远程加载示例',
},
},
{
path: 'fixedColumn',
name: 'FixedColumnDemo',
component: () => import('/@/views/demo/table/FixedColumn.vue'),
meta: {
title: '固定列',
},
},
{
path: 'customerCell',
name: 'CustomerCellDemo',
component: () => import('/@/views/demo/table/CustomerCell.vue'),
meta: {
title: '自定义列',
},
},
{
path: 'formTable',
name: 'FormTableDemo',
component: () => import('/@/views/demo/table/FormTable.vue'),
meta: {
title: '开启搜索区域',
},
},
{
path: 'useTable',
name: 'UseTableDemo',
component: () => import('/@/views/demo/table/UseTable.vue'),
meta: {
title: 'UseTable',
},
},
{
path: 'refTable',
name: 'RefTableDemo',
component: () => import('/@/views/demo/table/RefTable.vue'),
meta: {
title: 'RefTable',
},
},
{
path: 'multipleHeader',
name: 'MultipleHeaderDemo',
component: () => import('/@/views/demo/table/MultipleHeader.vue'),
meta: {
title: '多级表头',
},
},
{
path: 'mergeHeader',
name: 'MergeHeaderDemo',
component: () => import('/@/views/demo/table/MergeHeader.vue'),
meta: {
title: '合并表头',
},
},
{
path: 'expandTable',
name: 'ExpandTableDemo',
component: () => import('/@/views/demo/table/ExpandTable.vue'),
meta: {
title: '可展开表格',
},
},
{
path: 'fixedHeight',
name: 'FixedHeightDemo',
component: () => import('/@/views/demo/table/FixedHeight.vue'),
meta: {
title: '定高/头部自定义',
},
},
{
path: 'footerTable',
name: 'FooterTableDemo',
component: () => import('/@/views/demo/table/FooterTable.vue'),
meta: {
title: '表尾行合计',
},
},
{
path: 'editCellTable',
name: 'EditCellTableDemo',
component: () => import('/@/views/demo/table/EditCellTable.vue'),
meta: {
title: '可编辑单元格',
},
},
],
},
{
path: '/tree',
name: 'TreeDemo',
......
import type { App } from 'vue';
import { createStore, createLogger, Plugin } from 'vuex';
import {
createStore,
// createLogger, Plugin
} from 'vuex';
import { config } from 'vuex-module-decorators';
import { isDevMode } from '/@/utils/env';
config.rawError = true;
const isDev = isDevMode();
const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
// const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
const store = createStore({
modules: {},
strict: isDev,
plugins,
// plugins,
});
export function setupStore(app: App<Element>) {
app.use(store);
......
declare module 'ant-design-vue/es/locale/zh_CN';
declare module 'vue-draggable-resizable';
declare const React: string;
declare module '*.bmp' {
const src: string;
......
......@@ -2,8 +2,6 @@ import type { ProjectConfig } from '/@/types/config';
import { computed, ref } from 'vue';
import { BasicEmpty } from '/@/components/Basic';
import { ThemeModeEnum } from '/@/enums/appEnum';
import { PROJ_CFG_KEY } from '/@/enums/cacheEnum';
......@@ -59,10 +57,6 @@ export function useInitAppConfigStore() {
// Config Provider
export function useConfigProvider() {
function renderEmpty() {
return <BasicEmpty />;
}
function transformCellText({ text }: { text: string }) {
if (isNull(text) || isUnDef(text)) {
return ' - ';
......@@ -70,7 +64,6 @@ export function useConfigProvider() {
return text;
}
return {
renderEmpty,
transformCellText,
};
}
......
......@@ -25,7 +25,7 @@
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el4" @success="handleSuccess">
<template v-slot:actionIcon="isPassing">
<template #actionIcon="isPassing">
<BugOutlined v-if="isPassing" />
<RightOutlined v-else />
</template>
......@@ -35,7 +35,7 @@
<div class="flex justify-center p-4 items-center bg-gray-700">
<BasicDragVerify ref="el5" @success="handleSuccess">
<template v-slot:text="isPassing">
<template #text="isPassing">
<div v-if="isPassing">
<BugOutlined />
成功
......
<template>
<div class="p-4">
<BasicTable
title="基础示例"
titleHelpMessage="温馨提醒"
:columns="columns"
:dataSource="data"
:canResize="canResize"
:loading="loading"
:striped="striped"
:bordered="border"
:pagination="{ pageSize: 20 }"
>
<template #toolbar>
<a-button type="primary" @click="toggleCanResize">
{{ !canResize ? '自适应高度' : '取消自适应' }}
</a-button>
<a-button type="primary" @click="toggleBorder">
{{ !border ? '显示边框' : '隐藏边框' }}
</a-button>
<a-button type="primary" @click="toggleLoading"> 开启loading </a-button>
<a-button type="primary" @click="toggleStriped">
{{ !striped ? '显示斑马纹' : '隐藏斑马纹' }}
</a-button>
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
import { BasicTable } from '/@/components/Table';
import { getBasicColumns, getBasicData } from './tableData';
export default defineComponent({
components: { BasicTable },
setup() {
const canResize = ref(false);
const loading = ref(false);
const striped = ref(true);
const border = ref(true);
function toggleCanResize() {
canResize.value = !canResize.value;
}
function toggleStriped() {
striped.value = !striped.value;
}
function toggleLoading() {
loading.value = true;
setTimeout(() => {
loading.value = false;
}, 3000);
}
function toggleBorder() {
border.value = !border.value;
}
return {
columns: getBasicColumns(),
data: getBasicData(),
canResize,
loading,
striped,
border,
toggleStriped,
toggleCanResize,
toggleLoading,
toggleBorder,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #id="{ record }"> ID: {{ record.id }} </template>
<template #no="{ record }"
><Tag color="green">{{ record.no }}</Tag>
</template>
<template #img>
<TableImg
:imgList="['https://picsum.photos/id/66/346/216', 'https://picsum.photos/id/67/346/216']"
/>
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable, BasicColumn, TableImg } from '/@/components/Table';
import { Tag } from 'ant-design-vue';
import { demoListApi } from '/@/api/demo/table';
const columns: BasicColumn[] = [
{
title: 'ID',
dataIndex: 'id',
slots: { customRender: 'id' },
},
{
title: '姓名',
dataIndex: 'name',
width: 120,
},
{
title: '头像',
dataIndex: 'img',
width: 120,
slots: { customRender: 'img' },
},
{
title: '地址',
dataIndex: 'address',
},
{
title: '编号',
dataIndex: 'no',
slots: { customRender: 'no' },
},
{
title: '开始时间',
dataIndex: 'beginTime',
},
{
title: '结束时间',
dataIndex: 'endTime',
},
];
export default defineComponent({
components: { BasicTable, TableImg, Tag },
setup() {
const [registerTable] = useTable({
title: '自定义列内容',
api: demoListApi,
columns: columns,
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #customId>
<EditTableHeaderIcon title="Id" />
</template>
<template #customName>
<EditTableHeaderIcon title="姓名" />
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import {
BasicTable,
useTable,
BasicColumn,
renderEditableCell,
EditTableHeaderIcon,
} from '/@/components/Table';
import { demoListApi } from '/@/api/demo/table';
const columns: BasicColumn[] = [
{
// title: 'ID',
dataIndex: 'id',
slots: { title: 'customId' },
customRender: renderEditableCell({ dataIndex: 'id' }),
},
{
// title: '姓名',
dataIndex: 'name',
slots: { title: 'customName' },
customRender: renderEditableCell({
dataIndex: 'name',
}),
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
},
];
export default defineComponent({
components: { BasicTable, EditTableHeaderIcon },
setup() {
const [registerTable] = useTable({
title: '可编辑单元格示例',
api: demoListApi,
columns: columns,
showIndexColumn: false,
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #expandedRowRender="{ record }">
<span>No: {{ record.no }} </span>
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getBasicColumns } from './tableData';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
const [registerTable] = useTable({
title: '可展开表格',
api: demoListApi,
titleHelpMessage: '不能与scroll共用',
columns: getBasicColumns(),
rowKey: 'id',
canResize: false,
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #toolbar>
<a-button type="primary" @click="handleReloadCurrent"> 刷新当前页 </a-button>
<a-button type="primary" @click="handleReload"> 刷新并返回第一页 </a-button>
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getBasicColumns } from './tableData';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
const [registerTable, { reload }] = useTable({
title: '远程加载示例',
api: demoListApi,
columns: getBasicColumns(),
});
function handleReloadCurrent() {
reload();
// reload({
// searchInfo: 'xxx',
// });
}
function handleReload() {
reload({
page: 1,
});
}
return {
registerTable,
handleReloadCurrent,
handleReload,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #action>
<TableAction
:actions="[
{
label: '删除',
props: {
onClick: handleDelete,
},
},
]"
:dropDownActions="[
{
label: '启用',
props: {
onClick: handleOpen,
},
},
]"
/>
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable, BasicColumn, TableAction } from '/@/components/Table';
import { demoListApi } from '/@/api/demo/table';
const columns: BasicColumn[] = [
{
title: 'ID',
dataIndex: 'id',
fixed: 'left',
width: 280,
},
{
title: '姓名',
dataIndex: 'name',
width: 260,
},
{
title: '地址',
dataIndex: 'address',
width: 260,
},
{
title: '编号',
dataIndex: 'no',
width: 300,
},
{
title: '开始时间',
width: 200,
dataIndex: 'beginTime',
},
{
title: '结束时间',
dataIndex: 'endTime',
width: 200,
},
];
export default defineComponent({
components: { BasicTable, TableAction },
setup() {
const [registerTable] = useTable({
title: 'TableAction组件及固定列示例',
api: demoListApi,
columns: columns,
rowSelection: { type: 'radio' },
actionColumn: {
width: 160,
title: 'Action',
dataIndex: 'action',
slots: { customRender: 'action' },
},
});
function handleDelete() {
console.log('点击了删除');
}
function handleOpen() {
console.log('点击了启用');
}
return {
registerTable,
handleDelete,
handleOpen,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable">
<template #customTitle>
<span>
姓名
<BaseHelp class="ml-2" text="姓名" />
</span>
</template>
<template #customAddress>
地址
<FormOutlined class="ml-2" />
</template>
</BasicTable>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getCustomHeaderColumns } from './tableData';
import { FormOutlined } from '@ant-design/icons-vue';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable, FormOutlined },
setup() {
const [registerTable] = useTable({
title: '定高/头部自定义',
api: demoListApi,
columns: getCustomHeaderColumns(),
canResize: false,
scroll: { y: 100 },
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getBasicColumns } from './tableData';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
function handleSummary(tableData: any[]) {
const totalNo = tableData.reduce((prev, next) => {
prev += next.no;
return prev;
}, 0);
return [
{
_row: '合计',
_index: '平均值',
no: totalNo,
},
{
_row: '合计',
_index: '平均值',
no: totalNo,
},
];
}
const [registerTable] = useTable({
title: '表尾行合计示例',
api: demoListApi,
rowSelection: { type: 'checkbox' },
columns: getBasicColumns(),
showSummary: true,
summaryFunc: handleSummary,
scroll: { x: 2000 },
canResize: false,
});
return {
registerTable,
};
},
});
</script>
<template>
<BasicTable @register="registerTable" />
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getBasicColumns, getFormConfig } from './tableData';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
const [registerTable] = useTable({
title: '开启搜索区域',
api: demoListApi,
columns: getBasicColumns(),
useSearchForm: true,
formConfig: getFormConfig(),
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getMergeHeaderColumns } from './tableData';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
const [registerTable] = useTable({
title: '多级表头示例',
api: demoListApi,
columns: getMergeHeaderColumns(),
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable @register="registerTable" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getMultipleHeaderColumns } from './tableData';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
const [registerTable] = useTable({
title: '多级表头示例',
api: demoListApi,
columns: getMultipleHeaderColumns(),
});
return {
registerTable,
};
},
});
</script>
<template>
<div class="p-4">
<div class="mb-4">
<a-button class="mr-2" @click="reloadTable">还原</a-button>
<a-button class="mr-2" @click="changeLoading">开启loading</a-button>
<a-button class="mr-2" @click="changeColumns">更改Columns</a-button>
<a-button class="mr-2" @click="getColumn">获取Columns</a-button>
<a-button class="mr-2" @click="getTableData">获取表格数据</a-button>
<a-button class="mr-2" @click="setPaginationInfo">跳转到第2页</a-button>
</div>
<div class="mb-4">
<a-button class="mr-2" @click="getSelectRowList">获取选中行</a-button>
<a-button class="mr-2" @click="getSelectRowKeyList">获取选中行Key</a-button>
<a-button class="mr-2" @click="setSelectedRowKeyList">设置选中行</a-button>
<a-button class="mr-2" @click="clearSelect">清空选中行</a-button>
<a-button class="mr-2" @click="getPagination">获取分页信息</a-button>
</div>
<BasicTable
:canResize="false"
title="RefTable示例"
titleHelpMessage="使用Ref调用表格内方法"
ref="tableRef"
:api="api"
:columns="columns"
rowKey="id"
:rowSelection="{ type: 'checkbox' }"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, unref } from 'vue';
import { BasicTable, TableActionType } from '/@/components/Table';
import { getBasicColumns, getBasicShortColumns } from './tableData';
import { useMessage } from '/@/hooks/web/useMessage';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
const tableRef = ref<Nullable<TableActionType>>(null);
const { createMessage } = useMessage();
function getTableAction() {
const tableAction = unref(tableRef);
if (!tableAction) {
throw new Error('tableAction is null');
}
return tableAction;
}
function changeLoading() {
getTableAction().setLoading(true);
setTimeout(() => {
getTableAction().setLoading(false);
}, 1000);
}
function changeColumns() {
getTableAction().setColumns(getBasicShortColumns());
}
function reloadTable() {
getTableAction().setColumns(getBasicColumns());
getTableAction().reload({
page: 1,
});
}
function getColumn() {
createMessage.info('请在控制台查看!');
console.log(getTableAction().getColumns());
}
function getTableData() {
createMessage.info('请在控制台查看!');
console.log(getTableAction().getDataSource());
}
function getPagination() {
createMessage.info('请在控制台查看!');
console.log(getTableAction().getPaginationRef());
}
function setPaginationInfo() {
getTableAction().setPagination({
current: 2,
});
getTableAction().reload();
}
function getSelectRowList() {
createMessage.info('请在控制台查看!');
console.log(getTableAction().getSelectRows());
}
function getSelectRowKeyList() {
createMessage.info('请在控制台查看!');
console.log(getTableAction().getSelectRowKeys());
}
function setSelectedRowKeyList() {
getTableAction().setSelectedRowKeys(['0', '1', '2']);
}
function clearSelect() {
getTableAction().clearSelectedRowKeys();
}
return {
tableRef,
api: demoListApi,
columns: getBasicColumns(),
changeLoading,
changeColumns,
reloadTable,
getColumn,
getTableData,
getPagination,
setPaginationInfo,
getSelectRowList,
getSelectRowKeyList,
setSelectedRowKeyList,
clearSelect,
};
},
});
</script>
<template>
<div class="p-4">
<BasicTable
:rowSelection="{ type: 'checkbox' }"
:isTreeTable="true"
title="树形表格"
titleHelpMessage="树形组件不能和序列号列同时存在"
:columns="columns"
:dataSource="data"
rowKey="id"
:indentSize="20"
/>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable } from '/@/components/Table';
import { getBasicColumns, getTreeTableData } from './tableData';
export default defineComponent({
components: { BasicTable },
setup() {
return {
columns: getBasicColumns(),
data: getTreeTableData(),
};
},
});
</script>
<template>
<div class="p-4">
<div class="mb-4">
<a-button class="mr-2" @click="reloadTable">还原</a-button>
<a-button class="mr-2" @click="changeLoading">开启loading</a-button>
<a-button class="mr-2" @click="changeColumns">更改Columns</a-button>
<a-button class="mr-2" @click="getColumn">获取Columns</a-button>
<a-button class="mr-2" @click="getTableData">获取表格数据</a-button>
<a-button class="mr-2" @click="setPaginationInfo">跳转到第2页</a-button>
</div>
<div class="mb-4">
<a-button class="mr-2" @click="getSelectRowList">获取选中行</a-button>
<a-button class="mr-2" @click="getSelectRowKeyList">获取选中行Key</a-button>
<a-button class="mr-2" @click="setSelectedRowKeyList">设置选中行</a-button>
<a-button class="mr-2" @click="clearSelect">清空选中行</a-button>
<a-button class="mr-2" @click="getPagination">获取分页信息</a-button>
</div>
<BasicTable @register="registerTable" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { BasicTable, useTable } from '/@/components/Table';
import { getBasicColumns, getBasicShortColumns } from './tableData';
import { useMessage } from '/@/hooks/web/useMessage';
import { demoListApi } from '/@/api/demo/table';
export default defineComponent({
components: { BasicTable },
setup() {
const { createMessage } = useMessage();
const [
registerTable,
{
setLoading,
setColumns,
getColumns,
getDataSource,
reload,
getPaginationRef,
setPagination,
getSelectRows,
getSelectRowKeys,
setSelectedRowKeys,
clearSelectedRowKeys,
},
] = useTable({
canResize: false,
title: 'useTable示例',
titleHelpMessage: '使用useTable调用表格内方法',
api: demoListApi,
columns: getBasicColumns(),
rowKey: 'id',
rowSelection: {
type: 'checkbox',
},
});
function changeLoading() {
setLoading(true);
setTimeout(() => {
setLoading(false);
}, 1000);
}
function changeColumns() {
setColumns(getBasicShortColumns());
}
function reloadTable() {
setColumns(getBasicColumns());
reload({
page: 1,
});
}
function getColumn() {
createMessage.info('请在控制台查看!');
console.log(getColumns());
}
function getTableData() {
createMessage.info('请在控制台查看!');
console.log(getDataSource());
}
function getPagination() {
createMessage.info('请在控制台查看!');
console.log(getPaginationRef());
}
function setPaginationInfo() {
setPagination({
current: 2,
});
reload();
}
function getSelectRowList() {
createMessage.info('请在控制台查看!');
console.log(getSelectRows());
}
function getSelectRowKeyList() {
createMessage.info('请在控制台查看!');
console.log(getSelectRowKeys());
}
function setSelectedRowKeyList() {
setSelectedRowKeys(['0', '1', '2']);
}
function clearSelect() {
clearSelectedRowKeys();
}
return {
registerTable,
changeLoading,
changeColumns,
reloadTable,
getColumn,
getTableData,
getPagination,
setPaginationInfo,
getSelectRowList,
getSelectRowKeyList,
setSelectedRowKeyList,
clearSelect,
};
},
});
</script>
import { FormProps, FormSchema } from '/@/components/Table';
import { BasicColumn } from '/@/components/Table/src/types/table';
export function getBasicColumns(): BasicColumn[] {
return [
{
title: 'ID',
width: 150,
dataIndex: 'id',
},
{
title: '姓名',
dataIndex: 'name',
width: 120,
},
{
title: '地址',
dataIndex: 'address',
},
{
title: '编号',
dataIndex: 'no',
width: 80,
},
{
title: '开始时间',
dataIndex: 'beginTime',
},
{
title: '结束时间',
sorter: true,
dataIndex: 'endTime',
},
];
}
export function getBasicShortColumns(): BasicColumn[] {
return [
{
title: 'ID',
width: 150,
dataIndex: 'id',
},
{
title: '姓名',
dataIndex: 'name',
width: 120,
},
{
title: '地址',
dataIndex: 'address',
},
{
title: '编号',
dataIndex: 'no',
width: 80,
},
];
}
export function getMultipleHeaderColumns(): BasicColumn[] {
return [
{
title: 'ID',
dataIndex: 'id',
width: 200,
},
{
title: '姓名',
dataIndex: 'name',
width: 120,
},
{
title: '地址',
dataIndex: 'address',
sorter: true,
children: [
{
title: '编号',
dataIndex: 'no',
width: 120,
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
},
{
title: '开始时间',
dataIndex: 'beginTime',
width: 120,
},
{
title: '结束时间',
dataIndex: 'endTime',
width: 120,
},
],
},
];
}
export function getCustomHeaderColumns(): BasicColumn[] {
return [
{
title: 'ID',
dataIndex: 'id',
width: 200,
},
{
// title: '姓名',
dataIndex: 'name',
width: 120,
slots: { title: 'customTitle' },
},
{
// title: '地址',
dataIndex: 'address',
slots: { title: 'customAddress' },
sorter: true,
},
{
title: '编号',
dataIndex: 'no',
width: 120,
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
},
{
title: '开始时间',
dataIndex: 'beginTime',
width: 120,
},
{
title: '结束时间',
dataIndex: 'endTime',
width: 120,
},
];
}
const renderContent = ({ text, index }: { text: any; index: number }) => {
const obj: any = {
children: text,
attrs: {},
};
if (index === 9) {
obj.attrs.colSpan = 0;
}
return obj;
};
export function getMergeHeaderColumns(): BasicColumn[] {
return [
{
title: 'ID',
dataIndex: 'id',
width: 300,
customRender: renderContent,
},
{
title: '姓名',
dataIndex: 'name',
width: 300,
customRender: renderContent,
},
{
title: '地址',
dataIndex: 'address',
colSpan: 2,
width: 120,
sorter: true,
customRender: ({ text, index }: { text: any; index: number }) => {
const obj: any = {
children: text,
attrs: {},
};
if (index === 2) {
obj.attrs.rowSpan = 2;
}
if (index === 3) {
obj.attrs.colSpan = 0;
}
return obj;
},
},
{
title: '编号',
dataIndex: 'no',
colSpan: 0,
filters: [
{ text: 'Male', value: 'male' },
{ text: 'Female', value: 'female' },
],
customRender: renderContent,
},
{
title: '开始时间',
dataIndex: 'beginTime',
width: 200,
customRender: renderContent,
},
{
title: '结束时间',
dataIndex: 'endTime',
width: 200,
customRender: renderContent,
},
];
}
export const getAdvanceSchema = (itemNumber = 6): FormSchema[] => {
const arr: any = [];
for (let index = 0; index < itemNumber; index++) {
arr.push({
field: `field${index}`,
label: `字段${index}`,
component: 'Input',
colProps: {
xl: 12,
xxl: 8,
},
});
}
return arr;
};
export function getFormConfig(): Partial<FormProps> {
return {
labelWidth: 100,
schemas: getAdvanceSchema(6),
};
}
export function getBasicData() {
const data: any = (() => {
const arr: any = [];
for (let index = 0; index < 40; index++) {
arr.push({
id: `${index}`,
name: 'John Brown',
age: `1${index}`,
no: `${index + 10}`,
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park',
beginTime: new Date().toLocaleString(),
endTime: new Date().toLocaleString(),
});
}
return arr;
})();
return data;
}
export function getTreeTableData() {
const data: any = (() => {
const arr: any = [];
for (let index = 0; index < 40; index++) {
arr.push({
id: `${index}`,
name: 'John Brown',
age: `1${index}`,
no: `${index + 10}`,
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park',
beginTime: new Date().toLocaleString(),
endTime: new Date().toLocaleString(),
children: [
{
id: `l2-${index}`,
name: 'John Brown',
age: `1${index}`,
no: `${index + 10}`,
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park',
beginTime: new Date().toLocaleString(),
endTime: new Date().toLocaleString(),
children: [
{
id: `l3-${index}`,
name: 'John Brown',
age: `1${index}`,
no: `${index + 10}`,
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park',
beginTime: new Date().toLocaleString(),
endTime: new Date().toLocaleString(),
},
],
},
],
});
}
return arr;
})();
return data;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册