diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
index f66e4b22..04d81a13 100644
--- a/.github/release-drafter.yml
+++ b/.github/release-drafter.yml
@@ -30,5 +30,5 @@ categories:
- 'workflow'
change-template: '- $TITLE (#$NUMBER) @$AUTHOR'
template: |
- # Changes
+ # What's Changed
$CHANGES
diff --git a/README.md b/README.md
index a9d8eb00..c447b095 100644
--- a/README.md
+++ b/README.md
@@ -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)
-
@@ -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
## 加入我们
diff --git a/mock/_createProductionServer.ts b/mock/_createProductionServer.ts
index a81cebf5..003a0984 100644
--- a/mock/_createProductionServer.ts
+++ b/mock/_createProductionServer.ts
@@ -1,7 +1,8 @@
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]);
}
diff --git a/mock/_util.ts b/mock/_util.ts
index 6e4dc0ef..bf95ec5c 100644
--- a/mock/_util.ts
+++ b/mock/_util.ts
@@ -9,12 +9,19 @@ export function resultSuccess(result: T, { message = 'ok' } = {}) {
};
}
-export function resultPageSuccess(items: T[], total: number, { message = 'ok' } = {}) {
+export function resultPageSuccess(
+ 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',
diff --git a/mock/demo/table-demo.ts b/mock/demo/table-demo.ts
new file mode 100644
index 00000000..754007b9
--- /dev/null
+++ b/mock/demo/table-demo.ts
@@ -0,0 +1,30 @@
+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[];
diff --git a/src/App.vue b/src/App.vue
index 74625242..586f6f8a 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,10 +1,5 @@
-
+
@@ -28,10 +23,9 @@
useInitAppConfigStore();
useListenerNetWork();
createBreakpointListen();
- const { renderEmpty, transformCellText } = useConfigProvider();
+ const { transformCellText } = useConfigProvider();
const { on: lockOn } = useLockPage();
return {
- renderEmpty,
transformCellText,
zhCN,
lockOn,
diff --git a/src/api/demo/model/tableModel.ts b/src/api/demo/model/tableModel.ts
new file mode 100644
index 00000000..09b6a303
--- /dev/null
+++ b/src/api/demo/model/tableModel.ts
@@ -0,0 +1,20 @@
+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;
diff --git a/src/api/demo/table.ts b/src/api/demo/table.ts
new file mode 100644
index 00000000..e38208e7
--- /dev/null
+++ b/src/api/demo/table.ts
@@ -0,0 +1,17 @@
+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({
+ url: Api.DEMO_LIST,
+ method: 'GET',
+ params,
+ });
+}
diff --git a/src/api/model/baseModel.ts b/src/api/model/baseModel.ts
new file mode 100644
index 00000000..fc040801
--- /dev/null
+++ b/src/api/model/baseModel.ts
@@ -0,0 +1,9 @@
+export interface BasicPageParams {
+ page: number;
+ pageSize: number;
+}
+
+export interface BasicFetchResult {
+ items: T;
+ total: number;
+}
diff --git a/src/assets/images/lock-page.png b/src/assets/images/lock-page.png
deleted file mode 100644
index 645c7e91..00000000
Binary files a/src/assets/images/lock-page.png and /dev/null differ
diff --git a/src/assets/images/page_null.png b/src/assets/images/page_null.png
deleted file mode 100644
index 5e7cb211..00000000
Binary files a/src/assets/images/page_null.png and /dev/null differ
diff --git a/src/components/Basic/index.ts b/src/components/Basic/index.ts
index b7b24919..3e792fc4 100644
--- a/src/components/Basic/index.ts
+++ b/src/components/Basic/index.ts
@@ -1,4 +1,3 @@
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';
diff --git a/src/components/Basic/src/BasicArrow.vue b/src/components/Basic/src/BasicArrow.vue
index f0c6c467..eb816d5e 100644
--- a/src/components/Basic/src/BasicArrow.vue
+++ b/src/components/Basic/src/BasicArrow.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;
+ }
}
}
diff --git a/src/components/Basic/src/BasicEmpty.vue b/src/components/Basic/src/BasicEmpty.vue
deleted file mode 100644
index f0760d15..00000000
--- a/src/components/Basic/src/BasicEmpty.vue
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
-
diff --git a/src/components/Button/index.vue b/src/components/Button/index.vue
index 5d4f136a..b3fa3f73 100644
--- a/src/components/Button/index.vue
+++ b/src/components/Button/index.vue
@@ -1,7 +1,7 @@
diff --git a/src/components/Container/src/collapse/CollapseContainer.vue b/src/components/Container/src/collapse/CollapseContainer.vue
index 72813ac6..a3eb13bd 100644
--- a/src/components/Container/src/collapse/CollapseContainer.vue
+++ b/src/components/Container/src/collapse/CollapseContainer.vue
@@ -6,7 +6,7 @@
-
+
diff --git a/src/components/Form/src/BasicForm.vue b/src/components/Form/src/BasicForm.vue
index 11c7d071..bd153eca 100644
--- a/src/components/Form/src/BasicForm.vue
+++ b/src/components/Form/src/BasicForm.vue
@@ -9,8 +9,8 @@
:allDefaultValues="getAllDefaultValues"
:formModel="formModel"
>
-
-
+
+
diff --git a/src/components/Table/index.ts b/src/components/Table/index.ts
new file mode 100644
index 00000000..4a3e7638
--- /dev/null
+++ b/src/components/Table/index.ts
@@ -0,0 +1,13 @@
+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';
diff --git a/src/components/Table/src/BasicTable.vue b/src/components/Table/src/BasicTable.vue
new file mode 100644
index 00000000..dd5e7652
--- /dev/null
+++ b/src/components/Table/src/BasicTable.vue
@@ -0,0 +1,285 @@
+
+
+
+
diff --git a/src/components/Table/src/componentMap.ts b/src/components/Table/src/componentMap.ts
new file mode 100644
index 00000000..cf12a5f1
--- /dev/null
+++ b/src/components/Table/src/componentMap.ts
@@ -0,0 +1,26 @@
+import { Component } from 'vue';
+
+import { Input, Select, Checkbox, InputNumber, Switch } from 'ant-design-vue';
+
+import { ComponentType } from './types/componentType';
+
+const componentMap = new Map
();
+
+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 };
diff --git a/src/components/Table/src/components/CellResize.tsx b/src/components/Table/src/components/CellResize.tsx
new file mode 100644
index 00000000..e485074e
--- /dev/null
+++ b/src/components/Table/src/components/CellResize.tsx
@@ -0,0 +1,72 @@
+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(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 {getSlot(slots, 'default')} | ;
+ }
+ 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 (
+
+ {getSlot(slots, 'default')}
+
+ |
+ );
+ };
+ },
+});
diff --git a/src/components/Table/src/components/EditTableHeaderIcon.vue b/src/components/Table/src/components/EditTableHeaderIcon.vue
new file mode 100644
index 00000000..2666a279
--- /dev/null
+++ b/src/components/Table/src/components/EditTableHeaderIcon.vue
@@ -0,0 +1,21 @@
+
+
+ {{ title }}
+
+
+
+
diff --git a/src/components/Table/src/components/TableAction.tsx b/src/components/Table/src/components/TableAction.tsx
new file mode 100644
index 00000000..f7996582
--- /dev/null
+++ b/src/components/Table/src/components/TableAction.tsx
@@ -0,0 +1,144 @@
+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,
+ default: null,
+ },
+ dropDownActions: {
+ type: Array as PropType,
+ default: null,
+ },
+ },
+ setup(props) {
+ // 增加按钮的TYPE和COLOR
+ return () => {
+ const { dropDownActions = [], actions } = props;
+ return (
+
+ {actions &&
+ actions.length &&
+ actions.map((action, index) => {
+ const {
+ disabled = false,
+ label,
+ props,
+ icon,
+ color = '',
+ type = 'link',
+ popConfirm = null,
+ } = action;
+ const button = (
+
+ );
+ if (popConfirm !== null) {
+ const {
+ title,
+ okText = '确定',
+ cancelText = '取消',
+ confirm = () => {},
+ cancel = () => {},
+ icon = '',
+ } = popConfirm;
+ return (
+
+ {() => button}
+
+ );
+ }
+ return button;
+ })}
+ {dropDownActions && dropDownActions.length && (
+
+ {{
+ default: () => (
+
+ ),
+ overlay: () => {
+ return (
+
+ );
+ },
+ }}
+
+ )}
+
+ );
+ };
+ },
+});
diff --git a/src/components/Table/src/components/TableImg.vue b/src/components/Table/src/components/TableImg.vue
new file mode 100644
index 00000000..97055795
--- /dev/null
+++ b/src/components/Table/src/components/TableImg.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/Table/src/components/TableTitle.vue b/src/components/Table/src/components/TableTitle.vue
new file mode 100644
index 00000000..4d98075e
--- /dev/null
+++ b/src/components/Table/src/components/TableTitle.vue
@@ -0,0 +1,41 @@
+
+
+ {{ tableTitle }}
+
+
+
diff --git a/src/components/Table/src/components/renderEditableCell.tsx b/src/components/Table/src/components/renderEditableCell.tsx
new file mode 100644
index 00000000..125e0cd6
--- /dev/null
+++ b/src/components/Table/src/components/renderEditableCell.tsx
@@ -0,0 +1,153 @@
+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,
+ default: '',
+ },
+ componentProps: {
+ type: Object as PropType,
+ default: null,
+ },
+
+ dataKey: {
+ type: String as PropType,
+ default: '',
+ },
+
+ dataIndex: {
+ type: String as PropType,
+ default: '',
+ },
+
+ component: {
+ type: String as PropType,
+ default: 'Input',
+ },
+ },
+ setup(props, { attrs }) {
+ const table = injectTable();
+ const elRef = ref(null);
+
+ const isEditRef = ref(false);
+ const currentValueRef = ref('');
+
+ 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 (
+
+ {unref(isEditRef) && (
+
+ {() => (
+
+ )}
+
+ )}
+
+ {!unref(isEditRef) && (
+
+ {value}
+
+
+ )}
+
+ );
+ };
+ },
+});
+
+export function renderEditableCell({
+ dataIndex,
+ component,
+ componentOn = {},
+ componentProps = {},
+}: RenderEditableCellParams) {
+ return ({ text, record }: { text: string; record: any }) => {
+ return (
+
+ );
+ };
+}
diff --git a/src/components/Table/src/components/renderExpandIcon.tsx b/src/components/Table/src/components/renderExpandIcon.tsx
new file mode 100644
index 00000000..099ddaab
--- /dev/null
+++ b/src/components/Table/src/components/renderExpandIcon.tsx
@@ -0,0 +1,15 @@
+import { BasicArrow } from '/@/components/Basic';
+
+export default () => {
+ return (props: any) => {
+ return (
+ {
+ props.onExpand(props.record, e);
+ }}
+ expand={props.expanded}
+ class="right"
+ />
+ );
+ };
+};
diff --git a/src/components/Table/src/components/renderFooter.tsx b/src/components/Table/src/components/renderFooter.tsx
new file mode 100644
index 00000000..67acc322
--- /dev/null
+++ b/src/components/Table/src/components/renderFooter.tsx
@@ -0,0 +1,64 @@
+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;
+ summaryFunc: any;
+ rowKey?: string;
+ dataSourceRef: ComputedRef;
+ rowSelectionRef: ComputedRef | 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 (
+
+ );
+};
diff --git a/src/components/Table/src/components/renderTitle.tsx b/src/components/Table/src/components/renderTitle.tsx
new file mode 100644
index 00000000..c232f11b
--- /dev/null
+++ b/src/components/Table/src/components/renderTitle.tsx
@@ -0,0 +1,14 @@
+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 && ) || (
+
+ )}
+ {slots.toolbar && {getSlot(slots, 'toolbar')}
}
+ >
+ );
+};
diff --git a/src/components/Table/src/const.ts b/src/components/Table/src/const.ts
new file mode 100644
index 00000000..beb2d8a3
--- /dev/null
+++ b/src/components/Table/src/const.ts
@@ -0,0 +1,12 @@
+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',
+};
diff --git a/src/components/Table/src/hooks/useColumns.ts b/src/components/Table/src/hooks/useColumns.ts
new file mode 100644
index 00000000..77c11765
--- /dev/null
+++ b/src/components/Table/src/hooks/useColumns.ts
@@ -0,0 +1,123 @@
+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,
+ getPaginationRef: ComputedRef
+) {
+ const { propsRef } = useProps(refProps);
+
+ const columnsRef = (ref(unref(propsRef).columns) as unknown) as Ref;
+ const cacheColumnsRef = (ref(unref(propsRef).columns) as unknown) as Ref;
+
+ 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 };
+}
diff --git a/src/components/Table/src/hooks/useDataSource.ts b/src/components/Table/src/hooks/useDataSource.ts
new file mode 100644
index 00000000..301ec36b
--- /dev/null
+++ b/src/components/Table/src/hooks/useDataSource.ts
@@ -0,0 +1,148 @@
+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;
+ setPagination: (info: Partial) => void;
+ loadingRef: Ref;
+ getFieldsValue: () => {
+ [field: string]: any;
+ };
+}
+export function useDataSource(
+ refProps: ComputedRef,
+ { getPaginationRef, setPagination, loadingRef, getFieldsValue }: ActionType,
+ emit: EmitType
+) {
+ const { propsRef } = useProps(refProps);
+
+ const dataSourceRef = ref([]);
+
+ 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 };
+}
diff --git a/src/components/Table/src/hooks/useLoading.ts b/src/components/Table/src/hooks/useLoading.ts
new file mode 100644
index 00000000..b92bbbdb
--- /dev/null
+++ b/src/components/Table/src/hooks/useLoading.ts
@@ -0,0 +1,15 @@
+import { watch, ref, ComputedRef, unref } from 'vue';
+import { BasicTableProps } from '../types/table';
+import { useProps } from './useProps';
+export function useLoading(refProps: ComputedRef) {
+ const { propsRef } = useProps(refProps);
+
+ const loadingRef = ref(unref(propsRef).loading);
+ watch(
+ () => unref(propsRef).loading,
+ (v: boolean) => {
+ loadingRef.value = v;
+ }
+ );
+ return { loadingRef };
+}
diff --git a/src/components/Table/src/hooks/usePagination.tsx b/src/components/Table/src/hooks/usePagination.tsx
new file mode 100644
index 00000000..368db5ee
--- /dev/null
+++ b/src/components/Table/src/hooks/usePagination.tsx
@@ -0,0 +1,53 @@
+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) {
+ const configRef = ref({});
+ 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 ;
+ } else if (type === 'next') {
+ if (page === 1) {
+ return null;
+ }
+ return ;
+ }
+ return originalElement;
+ },
+ showQuickJumper: true,
+ ...(isBoolean(pagination) ? {} : pagination),
+ ...unref(configRef),
+ };
+ });
+
+ function setPagination(info: Partial) {
+ configRef.value = {
+ ...unref(getPaginationRef),
+ ...info,
+ };
+ }
+ return { getPaginationRef, setPagination };
+}
diff --git a/src/components/Table/src/hooks/useProps.ts b/src/components/Table/src/hooks/useProps.ts
new file mode 100644
index 00000000..109a1000
--- /dev/null
+++ b/src/components/Table/src/hooks/useProps.ts
@@ -0,0 +1,29 @@
+/*
+ * @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[>) {
+ const propsRef = (ref(unref(props)) as unknown) as Ref;
+ watch(
+ () => props.value,
+ (v) => {
+ propsRef.value = unref(v);
+ },
+ {
+ immediate: false,
+ }
+ );
+ return { propsRef };
+}
diff --git a/src/components/Table/src/hooks/useProvinceTable.ts b/src/components/Table/src/hooks/useProvinceTable.ts
new file mode 100644
index 00000000..726fb7c3
--- /dev/null
+++ b/src/components/Table/src/hooks/useProvinceTable.ts
@@ -0,0 +1,12 @@
+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;
+}
diff --git a/src/components/Table/src/hooks/useRowSelection.ts b/src/components/Table/src/hooks/useRowSelection.ts
new file mode 100644
index 00000000..3fab5d8e
--- /dev/null
+++ b/src/components/Table/src/hooks/useRowSelection.ts
@@ -0,0 +1,63 @@
+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, emit: EmitType) {
+ const { propsRef } = useProps(refProps);
+
+ const selectedRowKeysRef = ref([]);
+ const selectedRowRef = ref([]);
+
+ const getRowSelectionRef = computed((): TableRowSelection | 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,
+ };
+}
diff --git a/src/components/Table/src/hooks/useTable.ts b/src/components/Table/src/hooks/useTable.ts
new file mode 100644
index 00000000..a613e764
--- /dev/null
+++ b/src/components/Table/src/hooks/useTable.ts
@@ -0,0 +1,88 @@
+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
+): [(instance: TableActionType) => void, TableActionType] {
+ if (!getCurrentInstance()) {
+ throw new Error('Please put useTable function in the setup function!');
+ }
+
+ const tableRef = ref(null);
+ const loadedRef = ref(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) => {
+ 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) => {
+ 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];
+}
diff --git a/src/components/Table/src/hooks/useTableScroll.ts b/src/components/Table/src/hooks/useTableScroll.ts
new file mode 100644
index 00000000..eb01544e
--- /dev/null
+++ b/src/components/Table/src/hooks/useTableScroll.ts
@@ -0,0 +1,134 @@
+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, tableElRef: Ref) {
+ const { propsRef } = useProps(refProps);
+
+ const tableHeightRef: Ref = 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 };
+}
diff --git a/src/components/Table/src/props.ts b/src/components/Table/src/props.ts
new file mode 100644
index 00000000..87dfc831
--- /dev/null
+++ b/src/components/Table/src/props.ts
@@ -0,0 +1,155 @@
+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,
+ default: true,
+ },
+ striped: {
+ type: Boolean as PropType,
+ default: true,
+ },
+ showSummary: {
+ type: Boolean as PropType,
+ default: false,
+ },
+
+ summaryFunc: {
+ type: [Function, Array] as PropType<(...arg: any[]) => any[]>,
+ default: null,
+ },
+
+ canColDrag: {
+ type: Boolean as PropType,
+ default: true,
+ },
+ isTreeTable: {
+ type: Boolean as PropType,
+ default: false,
+ },
+ api: {
+ type: Function as PropType<(...arg: any[]) => Promise>,
+ default: null,
+ },
+ beforeFetch: {
+ type: Function as PropType,
+ default: null,
+ },
+ afterFetch: {
+ type: Function as PropType,
+ default: null,
+ },
+ handleSearchInfoFn: {
+ type: Function as PropType,
+ default: null,
+ },
+ fetchSetting: {
+ type: Object as PropType,
+ default: () => {
+ return FETCH_SETTING;
+ },
+ },
+ // 立即请求接口
+ immediate: { type: Boolean as PropType, default: true },
+
+ emptyDataIsShowTable: {
+ type: Boolean as PropType,
+ default: true,
+ },
+ // 额外的请求参数
+ searchInfo: {
+ type: Object as PropType,
+ default: null,
+ },
+ // 使用搜索表单
+ useSearchForm: {
+ type: Boolean as PropType,
+ default: false,
+ },
+ // 表单配置
+ formConfig: {
+ type: Object as PropType>,
+ default: null,
+ },
+ columns: {
+ type: [Array] as PropType,
+ default: null,
+ },
+ showIndexColumn: {
+ type: Boolean as PropType,
+ default: true,
+ },
+ indexColumnProps: {
+ type: Object as PropType,
+ default: null,
+ },
+ actionColumn: {
+ type: Object as PropType,
+ default: null,
+ },
+ ellipsis: {
+ type: Boolean as PropType,
+ default: true,
+ },
+ canResize: {
+ type: Boolean as PropType,
+ default: true,
+ },
+ clearSelectOnPageChange: {
+ type: Boolean as PropType,
+ default: false,
+ },
+ resizeHeightOffset: {
+ type: Number as PropType,
+ default: 0,
+ },
+ rowSelection: {
+ type: Object as PropType | null>,
+ default: null,
+ },
+ title: {
+ type: [String, Function] as PropType any)>,
+ default: null,
+ },
+ titleHelpMessage: {
+ type: [String, Array] as PropType,
+ },
+ maxHeight: {
+ type: Number as PropType,
+ },
+ dataSource: {
+ type: Array as PropType,
+ default: null,
+ },
+ rowKey: {
+ type: [String, Function] as PropType string)>,
+ default: '',
+ },
+ bordered: {
+ type: Boolean as PropType,
+ default: true,
+ },
+ pagination: {
+ type: [Object, Boolean] as PropType,
+ default: null,
+ },
+
+ loading: {
+ type: Boolean as PropType,
+ default: false,
+ },
+ rowClassName: {
+ type: Function as PropType<(record: TableCustomRecord, index: number) => string>,
+ },
+
+ scroll: {
+ type: Object as PropType<{ x: number | true; y: number }>,
+ default: null,
+ },
+};
diff --git a/src/components/Table/src/style/editable-cell.less b/src/components/Table/src/style/editable-cell.less
new file mode 100644
index 00000000..1d1beb85
--- /dev/null
+++ b/src/components/Table/src/style/editable-cell.less
@@ -0,0 +1,41 @@
+@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;
+ }
+ }
+}
diff --git a/src/components/Table/src/style/index.less b/src/components/Table/src/style/index.less
new file mode 100644
index 00000000..99186439
--- /dev/null
+++ b/src/components/Table/src/style/index.less
@@ -0,0 +1,228 @@
+@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;
+ }
+}
diff --git a/src/components/Table/src/types/componentType.ts b/src/components/Table/src/types/componentType.ts
new file mode 100644
index 00000000..03f3adef
--- /dev/null
+++ b/src/components/Table/src/types/componentType.ts
@@ -0,0 +1,8 @@
+export type ComponentType =
+ | 'Input'
+ | 'InputPassword'
+ | 'InputNumber'
+ | 'Select'
+ | 'Checkbox'
+ | 'CheckboxGroup'
+ | 'Switch';
diff --git a/src/components/Table/src/types/pagination.ts b/src/components/Table/src/types/pagination.ts
new file mode 100644
index 00000000..448bbc4d
--- /dev/null
+++ b/src/components/Table/src/types/pagination.ts
@@ -0,0 +1,89 @@
+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;
+}
diff --git a/src/components/Table/src/types/table.ts b/src/components/Table/src/types/table.ts
new file mode 100644
index 00000000..1b19618c
--- /dev/null
+++ b/src/components/Table/src/types/table.ts
@@ -0,0 +1,315 @@
+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;
+ getSelectRows: () => any[];
+ clearSelectedRowKeys: () => void;
+ getSelectRowKeys: () => string[];
+ deleteSelectRowByKey: (key: string) => void;
+ setPagination: (info: Partial) => void;
+ setTableData: (values: any[]) => void;
+ getColumns: ({ ignoreIndex }?: GetColumnsParams) => BasicColumn[];
+ setColumns: (columns: BasicColumn[] | string[]) => void;
+ getDataSource: () => any[];
+ setLoading: (loading: boolean) => void;
+ setProps: (props: Partial) => 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 {
+ // 斑马纹
+ striped?: boolean;
+ // 是否自动生成key
+ autoCreateKey?: boolean;
+ // 计算合计行的方法
+ summaryFunc?: (...arg: any) => any[];
+ // 是否显示合计行
+ showSummary?: boolean;
+ // 是否可拖拽列
+ canColDrag?: boolean;
+ // 是否树表
+ isTreeTable?: boolean;
+ // 接口请求对象
+ api?: (...arg: any) => Promise;
+ // 请求之前处理参数
+ 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) => 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) => string;
+
+ /**
+ * Row selection config
+ * @type object
+ */
+ rowSelection?: TableRowSelection;
+
+ /**
+ * 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, 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>,
+ sorter: SorterResult,
+ extra: TableCurrentDataSource
+ ) => 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 extends ColumnProps {
+ children?: BasicColumn[];
+ //
+ flag?: 'INDEX' | 'DEFAULT' | 'CHECKBOX' | 'RADIO' | 'ACTION';
+}
diff --git a/src/components/Table/src/types/tableAction.ts b/src/components/Table/src/types/tableAction.ts
new file mode 100644
index 00000000..f62c3d4d
--- /dev/null
+++ b/src/components/Table/src/types/tableAction.ts
@@ -0,0 +1,19 @@
+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;
+}
diff --git a/src/design/ant/pagination.less b/src/design/ant/pagination.less
index 5ff4dfbe..348629a1 100644
--- a/src/design/ant/pagination.less
+++ b/src/design/ant/pagination.less
@@ -1,98 +1,96 @@
-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;
+ min-width: 20px;
+ line-height: 20px;
+ color: @border-color-shallow-dark;
+ border: 1px solid;
+ }
- .ant-pagination-prev,
- .ant-pagination-next {
- width: 20px;
- height: 20px;
- min-width: 20px;
- line-height: 17px;
- 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;
- }
- }
-
- .ant-pagination-item-active {
- background: @primary-color;
-
- a {
- color: @white;
- }
- }
-
- .ant-pagination-options {
- margin-left: 20px;
- }
-
- .ant-select-sm .ant-select-selection--single {
- height: 20px;
- }
-
- .ant-pagination-options,
- .ant-pagination-total-text,
- .ant-pagination-options-quick-jumper {
- height: 20px;
- line-height: 20px;
- }
-
- .ant-select-selection__rendered {
- height: 18px;
- line-height: 18px;
- }
-
- .ant-pagination-total-text,
- .ant-select-selection__rendered,
- .ant-select-dropdown-menu-item,
- .ant-pagination-options-quick-jumper {
- font-size: 13px;
- }
-
- .ant-pagination-options-quick-jumper input {
- width: 40px;
- height: 20px;
- margin: 0 6px;
- line-height: 20px;
- text-align: center;
- }
-
- .ant-pagination-jump-prev,
- .ant-pagination-jump-next {
- height: 20px;
- line-height: 20px;
- }
-
- .ant-pagination-options-size-changer.ant-select {
- margin-right: 20px;
- }
-
- .ant-select-arrow {
- color: @border-color-shallow-dark;
+ &:last-child {
+ margin-right: 0 !important;
}
}
- &-disabled {
- display: none;
+ .ant-pagination-item-active {
+ background: @primary-color;
+
+ a {
+ color: @white;
+ }
+ }
+
+ .ant-pagination-options {
+ margin-left: 20px;
+ }
+
+ .ant-select-sm .ant-select-selection--single {
+ height: 20px;
+ }
+
+ .ant-pagination-options,
+ .ant-pagination-total-text,
+ .ant-pagination-options-quick-jumper {
+ height: 20px;
+ line-height: 20px;
+ }
+
+ .ant-select-selection__rendered {
+ height: 18px;
+ line-height: 18px;
+ }
+
+ .ant-pagination-total-text,
+ .ant-select-selection__rendered,
+ .ant-select-dropdown-menu-item,
+ .ant-pagination-options-quick-jumper {
+ font-size: 13px;
+ }
+
+ .ant-pagination-options-quick-jumper input {
+ width: 40px;
+ height: 20px;
+ margin: 0 6px;
+ line-height: 20px;
+ text-align: center;
+ }
+
+ .ant-pagination-jump-prev,
+ .ant-pagination-jump-next {
+ height: 20px;
+ line-height: 20px;
+ }
+
+ .ant-pagination-options-size-changer.ant-select {
+ margin-right: 20px;
+ }
+
+ .ant-select-arrow {
+ color: @border-color-shallow-dark;
}
}
+
+ &-disabled {
+ display: none;
+ }
}
diff --git a/src/layouts/default/LayoutContent.tsx b/src/layouts/default/LayoutContent.tsx
index 21d65df8..3bfb3344 100644
--- a/src/layouts/default/LayoutContent.tsx
+++ b/src/layouts/default/LayoutContent.tsx
@@ -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 (
{{
- default: () => ,
+ default: () => [, projectSetting.canEmbedIFramePage && ],
}}
);
diff --git a/src/layouts/page/index.tsx b/src/layouts/page/index.tsx
index 1ba28a69..096aa280 100644
--- a/src/layouts/page/index.tsx
+++ b/src/layouts/page/index.tsx
@@ -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 (
- ]
-
- {{
- default: ({ Component, route }: { Component: any; route: RouteLocation }) => {
- const Content = openCache ? (
-
-
-
- ) : (
+ return [
+
+ {{
+ default: ({ Component, route }: { Component: any; route: RouteLocation }) => {
+ const Content = openCache ? (
+
- );
- return openRouterTransition ? (
-
- {() => Content}
-
- ) : (
- Content
- );
- },
- }}
-
- {projectSetting.canEmbedIFramePage && }
-
- );
+
+ ) : (
+
+ );
+ return openRouterTransition ? (
+
+ {() => Content}
+
+ ) : (
+ Content
+ );
+ },
+ }}
+ ,
+ ];
};
},
});
diff --git a/src/router/guard/progressGuard.ts b/src/router/guard/progressGuard.ts
index 9ec2acf9..9f427162 100644
--- a/src/router/guard/progressGuard.ts
+++ b/src/router/guard/progressGuard.ts
@@ -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;
diff --git a/src/router/menus/modules/demo/comp.ts b/src/router/menus/modules/demo/comp.ts
index ab171f71..fd604d87 100644
--- a/src/router/menus/modules/demo/comp.ts
+++ b/src/router/menus/modules/demo/comp.ts
@@ -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: '表单组件',
diff --git a/src/router/routes/modules/demo/comp.ts b/src/router/routes/modules/demo/comp.ts
index fc58bd2b..8f44edd6 100644
--- a/src/router/routes/modules/demo/comp.ts
+++ b/src/router/routes/modules/demo/comp.ts
@@ -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',
diff --git a/src/store/index.ts b/src/store/index.ts
index fafc8159..4fab4a6e 100644
--- a/src/store/index.ts
+++ b/src/store/index.ts
@@ -1,16 +1,19 @@
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[] = isDev ? [createLogger()] : [];
+// const plugins: Plugin[] = isDev ? [createLogger()] : [];
const store = createStore({
modules: {},
strict: isDev,
- plugins,
+ // plugins,
});
export function setupStore(app: App) {
app.use(store);
diff --git a/src/types/source.d.ts b/src/types/source.d.ts
index d61652be..3fc7fd6f 100644
--- a/src/types/source.d.ts
+++ b/src/types/source.d.ts
@@ -1,4 +1,5 @@
declare module 'ant-design-vue/es/locale/zh_CN';
+declare module 'vue-draggable-resizable';
declare const React: string;
declare module '*.bmp' {
const src: string;
diff --git a/src/useApp.tsx b/src/useApp.tsx
index da7ecb50..74c1d4d1 100644
--- a/src/useApp.tsx
+++ b/src/useApp.tsx
@@ -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 ;
- }
-
function transformCellText({ text }: { text: string }) {
if (isNull(text) || isUnDef(text)) {
return ' - ';
@@ -70,7 +64,6 @@ export function useConfigProvider() {
return text;
}
return {
- renderEmpty,
transformCellText,
};
}
diff --git a/src/views/demo/comp/verify/index.vue b/src/views/demo/comp/verify/index.vue
index d2dea550..c7f9c023 100644
--- a/src/views/demo/comp/verify/index.vue
+++ b/src/views/demo/comp/verify/index.vue
@@ -25,7 +25,7 @@
-
+
@@ -35,7 +35,7 @@
-
+
成功
diff --git a/src/views/demo/table/Basic.vue b/src/views/demo/table/Basic.vue
new file mode 100644
index 00000000..0b1fd0e2
--- /dev/null
+++ b/src/views/demo/table/Basic.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+ {{ !canResize ? '自适应高度' : '取消自适应' }}
+
+
+ {{ !border ? '显示边框' : '隐藏边框' }}
+
+ 开启loading
+
+ {{ !striped ? '显示斑马纹' : '隐藏斑马纹' }}
+
+
+
+
+
+
diff --git a/src/views/demo/table/CustomerCell.vue b/src/views/demo/table/CustomerCell.vue
new file mode 100644
index 00000000..e9646bff
--- /dev/null
+++ b/src/views/demo/table/CustomerCell.vue
@@ -0,0 +1,70 @@
+
+
+
+ ID: {{ record.id }}
+ {{ record.no }}
+
+
+
+
+
+
+
+
diff --git a/src/views/demo/table/EditCellTable.vue b/src/views/demo/table/EditCellTable.vue
new file mode 100644
index 00000000..678b6ebf
--- /dev/null
+++ b/src/views/demo/table/EditCellTable.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/demo/table/ExpandTable.vue b/src/views/demo/table/ExpandTable.vue
new file mode 100644
index 00000000..036b48e3
--- /dev/null
+++ b/src/views/demo/table/ExpandTable.vue
@@ -0,0 +1,34 @@
+
+
+
+
+ No: {{ record.no }}
+
+
+
+
+
diff --git a/src/views/demo/table/FetchTable.vue b/src/views/demo/table/FetchTable.vue
new file mode 100644
index 00000000..9159f78b
--- /dev/null
+++ b/src/views/demo/table/FetchTable.vue
@@ -0,0 +1,44 @@
+
+
+
+
+ 刷新当前页
+ 刷新并返回第一页
+
+
+
+
+
diff --git a/src/views/demo/table/FixedColumn.vue b/src/views/demo/table/FixedColumn.vue
new file mode 100644
index 00000000..e06eb022
--- /dev/null
+++ b/src/views/demo/table/FixedColumn.vue
@@ -0,0 +1,93 @@
+
+
+
+
diff --git a/src/views/demo/table/FixedHeight.vue b/src/views/demo/table/FixedHeight.vue
new file mode 100644
index 00000000..118ed16b
--- /dev/null
+++ b/src/views/demo/table/FixedHeight.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+ 姓名
+
+
+
+
+ 地址
+
+
+
+
+
+
diff --git a/src/views/demo/table/FooterTable.vue b/src/views/demo/table/FooterTable.vue
new file mode 100644
index 00000000..90e1fc91
--- /dev/null
+++ b/src/views/demo/table/FooterTable.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
diff --git a/src/views/demo/table/FormTable.vue b/src/views/demo/table/FormTable.vue
new file mode 100644
index 00000000..49434889
--- /dev/null
+++ b/src/views/demo/table/FormTable.vue
@@ -0,0 +1,27 @@
+
+
+
+
diff --git a/src/views/demo/table/MergeHeader.vue b/src/views/demo/table/MergeHeader.vue
new file mode 100644
index 00000000..2c3b612e
--- /dev/null
+++ b/src/views/demo/table/MergeHeader.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
+
diff --git a/src/views/demo/table/MultipleHeader.vue b/src/views/demo/table/MultipleHeader.vue
new file mode 100644
index 00000000..fa0bf430
--- /dev/null
+++ b/src/views/demo/table/MultipleHeader.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/src/views/demo/table/RefTable.vue b/src/views/demo/table/RefTable.vue
new file mode 100644
index 00000000..bba7b28a
--- /dev/null
+++ b/src/views/demo/table/RefTable.vue
@@ -0,0 +1,119 @@
+
+
+
+
还原
+
开启loading
+
更改Columns
+
获取Columns
+
获取表格数据
+
跳转到第2页
+
+
+
获取选中行
+
获取选中行Key
+
设置选中行
+
清空选中行
+
获取分页信息
+
+
+
+
+
diff --git a/src/views/demo/table/TreeTable.vue b/src/views/demo/table/TreeTable.vue
new file mode 100644
index 00000000..0792a9b6
--- /dev/null
+++ b/src/views/demo/table/TreeTable.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
diff --git a/src/views/demo/table/UseTable.vue b/src/views/demo/table/UseTable.vue
new file mode 100644
index 00000000..95d6e55f
--- /dev/null
+++ b/src/views/demo/table/UseTable.vue
@@ -0,0 +1,126 @@
+
+
+
+
还原
+
开启loading
+
更改Columns
+
获取Columns
+
获取表格数据
+
跳转到第2页
+
+
+
获取选中行
+
获取选中行Key
+
设置选中行
+
清空选中行
+
获取分页信息
+
+
+
+
+
diff --git a/src/views/demo/table/tableData.tsx b/src/views/demo/table/tableData.tsx
new file mode 100644
index 00000000..b76bb7ed
--- /dev/null
+++ b/src/views/demo/table/tableData.tsx
@@ -0,0 +1,292 @@
+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
{
+ 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;
+}