71 changed files with 3937 additions and 191 deletions
@ -1,7 +1,8 @@ |
|||
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'; |
|||
import userMock from './sys/user'; |
|||
import menuMock from './sys/menu'; |
|||
import tableDemoMock from './demo/table-demo'; |
|||
|
|||
export function setupProdMockServer() { |
|||
createProdMockServer([...userMock, ...menuMock]); |
|||
createProdMockServer([...userMock, ...menuMock, ...tableDemoMock]); |
|||
} |
|||
|
|||
@ -0,0 +1,30 @@ |
|||
import { MockMethod } from 'vite-plugin-mock'; |
|||
import { resultPageSuccess } from '../_util'; |
|||
|
|||
const demoList = (() => { |
|||
const result: any[] = []; |
|||
for (let index = 0; index < 60; index++) { |
|||
result.push({ |
|||
id: `${index}`, |
|||
beginTime: '@datetime', |
|||
endTime: '@datetime', |
|||
address: '@city()', |
|||
name: '@cname()', |
|||
'no|100000-10000000': 100000, |
|||
'status|1': ['正常', '启用', '停用'], |
|||
}); |
|||
} |
|||
return result; |
|||
})(); |
|||
|
|||
export default [ |
|||
{ |
|||
url: '/api/table/getDemoList', |
|||
timeout: 1000, |
|||
method: 'get', |
|||
response: ({ query }) => { |
|||
const { page = 1, pageSize = 20 } = query; |
|||
return resultPageSuccess(page, pageSize, demoList); |
|||
}, |
|||
}, |
|||
] as MockMethod[]; |
|||
@ -0,0 +1,20 @@ |
|||
import { BasicPageParams, BasicFetchResult } from '/@/api/model/baseModel'; |
|||
/** |
|||
* @description: 请求列表接口参数 |
|||
*/ |
|||
export type DemoParams = BasicPageParams; |
|||
|
|||
export interface DemoListItem { |
|||
id: string; |
|||
beginTime: string; |
|||
endTime: string; |
|||
address: string; |
|||
name: string; |
|||
no: number; |
|||
status: number; |
|||
} |
|||
|
|||
/** |
|||
* @description: 请求列表返回值 |
|||
*/ |
|||
export type DemoListGetResultModel = BasicFetchResult<DemoListItem>; |
|||
@ -0,0 +1,17 @@ |
|||
import { defHttp } from '/@/utils/http/axios'; |
|||
import { DemoParams, DemoListGetResultModel } from './model/tableModel'; |
|||
|
|||
enum Api { |
|||
DEMO_LIST = '/table/getDemoList', |
|||
} |
|||
|
|||
/** |
|||
* @description: 获取示例列表值 |
|||
*/ |
|||
export function demoListApi(params: DemoParams) { |
|||
return defHttp.request<DemoListGetResultModel>({ |
|||
url: Api.DEMO_LIST, |
|||
method: 'GET', |
|||
params, |
|||
}); |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
export interface BasicPageParams { |
|||
page: number; |
|||
pageSize: number; |
|||
} |
|||
|
|||
export interface BasicFetchResult<T extends any> { |
|||
items: T; |
|||
total: number; |
|||
} |
|||
|
Before Width: | Height: | Size: 372 KiB |
|
Before Width: | Height: | Size: 9.9 KiB |
@ -1,28 +0,0 @@ |
|||
<template> |
|||
<Empty :image="image" :description="description" /> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { Empty } from 'ant-design-vue'; |
|||
|
|||
import emptySrc from '/@/assets/images/page_null.png'; |
|||
|
|||
export default defineComponent({ |
|||
extends: Empty as any, |
|||
components: { Empty }, |
|||
props: { |
|||
description: { |
|||
type: String, |
|||
default: '暂无内容', |
|||
}, |
|||
image: { |
|||
type: String, |
|||
default: emptySrc, |
|||
required: false, |
|||
}, |
|||
}, |
|||
setup() { |
|||
return {}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,13 @@ |
|||
export { default as BasicTable } from './src/BasicTable.vue'; |
|||
export { default as TableAction } from './src/components/TableAction'; |
|||
export { default as TableImg } from './src/components/TableImg.vue'; |
|||
export { renderEditableCell } from './src/components/renderEditableCell'; |
|||
export { default as EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue'; |
|||
|
|||
export * from './src/types/table'; |
|||
export * from './src/types/pagination'; |
|||
export * from './src/types/tableAction'; |
|||
|
|||
export { useTable } from './src/hooks/useTable'; |
|||
|
|||
export type { FormSchema, FormProps } from '/@/components/Form/src/types/form'; |
|||
@ -0,0 +1,285 @@ |
|||
<template> |
|||
<div |
|||
class="basic-table" |
|||
:class="{ |
|||
'table-form-container': getBindValues.useSearchForm, |
|||
}" |
|||
> |
|||
<BasicForm |
|||
v-bind="getFormProps" |
|||
v-if="getBindValues.useSearchForm" |
|||
:submitButtonOptions="{ loading }" |
|||
@register="registerForm" |
|||
@submit="handleSearchInfoChange" |
|||
@advanced-change="redoHeight" |
|||
> |
|||
<template #[item]="data" v-for="item in Object.keys($slots)"> |
|||
<slot :name="`form-${item}`" v-bind="data" /> |
|||
</template> |
|||
</BasicForm> |
|||
<Table |
|||
ref="tableElRef" |
|||
v-bind="getBindValues" |
|||
:rowClassName="getRowClassName" |
|||
:class="{ |
|||
hidden: !getEmptyDataIsShowTable, |
|||
}" |
|||
@change="handleTableChange" |
|||
> |
|||
<template #[item]="data" v-for="item in Object.keys($slots)"> |
|||
<slot :name="item" v-bind="data" /> |
|||
</template> |
|||
</Table> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, ref, computed, unref, watch, nextTick } from 'vue'; |
|||
import { Table } from 'ant-design-vue'; |
|||
import { basicProps } from './props'; |
|||
import type { |
|||
BasicTableProps, |
|||
FetchParams, |
|||
GetColumnsParams, |
|||
TableActionType, |
|||
} from './types/table'; |
|||
import { isFunction, isString } from '/@/utils/is'; |
|||
|
|||
import renderTitle from './components/renderTitle'; |
|||
import renderFooter from './components/renderFooter'; |
|||
import renderExpandIcon from './components/renderExpandIcon'; |
|||
|
|||
import { usePagination } from './hooks/usePagination'; |
|||
import { useColumns } from './hooks/useColumns'; |
|||
import { useDataSource } from './hooks/useDataSource'; |
|||
import { useLoading } from './hooks/useLoading'; |
|||
import { useRowSelection } from './hooks/useRowSelection'; |
|||
import { useTableScroll } from './hooks/useTableScroll'; |
|||
import { provideTable } from './hooks/useProvinceTable'; |
|||
import { BasicForm, FormProps, useForm } from '/@/components/Form/index'; |
|||
import { omit } from 'lodash-es'; |
|||
import './style/index.less'; |
|||
import { ROW_KEY } from './const'; |
|||
import { PaginationProps } from './types/pagination'; |
|||
import { deepMerge } from '/@/utils'; |
|||
import { TableCustomRecord } from 'ant-design-vue/types/table/table'; |
|||
import { useEvent } from '/@/hooks/event/useEvent'; |
|||
export default defineComponent({ |
|||
props: basicProps, |
|||
components: { Table, BasicForm }, |
|||
emits: ['fetch-success', 'fetch-error', 'selection-change', 'register'], |
|||
setup(props, { attrs, emit, slots }) { |
|||
const tableElRef = ref<any>(null); |
|||
const innerPropsRef = ref<Partial<BasicTableProps>>(); |
|||
const [registerForm, { getFieldsValue }] = useForm(); |
|||
|
|||
const getMergeProps = computed( |
|||
(): BasicTableProps => { |
|||
return { |
|||
...props, |
|||
...unref(innerPropsRef), |
|||
} as BasicTableProps; |
|||
} |
|||
); |
|||
const { loadingRef } = useLoading(getMergeProps); |
|||
const { getPaginationRef, setPagination } = usePagination(getMergeProps); |
|||
const { getColumnsRef, setColumns } = useColumns(getMergeProps, getPaginationRef); |
|||
const { getDataSourceRef, setTableData, fetch, getAutoCreateKey } = useDataSource( |
|||
getMergeProps, |
|||
{ |
|||
getPaginationRef, |
|||
loadingRef, |
|||
setPagination, |
|||
getFieldsValue, |
|||
}, |
|||
emit |
|||
); |
|||
const { getScrollRef, redoHeight } = useTableScroll(getMergeProps, tableElRef); |
|||
const { |
|||
getRowSelectionRef, |
|||
getSelectRows, |
|||
clearSelectedRowKeys, |
|||
getSelectRowKeys, |
|||
deleteSelectRowByKey, |
|||
setSelectedRowKeys, |
|||
} = useRowSelection(getMergeProps, emit); |
|||
|
|||
const getRowKey = computed(() => { |
|||
const { rowKey } = unref(getMergeProps); |
|||
|
|||
return unref(getAutoCreateKey) ? ROW_KEY : rowKey; |
|||
}); |
|||
const getBindValues = computed(() => { |
|||
const { title, titleHelpMessage, showSummary } = unref(getMergeProps); |
|||
const titleData: any = |
|||
!slots.tableTitle && !isString(title) && !title && !slots.toolbar |
|||
? {} |
|||
: { |
|||
title: |
|||
!slots.tableTitle && !title && !slots.toolbar |
|||
? null |
|||
: renderTitle.bind(null, title, titleHelpMessage, slots), |
|||
}; |
|||
const pagination = unref(getPaginationRef); |
|||
const rowSelection = unref(getRowSelectionRef); |
|||
const scroll = unref(getScrollRef); |
|||
const loading = unref(loadingRef); |
|||
const rowKey = unref(getRowKey); |
|||
const columns = unref(getColumnsRef); |
|||
const dataSource = unref(getDataSourceRef); |
|||
let propsData = { |
|||
size: 'middle', |
|||
...(slots.expandedRowRender ? { expandIcon: renderExpandIcon() } : {}), |
|||
...attrs, |
|||
...unref(getMergeProps), |
|||
...titleData, |
|||
scroll, |
|||
loading, |
|||
tableLayout: 'fixed', |
|||
rowSelection, |
|||
rowKey, |
|||
columns, |
|||
pagination, |
|||
dataSource, |
|||
}; |
|||
if (slots.expandedRowRender) { |
|||
propsData = omit(propsData, 'scroll'); |
|||
} |
|||
if (showSummary) { |
|||
propsData.footer = renderFooter.bind(null, { |
|||
scroll, |
|||
columnsRef: getColumnsRef, |
|||
summaryFunc: unref(getMergeProps).summaryFunc, |
|||
dataSourceRef: getDataSourceRef, |
|||
rowSelectionRef: getRowSelectionRef, |
|||
}); |
|||
} |
|||
return propsData; |
|||
}); |
|||
const getFormProps = computed(() => { |
|||
const { formConfig } = unref(getBindValues); |
|||
const formProps: FormProps = { |
|||
showAdvancedButton: true, |
|||
...(formConfig as FormProps), |
|||
compact: true, |
|||
}; |
|||
return formProps; |
|||
}); |
|||
|
|||
const getEmptyDataIsShowTable = computed(() => { |
|||
const { emptyDataIsShowTable, useSearchForm } = unref(getMergeProps); |
|||
if (emptyDataIsShowTable || !useSearchForm) { |
|||
return true; |
|||
} |
|||
return !!unref(getDataSourceRef).length; |
|||
}); |
|||
|
|||
function getRowClassName(record: TableCustomRecord<any>, index: number) { |
|||
const { striped, rowClassName } = unref(getMergeProps); |
|||
if (!striped) return; |
|||
if (rowClassName && isFunction(rowClassName)) { |
|||
return rowClassName(record); |
|||
} |
|||
return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : ''; |
|||
} |
|||
|
|||
function handleSearchInfoChange(info: any) { |
|||
const { handleSearchInfoFn } = unref(getMergeProps); |
|||
if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) { |
|||
info = handleSearchInfoFn(info) || info; |
|||
} |
|||
fetch({ searchInfo: info, page: 1 }); |
|||
} |
|||
|
|||
function handleTableChange(pagination: PaginationProps) { |
|||
const { clearSelectOnPageChange } = unref(getMergeProps); |
|||
if (clearSelectOnPageChange) { |
|||
clearSelectedRowKeys(); |
|||
} |
|||
setPagination(pagination); |
|||
fetch(); |
|||
} |
|||
watch( |
|||
() => unref(getDataSourceRef), |
|||
() => { |
|||
if (unref(getMergeProps).showSummary) { |
|||
nextTick(() => { |
|||
const tableEl = unref(tableElRef); |
|||
if (!tableEl) { |
|||
return; |
|||
} |
|||
const bodyDomList = tableEl.$el.querySelectorAll( |
|||
'.ant-table-body' |
|||
) as HTMLDivElement[]; |
|||
const bodyDom = bodyDomList[0]; |
|||
useEvent({ |
|||
el: bodyDom, |
|||
name: 'scroll', |
|||
listener: () => { |
|||
const footerBodyDom = tableEl.$el.querySelector( |
|||
'.ant-table-footer .ant-table-body' |
|||
) as HTMLDivElement; |
|||
if (!footerBodyDom || !bodyDom) return; |
|||
footerBodyDom.scrollLeft = bodyDom.scrollLeft; |
|||
}, |
|||
wait: 0, |
|||
options: true, |
|||
}); |
|||
}); |
|||
} |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
|
|||
const tableAction: TableActionType = { |
|||
reload: async (opt?: FetchParams) => { |
|||
await fetch(opt); |
|||
}, |
|||
getSelectRows, |
|||
clearSelectedRowKeys, |
|||
getSelectRowKeys, |
|||
deleteSelectRowByKey, |
|||
setPagination, |
|||
setTableData, |
|||
redoHeight, |
|||
setSelectedRowKeys, |
|||
setColumns, |
|||
getPaginationRef: () => { |
|||
return unref(getPaginationRef); |
|||
}, |
|||
getColumns: (opt?: GetColumnsParams) => { |
|||
const { ignoreIndex } = opt || {}; |
|||
let columns = unref(getColumnsRef); |
|||
if (ignoreIndex) { |
|||
columns = columns.filter((item) => item.flag !== 'INDEX'); |
|||
} |
|||
return columns; |
|||
}, |
|||
getDataSource: () => { |
|||
return unref(getDataSourceRef); |
|||
}, |
|||
setLoading: (loading: boolean) => { |
|||
loadingRef.value = loading; |
|||
}, |
|||
setProps: (props: Partial<BasicTableProps>) => { |
|||
innerPropsRef.value = deepMerge(unref(innerPropsRef) || {}, props); |
|||
}, |
|||
}; |
|||
|
|||
provideTable(tableAction); |
|||
|
|||
emit('register', tableAction); |
|||
return { |
|||
tableElRef, |
|||
getBindValues, |
|||
loading: loadingRef, |
|||
registerForm, |
|||
handleSearchInfoChange, |
|||
getFormProps, |
|||
getEmptyDataIsShowTable, |
|||
handleTableChange, |
|||
getRowClassName, |
|||
...tableAction, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,26 @@ |
|||
import { Component } from 'vue'; |
|||
|
|||
import { Input, Select, Checkbox, InputNumber, Switch } from 'ant-design-vue'; |
|||
|
|||
import { ComponentType } from './types/componentType'; |
|||
|
|||
const componentMap = new Map<ComponentType, Component>(); |
|||
|
|||
componentMap.set('Input', Input); |
|||
componentMap.set('InputPassword', Input.Password); |
|||
componentMap.set('InputNumber', InputNumber); |
|||
|
|||
componentMap.set('Select', Select); |
|||
componentMap.set('Switch', Switch); |
|||
componentMap.set('Checkbox', Checkbox); |
|||
componentMap.set('CheckboxGroup', Checkbox.Group); |
|||
|
|||
export function add(compName: ComponentType, component: Component) { |
|||
componentMap.set(compName, component); |
|||
} |
|||
|
|||
export function del(compName: ComponentType) { |
|||
componentMap.delete(compName); |
|||
} |
|||
|
|||
export { componentMap }; |
|||
@ -0,0 +1,72 @@ |
|||
import { defineComponent, ref, computed, unref } from 'vue'; |
|||
import { injectTable } from '../hooks/useProvinceTable'; |
|||
import { getSlot } from '/@/utils/helper/tsxHelper'; |
|||
|
|||
import VueDraggableResizable from 'vue-draggable-resizable'; |
|||
export default defineComponent({ |
|||
name: 'DragResize', |
|||
setup(props, { slots, attrs }) { |
|||
const elRef = ref<HTMLTableRowElement | null>(null); |
|||
const draggingMapRef = ref<{ [key in string]: number | string }>({}); |
|||
|
|||
const tableInstance = injectTable(); |
|||
|
|||
const getColumnsRef = computed(() => { |
|||
const columns = tableInstance.getColumns(); |
|||
columns.forEach((col) => { |
|||
const { key } = col; |
|||
if (key) { |
|||
draggingMapRef.value[key] = col.width as number; |
|||
} |
|||
}); |
|||
return columns; |
|||
}); |
|||
|
|||
return () => { |
|||
const { key = '', ...restProps } = { ...attrs }; |
|||
const col = unref(getColumnsRef).find((col) => { |
|||
const k = col.dataIndex || col.key; |
|||
return k === key; |
|||
}); |
|||
if (!col || !col.width) { |
|||
return <th {...restProps}>{getSlot(slots, 'default')}</th>; |
|||
} |
|||
const onDrag = (x: number) => { |
|||
draggingMapRef.value[key] = 0; |
|||
col.width = Math.max(x, 1); |
|||
}; |
|||
|
|||
const onDragstop = () => { |
|||
const el = unref(elRef); |
|||
if (!el) { |
|||
return; |
|||
} |
|||
draggingMapRef.value[key] = el.getBoundingClientRect().width; |
|||
}; |
|||
return ( |
|||
<th |
|||
{...restProps} |
|||
class="resize-table-th" |
|||
ref={elRef} |
|||
style={{ |
|||
width: `${col.width}px`, |
|||
}} |
|||
> |
|||
{getSlot(slots, 'default')} |
|||
<VueDraggableResizable |
|||
key={col.key} |
|||
class="table-draggable-handle" |
|||
w={10} |
|||
x={draggingMapRef.value[key] || col.width} |
|||
z={1} |
|||
axis="x" |
|||
draggable={true} |
|||
resizable={false} |
|||
onDragging={onDrag} |
|||
onDragstop={onDragstop} |
|||
/> |
|||
</th> |
|||
); |
|||
}; |
|||
}, |
|||
}); |
|||
@ -0,0 +1,21 @@ |
|||
<template> |
|||
<span> |
|||
{{ title }} |
|||
<FormOutlined class="ml-2" /> |
|||
</span> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, PropType } from 'vue'; |
|||
import { FormOutlined } from '@ant-design/icons-vue'; |
|||
export default defineComponent({ |
|||
name: 'EditTableHeaderIcon', |
|||
components: { FormOutlined }, |
|||
props: { |
|||
title: { |
|||
type: String as PropType<string>, |
|||
default: '', |
|||
}, |
|||
}, |
|||
setup() {}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,144 @@ |
|||
import { defineComponent, PropType } from 'vue'; |
|||
import { Dropdown, Menu, Popconfirm } from 'ant-design-vue'; |
|||
import Icon from '/@/components/Icon/index'; |
|||
import { DownOutlined } from '@ant-design/icons-vue'; |
|||
import { ActionItem } from '../types/tableAction'; |
|||
import Button from '/@/components/Button/index.vue'; |
|||
const prefixCls = 'basic-table-action'; |
|||
export default defineComponent({ |
|||
name: 'TableAction', |
|||
props: { |
|||
actions: { |
|||
type: Array as PropType<ActionItem[]>, |
|||
default: null, |
|||
}, |
|||
dropDownActions: { |
|||
type: Array as PropType<ActionItem[]>, |
|||
default: null, |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
// 增加按钮的TYPE和COLOR
|
|||
return () => { |
|||
const { dropDownActions = [], actions } = props; |
|||
return ( |
|||
<div class={prefixCls}> |
|||
{actions && |
|||
actions.length && |
|||
actions.map((action, index) => { |
|||
const { |
|||
disabled = false, |
|||
label, |
|||
props, |
|||
icon, |
|||
color = '', |
|||
type = 'link', |
|||
popConfirm = null, |
|||
} = action; |
|||
const button = ( |
|||
<Button |
|||
type={type} |
|||
size="small" |
|||
disabled={disabled} |
|||
color={color} |
|||
{...props} |
|||
key={index} |
|||
> |
|||
{() => ( |
|||
<> |
|||
{label} |
|||
{icon && <Icon icon={icon} />} |
|||
</> |
|||
)} |
|||
</Button> |
|||
); |
|||
if (popConfirm !== null) { |
|||
const { |
|||
title, |
|||
okText = '确定', |
|||
cancelText = '取消', |
|||
confirm = () => {}, |
|||
cancel = () => {}, |
|||
icon = '', |
|||
} = popConfirm; |
|||
return ( |
|||
<Popconfirm |
|||
key={`P-${index}`} |
|||
title={title} |
|||
onConfirm={confirm} |
|||
onCancel={cancel} |
|||
okText={okText} |
|||
cancelText={cancelText} |
|||
icon={icon} |
|||
> |
|||
{() => button} |
|||
</Popconfirm> |
|||
); |
|||
} |
|||
return button; |
|||
})} |
|||
{dropDownActions && dropDownActions.length && ( |
|||
<Dropdown> |
|||
{{ |
|||
default: () => ( |
|||
<Button type="link" size="small"> |
|||
{{ |
|||
default: () => ( |
|||
<> |
|||
更多 |
|||
<DownOutlined /> |
|||
</> |
|||
), |
|||
}} |
|||
</Button> |
|||
), |
|||
overlay: () => { |
|||
return ( |
|||
<Menu> |
|||
{{ |
|||
default: () => { |
|||
return dropDownActions.map((action, index) => { |
|||
const { |
|||
disabled = false, |
|||
label, |
|||
props, |
|||
icon, |
|||
color = '', |
|||
type = 'link', |
|||
} = action; |
|||
return ( |
|||
<Menu.Item key={`${index}`} disabled={disabled}> |
|||
{() => ( |
|||
<Button |
|||
type={type} |
|||
size="small" |
|||
{...props} |
|||
disabled={disabled} |
|||
color={color} |
|||
> |
|||
{{ |
|||
default: () => ( |
|||
<> |
|||
{label} |
|||
{icon && <Icon icon={icon} />} |
|||
</> |
|||
), |
|||
}} |
|||
</Button> |
|||
)} |
|||
</Menu.Item> |
|||
); |
|||
}); |
|||
}, |
|||
}} |
|||
</Menu> |
|||
); |
|||
}, |
|||
}} |
|||
</Dropdown> |
|||
)} |
|||
</div> |
|||
); |
|||
}; |
|||
}, |
|||
}); |
|||
@ -0,0 +1,36 @@ |
|||
<template> |
|||
<div class="basic-table-img__preview" v-if="imgList && imgList.length"> |
|||
<template v-for="(img, index) in imgList" :key="img"> |
|||
<img :width="size" @click="handlePreview(index)" :src="img" /> |
|||
</template> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, PropType } from 'vue'; |
|||
import { createImgPreview } from '/@/components/Preview/index'; |
|||
|
|||
export default defineComponent({ |
|||
name: 'TableAction', |
|||
props: { |
|||
imgList: { |
|||
type: Array as PropType<string[]>, |
|||
default: null, |
|||
}, |
|||
size: { |
|||
type: Number as PropType<number>, |
|||
default: 40, |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
function handlePreview(index: number) { |
|||
const { imgList } = props; |
|||
|
|||
createImgPreview({ |
|||
imageList: imgList as string[], |
|||
index: index, |
|||
}); |
|||
} |
|||
return { handlePreview }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,41 @@ |
|||
<template> |
|||
<BasicTitle class="basic-table-title" v-if="tableTitle" :helpMessage="helpMessage"> |
|||
{{ tableTitle }} |
|||
</BasicTitle> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { computed, defineComponent, PropType } from 'vue'; |
|||
|
|||
import { BasicTitle } from '/@/components/Basic/index'; |
|||
import { isFunction } from '/@/utils/is'; |
|||
export default defineComponent({ |
|||
name: 'TableTitle', |
|||
components: { BasicTitle }, |
|||
props: { |
|||
title: { |
|||
type: [Function, String] as PropType<string | ((data: any) => string)>, |
|||
}, |
|||
getSelectRows: { |
|||
type: Function as PropType<() => any[]>, |
|||
}, |
|||
helpMessage: { |
|||
type: [String, Array] as PropType<string | string[]>, |
|||
}, |
|||
}, |
|||
setup(props) { |
|||
const tableTitle = computed(() => { |
|||
const { title, getSelectRows = () => {} } = props; |
|||
let tit = title; |
|||
|
|||
if (isFunction(title)) { |
|||
tit = title({ |
|||
selectRows: getSelectRows(), |
|||
}); |
|||
} |
|||
return tit; |
|||
}); |
|||
|
|||
return { tableTitle }; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,153 @@ |
|||
import { defineComponent, PropType, ref, unref, nextTick } from 'vue'; |
|||
import { injectTable } from '../hooks/useProvinceTable'; |
|||
import ClickOutSide from '/@/components/ClickOutSide/index.vue'; |
|||
|
|||
import { RenderEditableCellParams } from '../types/table'; |
|||
import { ComponentType } from '../types/componentType'; |
|||
|
|||
import { componentMap } from '../componentMap'; |
|||
import '../style/editable-cell.less'; |
|||
import { isString, isBoolean } from '/@/utils/is'; |
|||
import { FormOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue'; |
|||
|
|||
const prefixCls = 'editable-cell'; |
|||
const EditableCell = defineComponent({ |
|||
name: 'EditableCell', |
|||
props: { |
|||
value: { |
|||
type: String as PropType<string>, |
|||
default: '', |
|||
}, |
|||
componentProps: { |
|||
type: Object as PropType<any>, |
|||
default: null, |
|||
}, |
|||
|
|||
dataKey: { |
|||
type: String as PropType<string>, |
|||
default: '', |
|||
}, |
|||
|
|||
dataIndex: { |
|||
type: String as PropType<string>, |
|||
default: '', |
|||
}, |
|||
|
|||
component: { |
|||
type: String as PropType<ComponentType>, |
|||
default: 'Input', |
|||
}, |
|||
}, |
|||
setup(props, { attrs }) { |
|||
const table = injectTable(); |
|||
const elRef = ref<any>(null); |
|||
|
|||
const isEditRef = ref(false); |
|||
const currentValueRef = ref<string | boolean>(''); |
|||
|
|||
function handleChange(e: ChangeEvent | string | boolean) { |
|||
if ((e as ChangeEvent).target && Reflect.has((e as ChangeEvent).target, 'value')) { |
|||
currentValueRef.value = (e as ChangeEvent).target.value; |
|||
} |
|||
if (isString(e) || isBoolean(e)) { |
|||
currentValueRef.value = e; |
|||
} |
|||
} |
|||
|
|||
function handleEdit() { |
|||
isEditRef.value = true; |
|||
nextTick(() => { |
|||
const el = unref(elRef); |
|||
el && el.focus && el.focus(); |
|||
}); |
|||
} |
|||
|
|||
function handleCancel() { |
|||
isEditRef.value = false; |
|||
} |
|||
|
|||
function handleSubmit() { |
|||
const { dataKey, dataIndex } = props; |
|||
if (!dataKey || !dataIndex) { |
|||
return; |
|||
} |
|||
isEditRef.value = false; |
|||
|
|||
const { getDataSource } = table; |
|||
const dataSource = getDataSource(); |
|||
const target = dataSource.find((item) => item.key === dataKey); |
|||
if (target) { |
|||
target[dataIndex] = unref(currentValueRef); |
|||
} |
|||
} |
|||
|
|||
function onClickOutside() { |
|||
const { component } = props; |
|||
|
|||
if (component?.includes('Input')) { |
|||
handleCancel(); |
|||
} |
|||
} |
|||
return () => { |
|||
const { value, component, componentProps = {} } = props; |
|||
|
|||
const Comp = componentMap.get(component!) as any; |
|||
// const propsData: any = {};
|
|||
return ( |
|||
<div class={prefixCls}> |
|||
{unref(isEditRef) && ( |
|||
<ClickOutSide onClickOutside={onClickOutside}> |
|||
{() => ( |
|||
<div class={`${prefixCls}__wrapper`}> |
|||
<Comp |
|||
{...{ |
|||
...attrs, |
|||
...componentProps, |
|||
}} |
|||
style={{ width: 'calc(100% - 48px)' }} |
|||
ref={elRef} |
|||
value={value} |
|||
size="small" |
|||
onChange={handleChange} |
|||
onPressEnter={handleSubmit} |
|||
/> |
|||
<div class={`${prefixCls}__action`}> |
|||
<CheckOutlined class={[`${prefixCls}__icon`, 'mx-2']} onClick={handleSubmit} /> |
|||
<CloseOutlined class={[`${prefixCls}__icon `]} onClick={handleCancel} /> |
|||
</div> |
|||
</div> |
|||
)} |
|||
</ClickOutSide> |
|||
)} |
|||
|
|||
{!unref(isEditRef) && ( |
|||
<div class={`${prefixCls}__normal`} onClick={handleEdit}> |
|||
{value} |
|||
<FormOutlined class={`${prefixCls}__normal-icon`} /> |
|||
</div> |
|||
)} |
|||
</div> |
|||
); |
|||
}; |
|||
}, |
|||
}); |
|||
|
|||
export function renderEditableCell({ |
|||
dataIndex, |
|||
component, |
|||
componentOn = {}, |
|||
componentProps = {}, |
|||
}: RenderEditableCellParams) { |
|||
return ({ text, record }: { text: string; record: any }) => { |
|||
return ( |
|||
<EditableCell |
|||
value={text} |
|||
dataKey={record.key} |
|||
dataIndex={dataIndex} |
|||
component={component} |
|||
on={componentOn} |
|||
componentProps={componentProps} |
|||
/> |
|||
); |
|||
}; |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
import { BasicArrow } from '/@/components/Basic'; |
|||
|
|||
export default () => { |
|||
return (props: any) => { |
|||
return ( |
|||
<BasicArrow |
|||
onClick={(e: Event) => { |
|||
props.onExpand(props.record, e); |
|||
}} |
|||
expand={props.expanded} |
|||
class="right" |
|||
/> |
|||
); |
|||
}; |
|||
}; |
|||
@ -0,0 +1,64 @@ |
|||
import { Table } from 'ant-design-vue'; |
|||
import { TableRowSelection } from 'ant-design-vue/types/table/table'; |
|||
import { cloneDeep } from 'lodash-es'; |
|||
import { unref, ComputedRef } from 'vue'; |
|||
import { BasicColumn } from '../types/table'; |
|||
import { isFunction } from '/@/utils/is'; |
|||
export default ({ |
|||
scroll = {}, |
|||
columnsRef, |
|||
summaryFunc, |
|||
rowKey = 'key', |
|||
dataSourceRef, |
|||
rowSelectionRef, |
|||
}: { |
|||
scroll: { x?: number | true; y?: number }; |
|||
columnsRef: ComputedRef<BasicColumn[]>; |
|||
summaryFunc: any; |
|||
rowKey?: string; |
|||
dataSourceRef: ComputedRef<any[]>; |
|||
rowSelectionRef: ComputedRef<TableRowSelection<any> | null>; |
|||
}) => { |
|||
if (!summaryFunc) { |
|||
return; |
|||
} |
|||
const dataSource: any[] = isFunction(summaryFunc) ? summaryFunc(unref(dataSourceRef)) : []; |
|||
const columns: BasicColumn[] = cloneDeep(unref(columnsRef)); |
|||
const index = columns.findIndex((item) => item.flag === 'INDEX'); |
|||
const hasRowSummary = dataSource.some((item) => Reflect.has(item, '_row')); |
|||
const hasIndexSummary = dataSource.some((item) => Reflect.has(item, '_index')); |
|||
|
|||
if (index !== -1) { |
|||
if (hasIndexSummary) { |
|||
columns[index].customRender = ({ record }) => record._index; |
|||
columns[index].ellipsis = false; |
|||
} else { |
|||
Reflect.deleteProperty(columns[index], 'customRender'); |
|||
} |
|||
} |
|||
if (unref(rowSelectionRef) && hasRowSummary) { |
|||
columns.unshift({ |
|||
width: 60, |
|||
title: 'selection', |
|||
key: 'selectionKey', |
|||
align: 'center', |
|||
customRender: ({ record }) => record._row, |
|||
}); |
|||
} |
|||
|
|||
dataSource.forEach((item, i) => { |
|||
item[rowKey] = i; |
|||
}); |
|||
return ( |
|||
<Table |
|||
showHeader={false} |
|||
bordered={false} |
|||
pagination={false} |
|||
dataSource={dataSource} |
|||
rowKey={rowKey} |
|||
columns={columns} |
|||
tableLayout="fixed" |
|||
scroll={scroll as any} |
|||
/> |
|||
); |
|||
}; |
|||
@ -0,0 +1,14 @@ |
|||
import { Slots } from 'vue'; |
|||
import TableTitle from './TableTitle.vue'; |
|||
import { getSlot } from '/@/utils/helper/tsxHelper'; |
|||
export default (title: any, titleHelpMessage: string | string[], slots: Slots) => { |
|||
return ( |
|||
<> |
|||
{getSlot(slots, 'tableTitle') || |
|||
(title && <TableTitle helpMessage={titleHelpMessage} title={title} />) || ( |
|||
<span> </span> |
|||
)} |
|||
{slots.toolbar && <div class="basic-table-toolbar">{getSlot(slots, 'toolbar')}</div>} |
|||
</> |
|||
); |
|||
}; |
|||
@ -0,0 +1,12 @@ |
|||
export const ROW_KEY = 'key'; |
|||
|
|||
export const PAGE_SIZE_OPTIONS = ['10', '50', '80', '100']; |
|||
|
|||
export const PAGE_SIZE = ~~PAGE_SIZE_OPTIONS[0]; |
|||
|
|||
export const FETCH_SETTING = { |
|||
pageField: 'page', |
|||
sizeField: 'pageSize', |
|||
listField: 'items', |
|||
totalField: 'total', |
|||
}; |
|||
@ -0,0 +1,123 @@ |
|||
import { BasicColumn, BasicTableProps } from '../types/table'; |
|||
import { PaginationProps } from '../types/pagination'; |
|||
import { unref, ComputedRef, Ref, computed, watch, ref } from 'vue'; |
|||
import { isBoolean, isArray, isObject } from '/@/utils/is'; |
|||
import { PAGE_SIZE } from '../const'; |
|||
import { useProps } from './useProps'; |
|||
|
|||
export function useColumns( |
|||
refProps: ComputedRef<BasicTableProps>, |
|||
getPaginationRef: ComputedRef<false | PaginationProps> |
|||
) { |
|||
const { propsRef } = useProps(refProps); |
|||
|
|||
const columnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>; |
|||
const cacheColumnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>; |
|||
|
|||
watch( |
|||
() => unref(propsRef).columns, |
|||
(columns) => { |
|||
columnsRef.value = columns; |
|||
cacheColumnsRef.value = columns; |
|||
}, |
|||
{ |
|||
immediate: true, |
|||
} |
|||
); |
|||
const getColumnsRef = computed(() => { |
|||
const props = unref(propsRef); |
|||
const { showIndexColumn, indexColumnProps, ellipsis, actionColumn, isTreeTable } = props; |
|||
|
|||
const columns = unref(columnsRef); |
|||
if (!columns) { |
|||
return []; |
|||
} |
|||
let pushIndexColumns = false; |
|||
columns.forEach((item) => { |
|||
const { key, dataIndex } = item; |
|||
item.align = item.align || 'center'; |
|||
if (ellipsis) { |
|||
if (!key) { |
|||
item.key = dataIndex; |
|||
} |
|||
if (!isBoolean(item.ellipsis)) { |
|||
Object.assign(item, { |
|||
ellipsis, |
|||
}); |
|||
} |
|||
} |
|||
const indIndex = columns.findIndex((column) => column.flag === 'INDEX'); |
|||
if (showIndexColumn && !isTreeTable) { |
|||
pushIndexColumns = indIndex === -1; |
|||
} else if (!showIndexColumn && !isTreeTable && indIndex !== -1) { |
|||
columns.splice(indIndex, 1); |
|||
} |
|||
}); |
|||
|
|||
if (pushIndexColumns) { |
|||
const isFixedLeft = columns.some((item) => item.fixed === 'left'); |
|||
|
|||
columns.unshift({ |
|||
flag: 'INDEX', |
|||
width: 50, |
|||
title: '序号', |
|||
align: 'center', |
|||
customRender: ({ index }) => { |
|||
const getPagination = unref(getPaginationRef); |
|||
if (isBoolean(getPagination)) { |
|||
return `${index + 1}`; |
|||
} |
|||
const { current = 1, pageSize = PAGE_SIZE } = getPagination; |
|||
const currentIndex = (current - 1) * pageSize + index + 1; |
|||
return currentIndex; |
|||
}, |
|||
...(isFixedLeft |
|||
? { |
|||
fixed: 'left', |
|||
} |
|||
: {}), |
|||
...indexColumnProps, |
|||
}); |
|||
} |
|||
if (actionColumn) { |
|||
const hasIndex = columns.findIndex((column) => column.flag === 'ACTION'); |
|||
if (hasIndex === -1) { |
|||
columns.push({ |
|||
fixed: 'right', |
|||
...actionColumn, |
|||
flag: 'ACTION', |
|||
}); |
|||
} else { |
|||
columns[hasIndex] = { |
|||
...columns[hasIndex], |
|||
fixed: 'right', |
|||
...actionColumn, |
|||
flag: 'ACTION', |
|||
}; |
|||
} |
|||
} |
|||
return columns; |
|||
}); |
|||
|
|||
function setColumns(columns: BasicColumn[] | string[]) { |
|||
if (!isArray(columns)) { |
|||
return; |
|||
} |
|||
if (columns.length <= 0) { |
|||
columnsRef.value = []; |
|||
return; |
|||
} |
|||
|
|||
const firstColumn = columns[0]; |
|||
if (isObject(firstColumn)) { |
|||
columnsRef.value = columns as any; |
|||
} else { |
|||
const newColumns = unref(cacheColumnsRef).filter((item) => |
|||
(columns as string[]).includes(item.key! || item.dataIndex!) |
|||
); |
|||
columnsRef.value = newColumns; |
|||
} |
|||
} |
|||
|
|||
return { getColumnsRef, setColumns }; |
|||
} |
|||
@ -0,0 +1,148 @@ |
|||
import { useTimeout } from '/@/hooks/core/useTimeout'; |
|||
import { BasicTableProps, FetchParams } from '../types/table'; |
|||
import { PaginationProps } from '../types/pagination'; |
|||
import { watch, ref, unref, ComputedRef, computed, onMounted, Ref } from 'vue'; |
|||
import { buildUUID } from '/@/utils/uuid'; |
|||
import { isFunction, isBoolean } from '/@/utils/is'; |
|||
import { FETCH_SETTING, ROW_KEY } from '../const'; |
|||
import { get } from 'lodash-es'; |
|||
import { useProps } from './useProps'; |
|||
|
|||
interface ActionType { |
|||
getPaginationRef: ComputedRef<false | PaginationProps>; |
|||
setPagination: (info: Partial<PaginationProps>) => void; |
|||
loadingRef: Ref<boolean | undefined>; |
|||
getFieldsValue: () => { |
|||
[field: string]: any; |
|||
}; |
|||
} |
|||
export function useDataSource( |
|||
refProps: ComputedRef<BasicTableProps>, |
|||
{ getPaginationRef, setPagination, loadingRef, getFieldsValue }: ActionType, |
|||
emit: EmitType |
|||
) { |
|||
const { propsRef } = useProps(refProps); |
|||
|
|||
const dataSourceRef = ref<any[]>([]); |
|||
|
|||
watch( |
|||
() => unref(propsRef).dataSource, |
|||
(data: any[]) => { |
|||
const { api } = unref(propsRef); |
|||
!api && (dataSourceRef.value = data); |
|||
}, |
|||
{ immediate: true } |
|||
); |
|||
|
|||
function setTableKey(items: any[]) { |
|||
if (!items || !Array.isArray(items)) { |
|||
return; |
|||
} |
|||
items.forEach((item) => { |
|||
if (!item[ROW_KEY]) { |
|||
item[ROW_KEY] = buildUUID(); |
|||
} |
|||
if (item.children && item.children.length) { |
|||
setTableKey(item.children); |
|||
} |
|||
}); |
|||
} |
|||
const getAutoCreateKey = computed(() => { |
|||
return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey; |
|||
}); |
|||
|
|||
const getDataSourceRef = computed(() => { |
|||
const dataSource = unref(dataSourceRef); |
|||
if (!dataSource || dataSource.length === 0) { |
|||
return []; |
|||
} |
|||
if (unref(getAutoCreateKey)) { |
|||
const firstItem = dataSource[0]; |
|||
const lastItem = dataSource[dataSource.length - 1]; |
|||
|
|||
if (firstItem && lastItem) { |
|||
if (!firstItem[ROW_KEY] || !lastItem[ROW_KEY]) { |
|||
unref(dataSourceRef).forEach((item) => { |
|||
if (!item[ROW_KEY]) { |
|||
item[ROW_KEY] = buildUUID(); |
|||
} |
|||
if (item.children && item.children.length) { |
|||
setTableKey(item.children); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
return unref(dataSourceRef); |
|||
}); |
|||
|
|||
async function fetch(opt?: FetchParams) { |
|||
const { api, searchInfo, fetchSetting, beforeFetch, afterFetch, useSearchForm } = unref( |
|||
propsRef |
|||
); |
|||
if (!api && !isFunction(api)) return; |
|||
try { |
|||
loadingRef.value = true; |
|||
const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING; |
|||
let pageParams: any = {}; |
|||
if (isBoolean(getPaginationRef)) { |
|||
pageParams = {}; |
|||
} else { |
|||
const { current, pageSize } = unref(getPaginationRef) as PaginationProps; |
|||
pageParams[pageField] = opt?.page || current; |
|||
pageParams[sizeField] = pageSize; |
|||
} |
|||
|
|||
let params: any = { |
|||
...pageParams, |
|||
...(useSearchForm ? getFieldsValue() : {}), |
|||
...searchInfo, |
|||
...(opt ? opt.searchInfo : {}), |
|||
}; |
|||
if (beforeFetch && isFunction(beforeFetch)) { |
|||
params = beforeFetch(params) || params; |
|||
} |
|||
|
|||
const res = await api(params); |
|||
let resultItems: any[] = get(res, listField); |
|||
const resultTotal: number = get(res, totalField); |
|||
if (afterFetch && isFunction(afterFetch)) { |
|||
resultItems = afterFetch(resultItems) || resultItems; |
|||
} |
|||
|
|||
dataSourceRef.value = resultItems; |
|||
setPagination({ |
|||
total: resultTotal || 0, |
|||
}); |
|||
if (opt && opt.page) { |
|||
setPagination({ |
|||
current: opt.page || 1, |
|||
}); |
|||
} |
|||
emit('fetch-success', { |
|||
items: unref(resultItems), |
|||
total: resultTotal, |
|||
}); |
|||
} catch (error) { |
|||
emit('fetch-error', error); |
|||
dataSourceRef.value = []; |
|||
setPagination({ |
|||
total: 0, |
|||
}); |
|||
} finally { |
|||
loadingRef.value = false; |
|||
} |
|||
} |
|||
|
|||
function setTableData(values: any[]) { |
|||
dataSourceRef.value = values; |
|||
} |
|||
onMounted(() => { |
|||
// 转异步任务
|
|||
useTimeout(() => { |
|||
unref(propsRef).immediate && fetch(); |
|||
}, 0); |
|||
}); |
|||
|
|||
return { getDataSourceRef, setTableData, getAutoCreateKey, fetch: fetch }; |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
import { watch, ref, ComputedRef, unref } from 'vue'; |
|||
import { BasicTableProps } from '../types/table'; |
|||
import { useProps } from './useProps'; |
|||
export function useLoading(refProps: ComputedRef<BasicTableProps>) { |
|||
const { propsRef } = useProps(refProps); |
|||
|
|||
const loadingRef = ref(unref(propsRef).loading); |
|||
watch( |
|||
() => unref(propsRef).loading, |
|||
(v: boolean) => { |
|||
loadingRef.value = v; |
|||
} |
|||
); |
|||
return { loadingRef }; |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
import { computed, unref, ref, ComputedRef } from 'vue'; |
|||
import { PaginationProps } from '../types/pagination'; |
|||
import { isBoolean } from '/@/utils/is'; |
|||
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; |
|||
|
|||
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const'; |
|||
import { useProps } from './useProps'; |
|||
import { BasicTableProps } from '../..'; |
|||
export function usePagination(refProps: ComputedRef<BasicTableProps>) { |
|||
const configRef = ref<PaginationProps>({}); |
|||
const { propsRef } = useProps(refProps); |
|||
|
|||
const getPaginationRef = computed((): PaginationProps | false => { |
|||
const { pagination } = unref(propsRef); |
|||
if (isBoolean(pagination) && !pagination) { |
|||
return false; |
|||
} |
|||
return { |
|||
current: 1, |
|||
pageSize: PAGE_SIZE, |
|||
size: 'small', |
|||
defaultPageSize: PAGE_SIZE, |
|||
showTotal: (total) => `共 ${total} 条数据`, |
|||
showSizeChanger: true, |
|||
pageSizeOptions: PAGE_SIZE_OPTIONS, |
|||
itemRender: ({ page, type, originalElement }) => { |
|||
if (type === 'prev') { |
|||
if (page === 0) { |
|||
return null; |
|||
} |
|||
return <LeftOutlined />; |
|||
} else if (type === 'next') { |
|||
if (page === 1) { |
|||
return null; |
|||
} |
|||
return <RightOutlined />; |
|||
} |
|||
return originalElement; |
|||
}, |
|||
showQuickJumper: true, |
|||
...(isBoolean(pagination) ? {} : pagination), |
|||
...unref(configRef), |
|||
}; |
|||
}); |
|||
|
|||
function setPagination(info: Partial<PaginationProps>) { |
|||
configRef.value = { |
|||
...unref(getPaginationRef), |
|||
...info, |
|||
}; |
|||
} |
|||
return { getPaginationRef, setPagination }; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/* |
|||
* @description: |
|||
* @author: wenbin.chen |
|||
* @Date: 2020-05-12 13:20:26 |
|||
* @LastEditors: vben |
|||
* @LastEditTime: 2020-10-07 14:52:34 |
|||
* @email: 190848757@qq.com |
|||
*/ |
|||
|
|||
import { Ref, ref, watch, unref } from 'vue'; |
|||
import { BasicTableProps } from '../types/table'; |
|||
|
|||
/** |
|||
* @description: |
|||
* @Date: 2020-05-12 13:20:37 |
|||
*/ |
|||
export function useProps(props: Readonly<Ref<BasicTableProps>>) { |
|||
const propsRef = (ref<BasicTableProps>(unref(props)) as unknown) as Ref<BasicTableProps>; |
|||
watch( |
|||
() => props.value, |
|||
(v) => { |
|||
propsRef.value = unref(v); |
|||
}, |
|||
{ |
|||
immediate: false, |
|||
} |
|||
); |
|||
return { propsRef }; |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import { provide, inject } from 'vue'; |
|||
import { TableActionType } from '../types/table'; |
|||
|
|||
const key = Symbol('table'); |
|||
|
|||
export function provideTable(instance: TableActionType) { |
|||
provide(key, instance); |
|||
} |
|||
|
|||
export function injectTable(): TableActionType { |
|||
return inject(key) as TableActionType; |
|||
} |
|||
@ -0,0 +1,63 @@ |
|||
import { computed, ref, unref, ComputedRef } from 'vue'; |
|||
import { BasicTableProps } from '../types/table'; |
|||
import { TableRowSelection } from 'ant-design-vue/types/table/table'; |
|||
import { useProps } from './useProps'; |
|||
|
|||
/* eslint-disable */ |
|||
export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: EmitType) { |
|||
const { propsRef } = useProps(refProps); |
|||
|
|||
const selectedRowKeysRef = ref<string[]>([]); |
|||
const selectedRowRef = ref<any[]>([]); |
|||
|
|||
const getRowSelectionRef = computed((): TableRowSelection<any> | null => { |
|||
const rowSelection = unref(propsRef).rowSelection; |
|||
if (!rowSelection) { |
|||
return null; |
|||
} |
|||
return { |
|||
selectedRowKeys: unref(selectedRowKeysRef), |
|||
hideDefaultSelections: false, |
|||
onChange: (selectedRowKeys: string[], selectedRows: any[]) => { |
|||
selectedRowKeysRef.value = selectedRowKeys; |
|||
selectedRowRef.value = selectedRows; |
|||
emit('selection-change', { |
|||
keys: selectedRowKeys, |
|||
rows: selectedRows, |
|||
}); |
|||
}, |
|||
...rowSelection, |
|||
}; |
|||
}); |
|||
function setSelectedRowKeys(rowKeys: string[]) { |
|||
selectedRowKeysRef.value = rowKeys; |
|||
} |
|||
|
|||
function clearSelectedRowKeys() { |
|||
selectedRowRef.value = []; |
|||
selectedRowKeysRef.value = []; |
|||
} |
|||
|
|||
function deleteSelectRowByKey(key: string) { |
|||
const selectedRowKeys = unref(selectedRowKeysRef); |
|||
const index = selectedRowKeys.findIndex((item) => item === key); |
|||
if (index !== -1) { |
|||
unref(selectedRowKeysRef).splice(index, 1); |
|||
} |
|||
} |
|||
function getSelectRowKeys() { |
|||
return unref(selectedRowKeysRef); |
|||
} |
|||
function getSelectRows() { |
|||
return unref(selectedRowRef); |
|||
} |
|||
|
|||
return { |
|||
getRowSelectionRef, |
|||
getSelectRows, |
|||
getSelectRowKeys, |
|||
setSelectedRowKeys, |
|||
clearSelectedRowKeys, |
|||
deleteSelectRowByKey, |
|||
}; |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
import type { BasicTableProps, TableActionType, FetchParams, BasicColumn } from '../types/table'; |
|||
import type { PaginationProps } from '../types/pagination'; |
|||
import { ref, getCurrentInstance, onUnmounted, unref } from 'vue'; |
|||
import { isProdMode } from '/@/utils/env'; |
|||
|
|||
export function useTable( |
|||
tableProps?: Partial<BasicTableProps> |
|||
): [(instance: TableActionType) => void, TableActionType] { |
|||
if (!getCurrentInstance()) { |
|||
throw new Error('Please put useTable function in the setup function!'); |
|||
} |
|||
|
|||
const tableRef = ref<TableActionType | null>(null); |
|||
const loadedRef = ref<boolean | null>(false); |
|||
|
|||
function register(instance: TableActionType) { |
|||
onUnmounted(() => { |
|||
tableRef.value = null; |
|||
loadedRef.value = null; |
|||
}); |
|||
if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) { |
|||
return; |
|||
} |
|||
tableRef.value = instance; |
|||
tableProps && instance.setProps(tableProps); |
|||
loadedRef.value = true; |
|||
} |
|||
|
|||
function getTableInstance(): TableActionType { |
|||
const table = unref(tableRef); |
|||
if (!table) { |
|||
throw new Error('table is undefined!'); |
|||
} |
|||
return table; |
|||
} |
|||
|
|||
const methods: TableActionType = { |
|||
reload: (opt?: FetchParams) => { |
|||
getTableInstance().reload(opt); |
|||
}, |
|||
setProps: (props: Partial<BasicTableProps>) => { |
|||
getTableInstance().setProps(props); |
|||
}, |
|||
redoHeight: () => { |
|||
getTableInstance().redoHeight(); |
|||
}, |
|||
setLoading: (loading: boolean) => { |
|||
getTableInstance().setLoading(loading); |
|||
}, |
|||
getDataSource: () => { |
|||
return getTableInstance().getDataSource(); |
|||
}, |
|||
getColumns: ({ ignoreIndex = false }: { ignoreIndex?: boolean } = {}) => { |
|||
const columns = getTableInstance().getColumns({ ignoreIndex }) || []; |
|||
|
|||
return columns; |
|||
}, |
|||
setColumns: (columns: BasicColumn[]) => { |
|||
getTableInstance().setColumns(columns); |
|||
}, |
|||
setTableData: (values: any[]) => { |
|||
return getTableInstance().setTableData(values); |
|||
}, |
|||
setPagination: (info: Partial<PaginationProps>) => { |
|||
return getTableInstance().setPagination(info); |
|||
}, |
|||
deleteSelectRowByKey: (key: string) => { |
|||
getTableInstance().deleteSelectRowByKey(key); |
|||
}, |
|||
getSelectRowKeys: () => { |
|||
return getTableInstance().getSelectRowKeys(); |
|||
}, |
|||
getSelectRows: () => { |
|||
return getTableInstance().getSelectRows(); |
|||
}, |
|||
clearSelectedRowKeys: () => { |
|||
getTableInstance().clearSelectedRowKeys(); |
|||
}, |
|||
setSelectedRowKeys: (keys: string[] | number[]) => { |
|||
getTableInstance().setSelectedRowKeys(keys); |
|||
}, |
|||
getPaginationRef: () => { |
|||
return getTableInstance().getPaginationRef(); |
|||
}, |
|||
} as TableActionType; |
|||
|
|||
return [register, methods]; |
|||
} |
|||
@ -0,0 +1,134 @@ |
|||
import { BasicTableProps } from '../types/table'; |
|||
import { computed, Ref, onMounted, unref, ref, nextTick, ComputedRef, watch } from 'vue'; |
|||
import { getViewportOffset } from '/@/utils/domUtils'; |
|||
import { triggerWindowResize } from '/@/utils/event/triggerWindowResizeEvent'; |
|||
import { isBoolean } from '/@/utils/is'; |
|||
import { useTimeout } from '/@/hooks/core/useTimeout'; |
|||
import { useWindowSizeFn } from '/@/hooks/event/useWindowSize'; |
|||
import { useProps } from './useProps'; |
|||
|
|||
export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRef: Ref<any>) { |
|||
const { propsRef } = useProps(refProps); |
|||
|
|||
const tableHeightRef: Ref<number | null> = ref(null); |
|||
|
|||
watch( |
|||
() => unref(propsRef).canResize, |
|||
() => { |
|||
redoHeight(); |
|||
} |
|||
); |
|||
function redoHeight() { |
|||
const { canResize } = unref(propsRef); |
|||
|
|||
if (!canResize) { |
|||
return; |
|||
} |
|||
calcTableHeight(); |
|||
} |
|||
|
|||
async function calcTableHeight(cb?: () => void) { |
|||
const { canResize, resizeHeightOffset, pagination, maxHeight } = unref(propsRef); |
|||
if (!canResize) { |
|||
return; |
|||
} |
|||
await nextTick(); |
|||
const table = unref(tableElRef) as any; |
|||
|
|||
if (!table) { |
|||
return; |
|||
} |
|||
const tableEl: Element = table.$el; |
|||
if (!tableEl) { |
|||
return; |
|||
} |
|||
const el: HTMLElement | null = tableEl.querySelector('.ant-table-thead '); |
|||
// const layoutMain: Element | null = document.querySelector('.default-layout__main ');
|
|||
if (!el) { |
|||
return; |
|||
} |
|||
// 表格距离底部高度
|
|||
const { bottomIncludeBody } = getViewportOffset(el); |
|||
// 表格高度+距离底部高度-自定义偏移量
|
|||
|
|||
const paddingHeight = 32; |
|||
const borderHeight = 2 * 2; |
|||
// 分页器高度
|
|||
|
|||
// TODO 先固定20
|
|||
const paginationHeight = 20; |
|||
// if (!isBoolean(pagination)) {
|
|||
// const paginationDom = tableEl.querySelector('.ant-pagination') as HTMLElement;
|
|||
// if (paginationDom) {
|
|||
// const offsetHeight = paginationDom.offsetHeight;
|
|||
// paginationHeight += offsetHeight || 0;
|
|||
// }
|
|||
// }
|
|||
|
|||
let footerHeight = 0; |
|||
if (!isBoolean(pagination)) { |
|||
const footerEl = tableEl.querySelector('.ant-table-footer') as HTMLElement; |
|||
if (footerEl) { |
|||
const offsetHeight = footerEl.offsetHeight; |
|||
footerHeight += offsetHeight || 0; |
|||
} |
|||
} |
|||
let headerHeight = 0; |
|||
if (el) { |
|||
headerHeight = (el as HTMLElement).offsetHeight; |
|||
} |
|||
tableHeightRef.value = |
|||
bottomIncludeBody - |
|||
(resizeHeightOffset || 0) - |
|||
paddingHeight - |
|||
borderHeight - |
|||
paginationHeight - |
|||
footerHeight - |
|||
headerHeight; |
|||
useTimeout(() => { |
|||
tableHeightRef.value = |
|||
tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value; |
|||
cb && cb(); |
|||
}, 0); |
|||
} |
|||
|
|||
const getCanResize = computed(() => { |
|||
const { canResize, scroll } = unref(propsRef); |
|||
return canResize && !(scroll || {}).y; |
|||
}); |
|||
|
|||
useWindowSizeFn(calcTableHeight, 100); |
|||
|
|||
// function clear() {
|
|||
// window.clearInterval(timer);
|
|||
// }
|
|||
|
|||
onMounted(() => { |
|||
if (unref(getCanResize)) { |
|||
calcTableHeight(); |
|||
const hasFixedLeft = (unref(propsRef).columns || []).some((item) => item.fixed === 'left'); |
|||
// TODO antv table问题情况太多,只能先用下面方式定时器hack
|
|||
useTimeout(() => { |
|||
calcTableHeight(() => { |
|||
// 有左侧固定列的时候才有问题
|
|||
hasFixedLeft && |
|||
useTimeout(() => { |
|||
triggerWindowResize(); |
|||
}, 300); |
|||
}); |
|||
}, 200); |
|||
} |
|||
}); |
|||
const getScrollRef = computed(() => { |
|||
const tableHeight = unref(tableHeightRef); |
|||
const { canResize, scroll } = unref(propsRef); |
|||
|
|||
return { |
|||
x: '100%', |
|||
y: canResize ? tableHeight : null, |
|||
scrollToFirstRowOnChange: false, |
|||
...scroll, |
|||
}; |
|||
}); |
|||
return { getScrollRef, redoHeight }; |
|||
} |
|||
@ -0,0 +1,155 @@ |
|||
import { PropType } from 'vue'; |
|||
import { PaginationProps } from './types/pagination'; |
|||
import { BasicColumn, FetchSetting } from './types/table'; |
|||
import { TableCustomRecord, TableRowSelection } from 'ant-design-vue/types/table/table'; |
|||
import { FormProps } from '/@/components/Form/index'; |
|||
import { FETCH_SETTING } from './const'; |
|||
|
|||
// 注释看 types/table
|
|||
export const basicProps = { |
|||
autoCreateKey: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
striped: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
showSummary: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: false, |
|||
}, |
|||
|
|||
summaryFunc: { |
|||
type: [Function, Array] as PropType<(...arg: any[]) => any[]>, |
|||
default: null, |
|||
}, |
|||
|
|||
canColDrag: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
isTreeTable: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: false, |
|||
}, |
|||
api: { |
|||
type: Function as PropType<(...arg: any[]) => Promise<any>>, |
|||
default: null, |
|||
}, |
|||
beforeFetch: { |
|||
type: Function as PropType<Fn>, |
|||
default: null, |
|||
}, |
|||
afterFetch: { |
|||
type: Function as PropType<Fn>, |
|||
default: null, |
|||
}, |
|||
handleSearchInfoFn: { |
|||
type: Function as PropType<Fn>, |
|||
default: null, |
|||
}, |
|||
fetchSetting: { |
|||
type: Object as PropType<FetchSetting>, |
|||
default: () => { |
|||
return FETCH_SETTING; |
|||
}, |
|||
}, |
|||
// 立即请求接口
|
|||
immediate: { type: Boolean as PropType<boolean>, default: true }, |
|||
|
|||
emptyDataIsShowTable: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
// 额外的请求参数
|
|||
searchInfo: { |
|||
type: Object as PropType<any>, |
|||
default: null, |
|||
}, |
|||
// 使用搜索表单
|
|||
useSearchForm: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: false, |
|||
}, |
|||
// 表单配置
|
|||
formConfig: { |
|||
type: Object as PropType<Partial<FormProps>>, |
|||
default: null, |
|||
}, |
|||
columns: { |
|||
type: [Array] as PropType<BasicColumn[]>, |
|||
default: null, |
|||
}, |
|||
showIndexColumn: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
indexColumnProps: { |
|||
type: Object as PropType<BasicColumn>, |
|||
default: null, |
|||
}, |
|||
actionColumn: { |
|||
type: Object as PropType<BasicColumn>, |
|||
default: null, |
|||
}, |
|||
ellipsis: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
canResize: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
clearSelectOnPageChange: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: false, |
|||
}, |
|||
resizeHeightOffset: { |
|||
type: Number as PropType<number>, |
|||
default: 0, |
|||
}, |
|||
rowSelection: { |
|||
type: Object as PropType<TableRowSelection<any> | null>, |
|||
default: null, |
|||
}, |
|||
title: { |
|||
type: [String, Function] as PropType<string | ((data: any) => any)>, |
|||
default: null, |
|||
}, |
|||
titleHelpMessage: { |
|||
type: [String, Array] as PropType<string | string[]>, |
|||
}, |
|||
maxHeight: { |
|||
type: Number as PropType<number>, |
|||
}, |
|||
dataSource: { |
|||
type: Array as PropType<any[]>, |
|||
default: null, |
|||
}, |
|||
rowKey: { |
|||
type: [String, Function] as PropType<string | ((record: any) => string)>, |
|||
default: '', |
|||
}, |
|||
bordered: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: true, |
|||
}, |
|||
pagination: { |
|||
type: [Object, Boolean] as PropType<PaginationProps | boolean>, |
|||
default: null, |
|||
}, |
|||
|
|||
loading: { |
|||
type: Boolean as PropType<boolean>, |
|||
default: false, |
|||
}, |
|||
rowClassName: { |
|||
type: Function as PropType<(record: TableCustomRecord<any>, index: number) => string>, |
|||
}, |
|||
|
|||
scroll: { |
|||
type: Object as PropType<{ x: number | true; y: number }>, |
|||
default: null, |
|||
}, |
|||
}; |
|||
@ -0,0 +1,41 @@ |
|||
@import (reference) '../../../../design/index.less'; |
|||
|
|||
@prefix-cls: ~'editable-cell'; |
|||
|
|||
.@{prefix-cls} { |
|||
position: relative; |
|||
|
|||
&__wrapper { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
&__icon { |
|||
&:hover { |
|||
transform: scale(1.2); |
|||
|
|||
svg { |
|||
color: @primary-color; |
|||
} |
|||
} |
|||
} |
|||
|
|||
&__normal { |
|||
padding-right: 48px; |
|||
|
|||
&-icon { |
|||
position: absolute; |
|||
top: 4px; |
|||
right: 0; |
|||
display: none; |
|||
width: 20px; |
|||
cursor: pointer; |
|||
} |
|||
} |
|||
|
|||
&:hover { |
|||
.@{prefix-cls}__normal-icon { |
|||
display: inline-block; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,228 @@ |
|||
@import (reference) '../../../../design/index.less'; |
|||
@border-color: hsla(0, 0%, 80.8%, 0.25); |
|||
|
|||
.basic-table { |
|||
&-title { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
} |
|||
|
|||
&-row__striped { |
|||
td { |
|||
background: #fafafa; |
|||
} |
|||
} |
|||
|
|||
&-img__preview { |
|||
display: flex; |
|||
|
|||
img { |
|||
margin-right: 4px; |
|||
} |
|||
} |
|||
|
|||
&-action { |
|||
display: flex; |
|||
} |
|||
|
|||
&-toolbar { |
|||
> * { |
|||
margin-right: 10px; |
|||
} |
|||
} |
|||
|
|||
.resize-table-th { |
|||
position: relative !important; |
|||
|
|||
.table-draggable-handle { |
|||
position: absolute; |
|||
right: -5px; |
|||
bottom: 0; |
|||
left: auto !important; |
|||
height: 100% !important; |
|||
cursor: col-resize; |
|||
transform: none !important; |
|||
touch-action: none; |
|||
} |
|||
} |
|||
|
|||
&-drag-body { |
|||
position: relative; |
|||
cursor: move; |
|||
} |
|||
|
|||
.drag-line td { |
|||
border-top: 2px dashed @primary-color; |
|||
} |
|||
|
|||
.ant-table-wrapper { |
|||
padding: 8px; |
|||
background: #fff; |
|||
border-radius: 2px; |
|||
|
|||
.ant-table-title { |
|||
padding: 0 0 10px 0 !important; |
|||
} |
|||
|
|||
.ant-table.ant-table-bordered .ant-table-title { |
|||
border: none !important; |
|||
} |
|||
} |
|||
|
|||
// |
|||
.ant-table { |
|||
&-title { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 8px 6px; |
|||
} |
|||
|
|||
.ant-table-thead > tr > th, |
|||
.ant-table-header { |
|||
background: #f1f3f4; |
|||
} |
|||
|
|||
.ant-table-tbody > tr.ant-table-row-selected td { |
|||
background: fade(@primary-color, 8%) !important; |
|||
} |
|||
} |
|||
|
|||
.ant-table-bordered .ant-table-header > table, |
|||
.ant-table-bordered .ant-table-body > table, |
|||
.ant-table-bordered .ant-table-fixed-left table, |
|||
.ant-table-bordered .ant-table-fixed-right table { |
|||
border: 1px solid @border-color; |
|||
} |
|||
|
|||
.ant-table-thead { |
|||
th { |
|||
border: none; |
|||
} |
|||
} |
|||
|
|||
.ant-table-bordered .ant-table-tbody > tr > td { |
|||
border-bottom: 1px solid @border-color; |
|||
|
|||
&:last-child { |
|||
border-right: none !important; |
|||
} |
|||
} |
|||
|
|||
.ant-table.ant-table-bordered .ant-table-footer, |
|||
.ant-table.ant-table-bordered .ant-table-title { |
|||
border: 1px solid @border-color !important; |
|||
} |
|||
|
|||
.ant-table-bordered.ant-table-empty .ant-table-placeholder { |
|||
border: 1px solid @border-color !important; |
|||
} |
|||
|
|||
.ant-table td { |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.ant-table-row-cell-last { |
|||
border-right: none !important; |
|||
} |
|||
|
|||
.ant-table-bordered .ant-table-thead > tr > th, |
|||
.ant-table-bordered .ant-table-tbody > tr > td { |
|||
border-right: 1px solid @border-color; |
|||
} |
|||
|
|||
.ant-table-thead > tr > th, |
|||
.ant-table-tbody > tr > td { |
|||
padding: 9px 8px !important; |
|||
} |
|||
|
|||
.ant-pagination { |
|||
margin: 10px 0 0 0; |
|||
} |
|||
|
|||
.ant-table-body { |
|||
overflow-x: auto !important; |
|||
overflow-y: scroll !important; |
|||
} |
|||
|
|||
.ant-table-header { |
|||
margin-bottom: 0 !important; |
|||
overflow-x: hidden !important; |
|||
overflow-y: scroll !important; |
|||
} |
|||
|
|||
.ant-table-fixed-right .ant-table-header { |
|||
border-left: 1px solid @border-color; |
|||
|
|||
.ant-table-fixed { |
|||
border-bottom: none; |
|||
} |
|||
} |
|||
|
|||
.ant-table-fixed-left { |
|||
.ant-table-header { |
|||
overflow-y: hidden !important; |
|||
} |
|||
|
|||
.ant-table-fixed { |
|||
border-bottom: none; |
|||
} |
|||
} |
|||
|
|||
.ant-radio { |
|||
&-inner { |
|||
border-color: @text-color-base; |
|||
} |
|||
} |
|||
|
|||
.ant-checkbox { |
|||
&:not(.ant-checkbox-checked) { |
|||
.ant-checkbox-inner { |
|||
border-color: @text-color-base; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.ant-table-bordered .ant-table-thead > tr:not(:last-child) > th, |
|||
.ant-table-tbody > tr > td { |
|||
word-break: break-word; |
|||
border-color: @border-color; |
|||
} |
|||
|
|||
.ant-table-footer { |
|||
padding: 0; |
|||
|
|||
.ant-table-wrapper { |
|||
padding: 0; |
|||
} |
|||
|
|||
table { |
|||
border: none !important; |
|||
} |
|||
|
|||
.ant-table-body { |
|||
overflow-x: hidden !important; |
|||
overflow-y: scroll !important; |
|||
} |
|||
|
|||
td { |
|||
padding: 12px 8px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.table-form-container { |
|||
padding: 16px; |
|||
|
|||
.ant-form { |
|||
padding: 12px 12px 4px 12px; |
|||
margin-bottom: 12px; |
|||
background: #fff; |
|||
border-radius: 2px; |
|||
} |
|||
|
|||
.ant-table-wrapper { |
|||
border-radius: 2px; |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
export type ComponentType = |
|||
| 'Input' |
|||
| 'InputPassword' |
|||
| 'InputNumber' |
|||
| 'Select' |
|||
| 'Checkbox' |
|||
| 'CheckboxGroup' |
|||
| 'Switch'; |
|||
@ -0,0 +1,89 @@ |
|||
import { VNodeChild } from 'vue'; |
|||
import { PaginationRenderProps } from 'ant-design-vue/types/pagination'; |
|||
export interface PaginationProps { |
|||
/** |
|||
* total number of data items |
|||
* @default 0 |
|||
* @type number |
|||
*/ |
|||
total?: number; |
|||
|
|||
/** |
|||
* default initial page number |
|||
* @default 1 |
|||
* @type number |
|||
*/ |
|||
defaultCurrent?: number; |
|||
|
|||
/** |
|||
* current page number |
|||
* @type number |
|||
*/ |
|||
current?: number; |
|||
|
|||
/** |
|||
* default number of data items per page |
|||
* @default 10 |
|||
* @type number |
|||
*/ |
|||
defaultPageSize?: number; |
|||
|
|||
/** |
|||
* number of data items per page |
|||
* @type number |
|||
*/ |
|||
pageSize?: number; |
|||
|
|||
/** |
|||
* Whether to hide pager on single page |
|||
* @default false |
|||
* @type boolean |
|||
*/ |
|||
hideOnSinglePage?: boolean; |
|||
|
|||
/** |
|||
* determine whether pageSize can be changed |
|||
* @default false |
|||
* @type boolean |
|||
*/ |
|||
showSizeChanger?: boolean; |
|||
|
|||
/** |
|||
* specify the sizeChanger options |
|||
* @default ['10', '20', '30', '40'] |
|||
* @type string[] |
|||
*/ |
|||
pageSizeOptions?: string[]; |
|||
|
|||
/** |
|||
* determine whether you can jump to pages directly |
|||
* @default false |
|||
* @type boolean |
|||
*/ |
|||
showQuickJumper?: boolean | object; |
|||
|
|||
/** |
|||
* to display the total number and range |
|||
* @type Function |
|||
*/ |
|||
showTotal?: (total: number, range: [number, number]) => any; |
|||
|
|||
/** |
|||
* specify the size of Pagination, can be set to small |
|||
* @default '' |
|||
* @type string |
|||
*/ |
|||
size?: string; |
|||
|
|||
/** |
|||
* whether to use simple mode |
|||
* @type boolean |
|||
*/ |
|||
simple?: boolean; |
|||
|
|||
/** |
|||
* to customize item innerHTML |
|||
* @type Function |
|||
*/ |
|||
itemRender?: (props: PaginationRenderProps) => VNodeChild | JSX.Element; |
|||
} |
|||
@ -0,0 +1,315 @@ |
|||
import { VNodeChild } from 'vue'; |
|||
import { PaginationProps } from './pagination'; |
|||
import { FormProps } from '/@/components/Form/index'; |
|||
import { |
|||
ExpandedRowRenderRecord, |
|||
PaginationConfig, |
|||
SorterResult, |
|||
TableCurrentDataSource, |
|||
TableCustomRecord, |
|||
TableRowSelection, |
|||
} from 'ant-design-vue/types/table/table'; |
|||
import { ColumnProps } from 'ant-design-vue/types/table/column'; |
|||
import { ComponentType } from './componentType'; |
|||
export declare type SortOrder = 'ascend' | 'descend'; |
|||
export interface ColumnFilterItem { |
|||
text?: string; |
|||
value?: string; |
|||
children?: any; |
|||
} |
|||
|
|||
export interface RenderEditableCellParams { |
|||
dataIndex: string; |
|||
component?: ComponentType; |
|||
componentOn?: { [key: string]: Fn }; |
|||
componentProps?: any; |
|||
} |
|||
|
|||
export interface FetchParams { |
|||
searchInfo?: any; |
|||
page?: number; |
|||
} |
|||
|
|||
export interface GetColumnsParams { |
|||
ignoreIndex?: boolean; |
|||
} |
|||
export interface TableActionType { |
|||
reload: (opt?: FetchParams) => Promise<void>; |
|||
getSelectRows: () => any[]; |
|||
clearSelectedRowKeys: () => void; |
|||
getSelectRowKeys: () => string[]; |
|||
deleteSelectRowByKey: (key: string) => void; |
|||
setPagination: (info: Partial<PaginationProps>) => void; |
|||
setTableData: (values: any[]) => void; |
|||
getColumns: ({ ignoreIndex }?: GetColumnsParams) => BasicColumn[]; |
|||
setColumns: (columns: BasicColumn[] | string[]) => void; |
|||
getDataSource: () => any[]; |
|||
setLoading: (loading: boolean) => void; |
|||
setProps: (props: Partial<BasicTableProps>) => void; |
|||
redoHeight: () => void; |
|||
setSelectedRowKeys: (rowKeys: string[] | number[]) => void; |
|||
getPaginationRef: () => PaginationProps | boolean; |
|||
} |
|||
|
|||
export interface FetchSetting { |
|||
// 请求接口当前页数
|
|||
pageField: string; |
|||
// 每页显示多少条
|
|||
sizeField: string; |
|||
// 请求结果列表字段 支持 a.b.c
|
|||
listField: string; |
|||
// 请求结果总数字段 支持 a.b.c
|
|||
totalField: string; |
|||
} |
|||
export interface BasicTableProps<T = any> { |
|||
// 斑马纹
|
|||
striped?: boolean; |
|||
// 是否自动生成key
|
|||
autoCreateKey?: boolean; |
|||
// 计算合计行的方法
|
|||
summaryFunc?: (...arg: any) => any[]; |
|||
// 是否显示合计行
|
|||
showSummary?: boolean; |
|||
// 是否可拖拽列
|
|||
canColDrag?: boolean; |
|||
// 是否树表
|
|||
isTreeTable?: boolean; |
|||
// 接口请求对象
|
|||
api?: (...arg: any) => Promise<any>; |
|||
// 请求之前处理参数
|
|||
beforeFetch?: Fn; |
|||
// 自定义处理接口返回参数
|
|||
afterFetch?: Fn; |
|||
// 查询条件请求之前处理
|
|||
handleSearchInfoFn?: Fn; |
|||
// 请求接口配置
|
|||
fetchSetting?: FetchSetting; |
|||
// 立即请求接口
|
|||
immediate?: boolean; |
|||
// 在开起搜索表单的时候,如果没有数据是否显示表格
|
|||
emptyDataIsShowTable?: boolean; |
|||
// 额外的请求参数
|
|||
searchInfo?: any; |
|||
|
|||
// 使用搜索表单
|
|||
useSearchForm?: boolean; |
|||
// 表单配置
|
|||
formConfig?: FormProps; |
|||
// 列配置
|
|||
columns: BasicColumn[]; |
|||
// 是否显示序号列
|
|||
showIndexColumn?: boolean; |
|||
// 序号列配置
|
|||
indexColumnProps?: BasicColumn; |
|||
actionColumn?: BasicColumn; |
|||
// 文本超过宽度是否显示。。。
|
|||
ellipsis?: boolean; |
|||
// 是否可以自适应高度
|
|||
canResize?: boolean; |
|||
// 自适应高度偏移, 计算结果-偏移量
|
|||
resizeHeightOffset?: number; |
|||
|
|||
// 在分页改变的时候清空选项
|
|||
clearSelectOnPageChange?: boolean; |
|||
//
|
|||
rowKey?: string | ((record: any) => string); |
|||
// 数据
|
|||
dataSource?: any[]; |
|||
// 标题右侧提示
|
|||
titleHelpMessage?: string | string[]; |
|||
// 表格滚动最大高度
|
|||
maxHeight?: number; |
|||
// 是否显示边框
|
|||
bordered?: boolean; |
|||
// 分页配置
|
|||
pagination?: PaginationProps | boolean; |
|||
// loading加载
|
|||
loading?: boolean; |
|||
|
|||
/** |
|||
* The column contains children to display |
|||
* @default 'children' |
|||
* @type string | string[] |
|||
*/ |
|||
childrenColumnName?: string | string[]; |
|||
|
|||
/** |
|||
* Override default table elements |
|||
* @type object |
|||
*/ |
|||
components?: object; |
|||
|
|||
/** |
|||
* Expand all rows initially |
|||
* @default false |
|||
* @type boolean |
|||
*/ |
|||
defaultExpandAllRows?: boolean; |
|||
|
|||
/** |
|||
* Initial expanded row keys |
|||
* @type string[] |
|||
*/ |
|||
defaultExpandedRowKeys?: string[]; |
|||
|
|||
/** |
|||
* Current expanded row keys |
|||
* @type string[] |
|||
*/ |
|||
expandedRowKeys?: string[]; |
|||
|
|||
/** |
|||
* Expanded container render for each row |
|||
* @type Function |
|||
*/ |
|||
expandedRowRender?: (record?: ExpandedRowRenderRecord<T>) => VNodeChild | JSX.Element; |
|||
|
|||
/** |
|||
* Customize row expand Icon. |
|||
* @type Function | VNodeChild |
|||
*/ |
|||
expandIcon?: Function | VNodeChild | JSX.Element; |
|||
|
|||
/** |
|||
* Whether to expand row by clicking anywhere in the whole row |
|||
* @default false |
|||
* @type boolean |
|||
*/ |
|||
expandRowByClick?: boolean; |
|||
|
|||
/** |
|||
* The index of `expandIcon` which column will be inserted when `expandIconAsCell` is false. default 0 |
|||
*/ |
|||
expandIconColumnIndex?: number; |
|||
|
|||
/** |
|||
* Table footer renderer |
|||
* @type Function | VNodeChild |
|||
*/ |
|||
footer?: Function | VNodeChild | JSX.Element; |
|||
|
|||
/** |
|||
* Indent size in pixels of tree data |
|||
* @default 15 |
|||
* @type number |
|||
*/ |
|||
indentSize?: number; |
|||
|
|||
/** |
|||
* i18n text including filter, sort, empty text, etc |
|||
* @default { filterConfirm: 'Ok', filterReset: 'Reset', emptyText: 'No Data' } |
|||
* @type object |
|||
*/ |
|||
locale?: object; |
|||
|
|||
/** |
|||
* Row's className |
|||
* @type Function |
|||
*/ |
|||
rowClassName?: (record: TableCustomRecord<T>) => string; |
|||
|
|||
/** |
|||
* Row selection config |
|||
* @type object |
|||
*/ |
|||
rowSelection?: TableRowSelection<T>; |
|||
|
|||
/** |
|||
* Set horizontal or vertical scrolling, can also be used to specify the width and height of the scroll area. |
|||
* It is recommended to set a number for x, if you want to set it to true, |
|||
* you need to add style .ant-table td { white-space: nowrap; }. |
|||
* @type object |
|||
*/ |
|||
scroll?: { x?: number | true; y?: number }; |
|||
|
|||
/** |
|||
* Whether to show table header |
|||
* @default true |
|||
* @type boolean |
|||
*/ |
|||
showHeader?: boolean; |
|||
|
|||
/** |
|||
* Size of table |
|||
* @default 'default' |
|||
* @type string |
|||
*/ |
|||
size?: 'default' | 'middle' | 'small' | 'large'; |
|||
|
|||
/** |
|||
* Table title renderer |
|||
* @type Function | ScopedSlot |
|||
*/ |
|||
title?: VNodeChild | JSX.Element; |
|||
|
|||
/** |
|||
* Set props on per header row |
|||
* @type Function |
|||
*/ |
|||
customHeaderRow?: (column: ColumnProps<T>, index: number) => object; |
|||
|
|||
/** |
|||
* Set props on per row |
|||
* @type Function |
|||
*/ |
|||
customRow?: (record: T, index: number) => object; |
|||
|
|||
/** |
|||
* `table-layout` attribute of table element |
|||
* `fixed` when header/columns are fixed, or using `column.ellipsis` |
|||
* |
|||
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout
|
|||
* @version 1.5.0 |
|||
*/ |
|||
tableLayout?: 'auto' | 'fixed' | string; |
|||
|
|||
/** |
|||
* the render container of dropdowns in table |
|||
* @param triggerNode |
|||
* @version 1.5.0 |
|||
*/ |
|||
getPopupContainer?: (triggerNode?: HTMLElement) => HTMLElement; |
|||
|
|||
/** |
|||
* Data can be changed again before rendering. |
|||
* The default configuration of general user empty data. |
|||
* You can configured globally through [ConfigProvider](https://antdv.com/components/config-provider-cn/)
|
|||
* |
|||
* @version 1.5.4 |
|||
*/ |
|||
transformCellText?: Function; |
|||
|
|||
/** |
|||
* Callback executed when pagination, filters or sorter is changed |
|||
* @param pagination |
|||
* @param filters |
|||
* @param sorter |
|||
* @param currentDataSource |
|||
*/ |
|||
onChange?: ( |
|||
pagination: PaginationConfig, |
|||
filters: Partial<Record<keyof T, string[]>>, |
|||
sorter: SorterResult<T>, |
|||
extra: TableCurrentDataSource<T> |
|||
) => void; |
|||
|
|||
/** |
|||
* Callback executed when the row expand icon is clicked |
|||
* |
|||
* @param expanded |
|||
* @param record |
|||
*/ |
|||
onExpand?: (expande: boolean, record: T) => void; |
|||
|
|||
/** |
|||
* Callback executed when the expanded rows change |
|||
* @param expandedRows |
|||
*/ |
|||
onExpandedRowsChange?: (expandedRows: string[] | number[]) => void; |
|||
} |
|||
|
|||
export interface BasicColumn<T = any> extends ColumnProps<T> { |
|||
children?: BasicColumn[]; |
|||
//
|
|||
flag?: 'INDEX' | 'DEFAULT' | 'CHECKBOX' | 'RADIO' | 'ACTION'; |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
export interface ActionItem { |
|||
on?: any; |
|||
label: string; |
|||
disabled?: boolean; |
|||
color?: 'success' | 'error' | 'warning'; |
|||
type?: string; |
|||
props?: any; |
|||
icon?: string; |
|||
popConfirm?: PopConfirm; |
|||
} |
|||
|
|||
export interface PopConfirm { |
|||
title: string; |
|||
okText?: string; |
|||
cancelText?: string; |
|||
confirm: any; |
|||
cancel?: any; |
|||
icon?: string; |
|||
} |
|||
@ -1,98 +1,96 @@ |
|||
body { |
|||
.ant-pagination { |
|||
&.mini { |
|||
.ant-pagination { |
|||
&.mini { |
|||
height: 20px; |
|||
font-size: 13px; |
|||
|
|||
.ant-pagination-prev, |
|||
.ant-pagination-next { |
|||
width: 20px; |
|||
height: 20px; |
|||
font-size: 13px; |
|||
|
|||
.ant-pagination-prev, |
|||
.ant-pagination-next { |
|||
width: 20px; |
|||
height: 20px; |
|||
min-width: 20px; |
|||
line-height: 17px; |
|||
color: @border-color-shallow-dark; |
|||
border: 1px solid; |
|||
} |
|||
min-width: 20px; |
|||
line-height: 20px; |
|||
color: @border-color-shallow-dark; |
|||
border: 1px solid; |
|||
} |
|||
|
|||
.ant-pagination-prev:hover, |
|||
.ant-pagination-next:hover, |
|||
.ant-pagination-item:focus, |
|||
.ant-pagination-item:hover { |
|||
color: @primary-color; |
|||
border: 1px solid @primary-color; |
|||
} |
|||
.ant-pagination-prev:hover, |
|||
.ant-pagination-next:hover, |
|||
.ant-pagination-item:focus, |
|||
.ant-pagination-item:hover { |
|||
color: @primary-color; |
|||
border: 1px solid @primary-color; |
|||
} |
|||
|
|||
.ant-pagination-item { |
|||
height: 20px; |
|||
min-width: 20px; |
|||
margin: 0 3px; |
|||
line-height: 20px; |
|||
.ant-pagination-item { |
|||
height: 20px; |
|||
min-width: 20px; |
|||
margin: 0 3px; |
|||
line-height: 20px; |
|||
|
|||
&:last-child { |
|||
margin-right: 0 !important; |
|||
} |
|||
&:last-child { |
|||
margin-right: 0 !important; |
|||
} |
|||
} |
|||
|
|||
.ant-pagination-item-active { |
|||
background: @primary-color; |
|||
.ant-pagination-item-active { |
|||
background: @primary-color; |
|||
|
|||
a { |
|||
color: @white; |
|||
} |
|||
} |
|||
|
|||
.ant-pagination-options { |
|||
margin-left: 20px; |
|||
a { |
|||
color: @white; |
|||
} |
|||
} |
|||
|
|||
.ant-select-sm .ant-select-selection--single { |
|||
height: 20px; |
|||
} |
|||
.ant-pagination-options { |
|||
margin-left: 20px; |
|||
} |
|||
|
|||
.ant-pagination-options, |
|||
.ant-pagination-total-text, |
|||
.ant-pagination-options-quick-jumper { |
|||
height: 20px; |
|||
line-height: 20px; |
|||
} |
|||
.ant-select-sm .ant-select-selection--single { |
|||
height: 20px; |
|||
} |
|||
|
|||
.ant-select-selection__rendered { |
|||
height: 18px; |
|||
line-height: 18px; |
|||
} |
|||
.ant-pagination-options, |
|||
.ant-pagination-total-text, |
|||
.ant-pagination-options-quick-jumper { |
|||
height: 20px; |
|||
line-height: 20px; |
|||
} |
|||
|
|||
.ant-pagination-total-text, |
|||
.ant-select-selection__rendered, |
|||
.ant-select-dropdown-menu-item, |
|||
.ant-pagination-options-quick-jumper { |
|||
font-size: 13px; |
|||
} |
|||
.ant-select-selection__rendered { |
|||
height: 18px; |
|||
line-height: 18px; |
|||
} |
|||
|
|||
.ant-pagination-options-quick-jumper input { |
|||
width: 40px; |
|||
height: 20px; |
|||
margin: 0 6px; |
|||
line-height: 20px; |
|||
text-align: center; |
|||
} |
|||
.ant-pagination-total-text, |
|||
.ant-select-selection__rendered, |
|||
.ant-select-dropdown-menu-item, |
|||
.ant-pagination-options-quick-jumper { |
|||
font-size: 13px; |
|||
} |
|||
|
|||
.ant-pagination-jump-prev, |
|||
.ant-pagination-jump-next { |
|||
height: 20px; |
|||
line-height: 20px; |
|||
} |
|||
.ant-pagination-options-quick-jumper input { |
|||
width: 40px; |
|||
height: 20px; |
|||
margin: 0 6px; |
|||
line-height: 20px; |
|||
text-align: center; |
|||
} |
|||
|
|||
.ant-pagination-options-size-changer.ant-select { |
|||
margin-right: 20px; |
|||
} |
|||
.ant-pagination-jump-prev, |
|||
.ant-pagination-jump-next { |
|||
height: 20px; |
|||
line-height: 20px; |
|||
} |
|||
|
|||
.ant-select-arrow { |
|||
color: @border-color-shallow-dark; |
|||
} |
|||
.ant-pagination-options-size-changer.ant-select { |
|||
margin-right: 20px; |
|||
} |
|||
|
|||
&-disabled { |
|||
display: none; |
|||
.ant-select-arrow { |
|||
color: @border-color-shallow-dark; |
|||
} |
|||
} |
|||
|
|||
&-disabled { |
|||
display: none; |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,70 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable |
|||
title="基础示例" |
|||
titleHelpMessage="温馨提醒" |
|||
:columns="columns" |
|||
:dataSource="data" |
|||
:canResize="canResize" |
|||
:loading="loading" |
|||
:striped="striped" |
|||
:bordered="border" |
|||
:pagination="{ pageSize: 20 }" |
|||
> |
|||
<template #toolbar> |
|||
<a-button type="primary" @click="toggleCanResize"> |
|||
{{ !canResize ? '自适应高度' : '取消自适应' }} |
|||
</a-button> |
|||
<a-button type="primary" @click="toggleBorder"> |
|||
{{ !border ? '显示边框' : '隐藏边框' }} |
|||
</a-button> |
|||
<a-button type="primary" @click="toggleLoading"> 开启loading </a-button> |
|||
<a-button type="primary" @click="toggleStriped"> |
|||
{{ !striped ? '显示斑马纹' : '隐藏斑马纹' }} |
|||
</a-button> |
|||
</template> |
|||
</BasicTable> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, ref } from 'vue'; |
|||
import { BasicTable } from '/@/components/Table'; |
|||
import { getBasicColumns, getBasicData } from './tableData'; |
|||
|
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const canResize = ref(false); |
|||
const loading = ref(false); |
|||
const striped = ref(true); |
|||
const border = ref(true); |
|||
function toggleCanResize() { |
|||
canResize.value = !canResize.value; |
|||
} |
|||
function toggleStriped() { |
|||
striped.value = !striped.value; |
|||
} |
|||
function toggleLoading() { |
|||
loading.value = true; |
|||
setTimeout(() => { |
|||
loading.value = false; |
|||
}, 3000); |
|||
} |
|||
function toggleBorder() { |
|||
border.value = !border.value; |
|||
} |
|||
return { |
|||
columns: getBasicColumns(), |
|||
data: getBasicData(), |
|||
canResize, |
|||
loading, |
|||
striped, |
|||
border, |
|||
toggleStriped, |
|||
toggleCanResize, |
|||
toggleLoading, |
|||
toggleBorder, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,70 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable"> |
|||
<template #id="{ record }"> ID: {{ record.id }} </template> |
|||
<template #no="{ record }" |
|||
><Tag color="green">{{ record.no }}</Tag> |
|||
</template> |
|||
<template #img> |
|||
<TableImg |
|||
:imgList="['https://picsum.photos/id/66/346/216', 'https://picsum.photos/id/67/346/216']" |
|||
/> |
|||
</template> |
|||
</BasicTable> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable, BasicColumn, TableImg } from '/@/components/Table'; |
|||
import { Tag } from 'ant-design-vue'; |
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
const columns: BasicColumn[] = [ |
|||
{ |
|||
title: 'ID', |
|||
dataIndex: 'id', |
|||
slots: { customRender: 'id' }, |
|||
}, |
|||
{ |
|||
title: '姓名', |
|||
dataIndex: 'name', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '头像', |
|||
dataIndex: 'img', |
|||
width: 120, |
|||
slots: { customRender: 'img' }, |
|||
}, |
|||
{ |
|||
title: '地址', |
|||
dataIndex: 'address', |
|||
}, |
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'no', |
|||
slots: { customRender: 'no' }, |
|||
}, |
|||
{ |
|||
title: '开始时间', |
|||
dataIndex: 'beginTime', |
|||
}, |
|||
{ |
|||
title: '结束时间', |
|||
dataIndex: 'endTime', |
|||
}, |
|||
]; |
|||
export default defineComponent({ |
|||
components: { BasicTable, TableImg, Tag }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: '自定义列内容', |
|||
api: demoListApi, |
|||
columns: columns, |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,60 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable"> |
|||
<template #customId> |
|||
<EditTableHeaderIcon title="Id" /> |
|||
</template> |
|||
<template #customName> |
|||
<EditTableHeaderIcon title="姓名" /> |
|||
</template> |
|||
</BasicTable> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { |
|||
BasicTable, |
|||
useTable, |
|||
BasicColumn, |
|||
renderEditableCell, |
|||
EditTableHeaderIcon, |
|||
} from '/@/components/Table'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
const columns: BasicColumn[] = [ |
|||
{ |
|||
// title: 'ID', |
|||
dataIndex: 'id', |
|||
slots: { title: 'customId' }, |
|||
customRender: renderEditableCell({ dataIndex: 'id' }), |
|||
}, |
|||
{ |
|||
// title: '姓名', |
|||
dataIndex: 'name', |
|||
slots: { title: 'customName' }, |
|||
customRender: renderEditableCell({ |
|||
dataIndex: 'name', |
|||
}), |
|||
}, |
|||
{ |
|||
title: '地址', |
|||
dataIndex: 'address', |
|||
sorter: true, |
|||
}, |
|||
]; |
|||
export default defineComponent({ |
|||
components: { BasicTable, EditTableHeaderIcon }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: '可编辑单元格示例', |
|||
api: demoListApi, |
|||
columns: columns, |
|||
showIndexColumn: false, |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,34 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable"> |
|||
<template #expandedRowRender="{ record }"> |
|||
<span>No: {{ record.no }} </span> |
|||
</template> |
|||
</BasicTable> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getBasicColumns } from './tableData'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
|
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: '可展开表格', |
|||
api: demoListApi, |
|||
titleHelpMessage: '不能与scroll共用', |
|||
columns: getBasicColumns(), |
|||
rowKey: 'id', |
|||
canResize: false, |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,44 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable"> |
|||
<template #toolbar> |
|||
<a-button type="primary" @click="handleReloadCurrent"> 刷新当前页 </a-button> |
|||
<a-button type="primary" @click="handleReload"> 刷新并返回第一页 </a-button> |
|||
</template> |
|||
</BasicTable> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getBasicColumns } from './tableData'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const [registerTable, { reload }] = useTable({ |
|||
title: '远程加载示例', |
|||
api: demoListApi, |
|||
columns: getBasicColumns(), |
|||
}); |
|||
function handleReloadCurrent() { |
|||
reload(); |
|||
// reload({ |
|||
// searchInfo: 'xxx', |
|||
// }); |
|||
} |
|||
|
|||
function handleReload() { |
|||
reload({ |
|||
page: 1, |
|||
}); |
|||
} |
|||
return { |
|||
registerTable, |
|||
handleReloadCurrent, |
|||
handleReload, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,93 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable"> |
|||
<template #action> |
|||
<TableAction |
|||
:actions="[ |
|||
{ |
|||
label: '删除', |
|||
props: { |
|||
onClick: handleDelete, |
|||
}, |
|||
}, |
|||
]" |
|||
:dropDownActions="[ |
|||
{ |
|||
label: '启用', |
|||
props: { |
|||
onClick: handleOpen, |
|||
}, |
|||
}, |
|||
]" |
|||
/> |
|||
</template> |
|||
</BasicTable> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable, BasicColumn, TableAction } from '/@/components/Table'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
const columns: BasicColumn[] = [ |
|||
{ |
|||
title: 'ID', |
|||
dataIndex: 'id', |
|||
fixed: 'left', |
|||
width: 280, |
|||
}, |
|||
{ |
|||
title: '姓名', |
|||
dataIndex: 'name', |
|||
width: 260, |
|||
}, |
|||
{ |
|||
title: '地址', |
|||
dataIndex: 'address', |
|||
width: 260, |
|||
}, |
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'no', |
|||
width: 300, |
|||
}, |
|||
{ |
|||
title: '开始时间', |
|||
width: 200, |
|||
dataIndex: 'beginTime', |
|||
}, |
|||
{ |
|||
title: '结束时间', |
|||
dataIndex: 'endTime', |
|||
width: 200, |
|||
}, |
|||
]; |
|||
export default defineComponent({ |
|||
components: { BasicTable, TableAction }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: 'TableAction组件及固定列示例', |
|||
api: demoListApi, |
|||
columns: columns, |
|||
rowSelection: { type: 'radio' }, |
|||
actionColumn: { |
|||
width: 160, |
|||
title: 'Action', |
|||
dataIndex: 'action', |
|||
slots: { customRender: 'action' }, |
|||
}, |
|||
}); |
|||
function handleDelete() { |
|||
console.log('点击了删除'); |
|||
} |
|||
function handleOpen() { |
|||
console.log('点击了启用'); |
|||
} |
|||
return { |
|||
registerTable, |
|||
handleDelete, |
|||
handleOpen, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,40 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable"> |
|||
<template #customTitle> |
|||
<span> |
|||
姓名 |
|||
<BaseHelp class="ml-2" text="姓名" /> |
|||
</span> |
|||
</template> |
|||
<template #customAddress> |
|||
地址 |
|||
<FormOutlined class="ml-2" /> |
|||
</template> |
|||
</BasicTable> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getCustomHeaderColumns } from './tableData'; |
|||
import { FormOutlined } from '@ant-design/icons-vue'; |
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
|
|||
export default defineComponent({ |
|||
components: { BasicTable, FormOutlined }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: '定高/头部自定义', |
|||
api: demoListApi, |
|||
columns: getCustomHeaderColumns(), |
|||
canResize: false, |
|||
scroll: { y: 100 }, |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,50 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable" /> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getBasicColumns } from './tableData'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
|
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
function handleSummary(tableData: any[]) { |
|||
const totalNo = tableData.reduce((prev, next) => { |
|||
prev += next.no; |
|||
return prev; |
|||
}, 0); |
|||
return [ |
|||
{ |
|||
_row: '合计', |
|||
_index: '平均值', |
|||
no: totalNo, |
|||
}, |
|||
{ |
|||
_row: '合计', |
|||
_index: '平均值', |
|||
no: totalNo, |
|||
}, |
|||
]; |
|||
} |
|||
const [registerTable] = useTable({ |
|||
title: '表尾行合计示例', |
|||
api: demoListApi, |
|||
rowSelection: { type: 'checkbox' }, |
|||
columns: getBasicColumns(), |
|||
showSummary: true, |
|||
summaryFunc: handleSummary, |
|||
scroll: { x: 2000 }, |
|||
canResize: false, |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,27 @@ |
|||
<template> |
|||
<BasicTable @register="registerTable" /> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getBasicColumns, getFormConfig } from './tableData'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
|
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: '开启搜索区域', |
|||
api: demoListApi, |
|||
columns: getBasicColumns(), |
|||
useSearchForm: true, |
|||
formConfig: getFormConfig(), |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,27 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable" /> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getMergeHeaderColumns } from './tableData'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
|
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: '多级表头示例', |
|||
api: demoListApi, |
|||
columns: getMergeHeaderColumns(), |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,26 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable @register="registerTable" /> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getMultipleHeaderColumns } from './tableData'; |
|||
|
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const [registerTable] = useTable({ |
|||
title: '多级表头示例', |
|||
api: demoListApi, |
|||
columns: getMultipleHeaderColumns(), |
|||
}); |
|||
|
|||
return { |
|||
registerTable, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,119 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<div class="mb-4"> |
|||
<a-button class="mr-2" @click="reloadTable">还原</a-button> |
|||
<a-button class="mr-2" @click="changeLoading">开启loading</a-button> |
|||
<a-button class="mr-2" @click="changeColumns">更改Columns</a-button> |
|||
<a-button class="mr-2" @click="getColumn">获取Columns</a-button> |
|||
<a-button class="mr-2" @click="getTableData">获取表格数据</a-button> |
|||
<a-button class="mr-2" @click="setPaginationInfo">跳转到第2页</a-button> |
|||
</div> |
|||
<div class="mb-4"> |
|||
<a-button class="mr-2" @click="getSelectRowList">获取选中行</a-button> |
|||
<a-button class="mr-2" @click="getSelectRowKeyList">获取选中行Key</a-button> |
|||
<a-button class="mr-2" @click="setSelectedRowKeyList">设置选中行</a-button> |
|||
<a-button class="mr-2" @click="clearSelect">清空选中行</a-button> |
|||
<a-button class="mr-2" @click="getPagination">获取分页信息</a-button> |
|||
</div> |
|||
<BasicTable |
|||
:canResize="false" |
|||
title="RefTable示例" |
|||
titleHelpMessage="使用Ref调用表格内方法" |
|||
ref="tableRef" |
|||
:api="api" |
|||
:columns="columns" |
|||
rowKey="id" |
|||
:rowSelection="{ type: 'checkbox' }" |
|||
/> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent, ref, unref } from 'vue'; |
|||
import { BasicTable, TableActionType } from '/@/components/Table'; |
|||
import { getBasicColumns, getBasicShortColumns } from './tableData'; |
|||
import { useMessage } from '/@/hooks/web/useMessage'; |
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const tableRef = ref<Nullable<TableActionType>>(null); |
|||
const { createMessage } = useMessage(); |
|||
|
|||
function getTableAction() { |
|||
const tableAction = unref(tableRef); |
|||
if (!tableAction) { |
|||
throw new Error('tableAction is null'); |
|||
} |
|||
return tableAction; |
|||
} |
|||
function changeLoading() { |
|||
getTableAction().setLoading(true); |
|||
setTimeout(() => { |
|||
getTableAction().setLoading(false); |
|||
}, 1000); |
|||
} |
|||
function changeColumns() { |
|||
getTableAction().setColumns(getBasicShortColumns()); |
|||
} |
|||
function reloadTable() { |
|||
getTableAction().setColumns(getBasicColumns()); |
|||
|
|||
getTableAction().reload({ |
|||
page: 1, |
|||
}); |
|||
} |
|||
function getColumn() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getTableAction().getColumns()); |
|||
} |
|||
|
|||
function getTableData() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getTableAction().getDataSource()); |
|||
} |
|||
|
|||
function getPagination() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getTableAction().getPaginationRef()); |
|||
} |
|||
|
|||
function setPaginationInfo() { |
|||
getTableAction().setPagination({ |
|||
current: 2, |
|||
}); |
|||
getTableAction().reload(); |
|||
} |
|||
function getSelectRowList() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getTableAction().getSelectRows()); |
|||
} |
|||
function getSelectRowKeyList() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getTableAction().getSelectRowKeys()); |
|||
} |
|||
function setSelectedRowKeyList() { |
|||
getTableAction().setSelectedRowKeys(['0', '1', '2']); |
|||
} |
|||
function clearSelect() { |
|||
getTableAction().clearSelectedRowKeys(); |
|||
} |
|||
|
|||
return { |
|||
tableRef, |
|||
api: demoListApi, |
|||
columns: getBasicColumns(), |
|||
changeLoading, |
|||
changeColumns, |
|||
reloadTable, |
|||
getColumn, |
|||
getTableData, |
|||
getPagination, |
|||
setPaginationInfo, |
|||
getSelectRowList, |
|||
getSelectRowKeyList, |
|||
setSelectedRowKeyList, |
|||
clearSelect, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,29 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<BasicTable |
|||
:rowSelection="{ type: 'checkbox' }" |
|||
:isTreeTable="true" |
|||
title="树形表格" |
|||
titleHelpMessage="树形组件不能和序列号列同时存在" |
|||
:columns="columns" |
|||
:dataSource="data" |
|||
rowKey="id" |
|||
:indentSize="20" |
|||
/> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable } from '/@/components/Table'; |
|||
import { getBasicColumns, getTreeTableData } from './tableData'; |
|||
|
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
return { |
|||
columns: getBasicColumns(), |
|||
data: getTreeTableData(), |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,126 @@ |
|||
<template> |
|||
<div class="p-4"> |
|||
<div class="mb-4"> |
|||
<a-button class="mr-2" @click="reloadTable">还原</a-button> |
|||
<a-button class="mr-2" @click="changeLoading">开启loading</a-button> |
|||
<a-button class="mr-2" @click="changeColumns">更改Columns</a-button> |
|||
<a-button class="mr-2" @click="getColumn">获取Columns</a-button> |
|||
<a-button class="mr-2" @click="getTableData">获取表格数据</a-button> |
|||
<a-button class="mr-2" @click="setPaginationInfo">跳转到第2页</a-button> |
|||
</div> |
|||
<div class="mb-4"> |
|||
<a-button class="mr-2" @click="getSelectRowList">获取选中行</a-button> |
|||
<a-button class="mr-2" @click="getSelectRowKeyList">获取选中行Key</a-button> |
|||
<a-button class="mr-2" @click="setSelectedRowKeyList">设置选中行</a-button> |
|||
<a-button class="mr-2" @click="clearSelect">清空选中行</a-button> |
|||
<a-button class="mr-2" @click="getPagination">获取分页信息</a-button> |
|||
</div> |
|||
<BasicTable @register="registerTable" /> |
|||
</div> |
|||
</template> |
|||
<script lang="ts"> |
|||
import { defineComponent } from 'vue'; |
|||
import { BasicTable, useTable } from '/@/components/Table'; |
|||
import { getBasicColumns, getBasicShortColumns } from './tableData'; |
|||
import { useMessage } from '/@/hooks/web/useMessage'; |
|||
import { demoListApi } from '/@/api/demo/table'; |
|||
export default defineComponent({ |
|||
components: { BasicTable }, |
|||
setup() { |
|||
const { createMessage } = useMessage(); |
|||
const [ |
|||
registerTable, |
|||
{ |
|||
setLoading, |
|||
setColumns, |
|||
getColumns, |
|||
getDataSource, |
|||
reload, |
|||
getPaginationRef, |
|||
setPagination, |
|||
getSelectRows, |
|||
getSelectRowKeys, |
|||
setSelectedRowKeys, |
|||
clearSelectedRowKeys, |
|||
}, |
|||
] = useTable({ |
|||
canResize: false, |
|||
title: 'useTable示例', |
|||
titleHelpMessage: '使用useTable调用表格内方法', |
|||
api: demoListApi, |
|||
columns: getBasicColumns(), |
|||
rowKey: 'id', |
|||
rowSelection: { |
|||
type: 'checkbox', |
|||
}, |
|||
}); |
|||
|
|||
function changeLoading() { |
|||
setLoading(true); |
|||
setTimeout(() => { |
|||
setLoading(false); |
|||
}, 1000); |
|||
} |
|||
function changeColumns() { |
|||
setColumns(getBasicShortColumns()); |
|||
} |
|||
function reloadTable() { |
|||
setColumns(getBasicColumns()); |
|||
|
|||
reload({ |
|||
page: 1, |
|||
}); |
|||
} |
|||
function getColumn() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getColumns()); |
|||
} |
|||
|
|||
function getTableData() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getDataSource()); |
|||
} |
|||
|
|||
function getPagination() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getPaginationRef()); |
|||
} |
|||
|
|||
function setPaginationInfo() { |
|||
setPagination({ |
|||
current: 2, |
|||
}); |
|||
reload(); |
|||
} |
|||
function getSelectRowList() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getSelectRows()); |
|||
} |
|||
function getSelectRowKeyList() { |
|||
createMessage.info('请在控制台查看!'); |
|||
console.log(getSelectRowKeys()); |
|||
} |
|||
function setSelectedRowKeyList() { |
|||
setSelectedRowKeys(['0', '1', '2']); |
|||
} |
|||
function clearSelect() { |
|||
clearSelectedRowKeys(); |
|||
} |
|||
|
|||
return { |
|||
registerTable, |
|||
changeLoading, |
|||
changeColumns, |
|||
reloadTable, |
|||
getColumn, |
|||
getTableData, |
|||
getPagination, |
|||
setPaginationInfo, |
|||
getSelectRowList, |
|||
getSelectRowKeyList, |
|||
setSelectedRowKeyList, |
|||
clearSelect, |
|||
}; |
|||
}, |
|||
}); |
|||
</script> |
|||
@ -0,0 +1,292 @@ |
|||
import { FormProps, FormSchema } from '/@/components/Table'; |
|||
import { BasicColumn } from '/@/components/Table/src/types/table'; |
|||
|
|||
export function getBasicColumns(): BasicColumn[] { |
|||
return [ |
|||
{ |
|||
title: 'ID', |
|||
width: 150, |
|||
dataIndex: 'id', |
|||
}, |
|||
{ |
|||
title: '姓名', |
|||
dataIndex: 'name', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '地址', |
|||
dataIndex: 'address', |
|||
}, |
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'no', |
|||
width: 80, |
|||
}, |
|||
{ |
|||
title: '开始时间', |
|||
dataIndex: 'beginTime', |
|||
}, |
|||
{ |
|||
title: '结束时间', |
|||
sorter: true, |
|||
dataIndex: 'endTime', |
|||
}, |
|||
]; |
|||
} |
|||
|
|||
export function getBasicShortColumns(): BasicColumn[] { |
|||
return [ |
|||
{ |
|||
title: 'ID', |
|||
width: 150, |
|||
dataIndex: 'id', |
|||
}, |
|||
{ |
|||
title: '姓名', |
|||
dataIndex: 'name', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '地址', |
|||
dataIndex: 'address', |
|||
}, |
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'no', |
|||
width: 80, |
|||
}, |
|||
]; |
|||
} |
|||
|
|||
export function getMultipleHeaderColumns(): BasicColumn[] { |
|||
return [ |
|||
{ |
|||
title: 'ID', |
|||
dataIndex: 'id', |
|||
width: 200, |
|||
}, |
|||
{ |
|||
title: '姓名', |
|||
dataIndex: 'name', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '地址', |
|||
dataIndex: 'address', |
|||
sorter: true, |
|||
children: [ |
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'no', |
|||
width: 120, |
|||
filters: [ |
|||
{ text: 'Male', value: 'male' }, |
|||
{ text: 'Female', value: 'female' }, |
|||
], |
|||
}, |
|||
|
|||
{ |
|||
title: '开始时间', |
|||
dataIndex: 'beginTime', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '结束时间', |
|||
dataIndex: 'endTime', |
|||
width: 120, |
|||
}, |
|||
], |
|||
}, |
|||
]; |
|||
} |
|||
|
|||
export function getCustomHeaderColumns(): BasicColumn[] { |
|||
return [ |
|||
{ |
|||
title: 'ID', |
|||
dataIndex: 'id', |
|||
width: 200, |
|||
}, |
|||
{ |
|||
// title: '姓名',
|
|||
dataIndex: 'name', |
|||
width: 120, |
|||
slots: { title: 'customTitle' }, |
|||
}, |
|||
{ |
|||
// title: '地址',
|
|||
dataIndex: 'address', |
|||
slots: { title: 'customAddress' }, |
|||
sorter: true, |
|||
}, |
|||
|
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'no', |
|||
width: 120, |
|||
filters: [ |
|||
{ text: 'Male', value: 'male' }, |
|||
{ text: 'Female', value: 'female' }, |
|||
], |
|||
}, |
|||
{ |
|||
title: '开始时间', |
|||
dataIndex: 'beginTime', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '结束时间', |
|||
dataIndex: 'endTime', |
|||
width: 120, |
|||
}, |
|||
]; |
|||
} |
|||
const renderContent = ({ text, index }: { text: any; index: number }) => { |
|||
const obj: any = { |
|||
children: text, |
|||
attrs: {}, |
|||
}; |
|||
if (index === 9) { |
|||
obj.attrs.colSpan = 0; |
|||
} |
|||
return obj; |
|||
}; |
|||
export function getMergeHeaderColumns(): BasicColumn[] { |
|||
return [ |
|||
{ |
|||
title: 'ID', |
|||
dataIndex: 'id', |
|||
width: 300, |
|||
customRender: renderContent, |
|||
}, |
|||
{ |
|||
title: '姓名', |
|||
dataIndex: 'name', |
|||
width: 300, |
|||
customRender: renderContent, |
|||
}, |
|||
{ |
|||
title: '地址', |
|||
dataIndex: 'address', |
|||
colSpan: 2, |
|||
width: 120, |
|||
sorter: true, |
|||
customRender: ({ text, index }: { text: any; index: number }) => { |
|||
const obj: any = { |
|||
children: text, |
|||
attrs: {}, |
|||
}; |
|||
if (index === 2) { |
|||
obj.attrs.rowSpan = 2; |
|||
} |
|||
if (index === 3) { |
|||
obj.attrs.colSpan = 0; |
|||
} |
|||
return obj; |
|||
}, |
|||
}, |
|||
{ |
|||
title: '编号', |
|||
dataIndex: 'no', |
|||
colSpan: 0, |
|||
filters: [ |
|||
{ text: 'Male', value: 'male' }, |
|||
{ text: 'Female', value: 'female' }, |
|||
], |
|||
customRender: renderContent, |
|||
}, |
|||
{ |
|||
title: '开始时间', |
|||
dataIndex: 'beginTime', |
|||
width: 200, |
|||
customRender: renderContent, |
|||
}, |
|||
{ |
|||
title: '结束时间', |
|||
dataIndex: 'endTime', |
|||
width: 200, |
|||
customRender: renderContent, |
|||
}, |
|||
]; |
|||
} |
|||
export const getAdvanceSchema = (itemNumber = 6): FormSchema[] => { |
|||
const arr: any = []; |
|||
for (let index = 0; index < itemNumber; index++) { |
|||
arr.push({ |
|||
field: `field${index}`, |
|||
label: `字段${index}`, |
|||
component: 'Input', |
|||
colProps: { |
|||
xl: 12, |
|||
xxl: 8, |
|||
}, |
|||
}); |
|||
} |
|||
return arr; |
|||
}; |
|||
export function getFormConfig(): Partial<FormProps> { |
|||
return { |
|||
labelWidth: 100, |
|||
schemas: getAdvanceSchema(6), |
|||
}; |
|||
} |
|||
export function getBasicData() { |
|||
const data: any = (() => { |
|||
const arr: any = []; |
|||
for (let index = 0; index < 40; index++) { |
|||
arr.push({ |
|||
id: `${index}`, |
|||
name: 'John Brown', |
|||
age: `1${index}`, |
|||
no: `${index + 10}`, |
|||
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', |
|||
beginTime: new Date().toLocaleString(), |
|||
endTime: new Date().toLocaleString(), |
|||
}); |
|||
} |
|||
return arr; |
|||
})(); |
|||
return data; |
|||
} |
|||
|
|||
export function getTreeTableData() { |
|||
const data: any = (() => { |
|||
const arr: any = []; |
|||
for (let index = 0; index < 40; index++) { |
|||
arr.push({ |
|||
id: `${index}`, |
|||
name: 'John Brown', |
|||
age: `1${index}`, |
|||
no: `${index + 10}`, |
|||
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', |
|||
beginTime: new Date().toLocaleString(), |
|||
endTime: new Date().toLocaleString(), |
|||
children: [ |
|||
{ |
|||
id: `l2-${index}`, |
|||
name: 'John Brown', |
|||
age: `1${index}`, |
|||
no: `${index + 10}`, |
|||
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', |
|||
beginTime: new Date().toLocaleString(), |
|||
endTime: new Date().toLocaleString(), |
|||
children: [ |
|||
{ |
|||
id: `l3-${index}`, |
|||
name: 'John Brown', |
|||
age: `1${index}`, |
|||
no: `${index + 10}`, |
|||
address: 'New York No. 1 Lake ParkNew York No. 1 Lake Park', |
|||
beginTime: new Date().toLocaleString(), |
|||
endTime: new Date().toLocaleString(), |
|||
}, |
|||
], |
|||
}, |
|||
], |
|||
}); |
|||
} |
|||
return arr; |
|||
})(); |
|||
|
|||
return data; |
|||
} |
|||
Loading…
Reference in new issue