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