Skip to content

feat: 新增ProTable组件 #253

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Nov 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions packages/pro-table/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
.DS_Store
dist
dist-ssr
coverage
*.local

/cypress/videos/
/cypress/screenshots/

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
277 changes: 277 additions & 0 deletions packages/pro-table/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
<h1 align="center">
Ant Design Pro Table
</h1>

<div align="center">

[![NPM version](https://img.shields.io/npm/v/@ant-design-vue/pro-table/latest?style=flat)](https://npmjs.org/package/@ant-design-vue/pro-table) [![Vue Support](https://img.shields.io/badge/support-Vue3-green?style=flat)](./package.json) [![Vue Grammar Level](https://img.shields.io/badge/full-Composition%20API-blue?style=flat)](https://v3.vuejs.org/guide/composition-api-introduction.html) [![NPM downloads](http://img.shields.io/npm/dm/@ant-design-vue/pro-table.svg?style=flat)](https://npmjs.org/package/@ant-design-vue/pro-table) [![License](https://img.shields.io/github/license/vueComponent/pro-layout)](./LICENSE)

</div>

## Basic Usage

Recommend look [Examples](./examples/) or [Use Template](https://github.com/sendya/preview-pro)

## Branch

- next : Vue3 + ant-design-vue@3.x (latest)
- v3.1 : Vue3 + ant-design-vue@2.2.x (release LTS)
- v2 : Vue2 + ant-design-vue@1.7.x

## Install

```bash
# yarn
yarn add @ant-design-vue/pro-table
# npm
npm i @ant-design-vue/pro-table -S
```

### Simple Usage

First, you should add the `@ant-design-vue/pro-table` that you need into the library.

```js
// main.[js|ts]
import '@ant-design-vue/pro-table/dist/style.css'; // pro-layout css or style.less

import { createApp } from 'vue';
import App from './App.vue';
import Antd from 'ant-design-vue';
import ProTable from '@ant-design-vue/pro-table';

const app = createApp(App);

app.use(Antd).use(ProLayout).use(PageContainer).mount('#app');
```

After that, you can use pro-layout in your Vue components as simply as this:

```vue
<template>
<pro-table
:request="request"
:columns="columns"
:bordered="true"
:pagination="pagination"
></pro-table>
</template>

<script setup lang="ts">
import { reactive, ref } from 'vue';

import type { ColumnsType } from 'ant-design-vue/lib/vc-table/interface';
const pagination = reactive({
pageSize: 10
});
const columns = reactive<ColumnsType>([
{
dataIndex: 'name',
title: '姓名',
key: 'name'
},
{
dataIndex: 'age',
title: '年龄',
key: 'age'
}
]);
const request = async (params: any) => {
let data: any[] = [];

console.log('params', params);
for (let i = 0; i < params.pageSize; i++) {
data.push({ name: '第' + params.current + '页的' + (i + 1), age: 18 });
}
return {
data,
success: true,
total: 100
};
};
</script>
```

## API

### ProLayout

| Property | Description | Type | Default Value |
| ----------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------- | ------------------ |
| title | layout in the upper left corner title | VNode \| String | `'Ant Design Pro'` |
| logo | layout top left logo url | VNode \| render | - |
| loading | layout loading status | boolean | - |
| layout | layout menu mode, sidemenu: right navigation, topmenu: top navigation | 'side' \| 'top' \| 'mix' | `'side'` |
| contentWidth | content mode of layout, Fluid: adaptive, Fixed: fixed width 1200px | 'Fixed' \| 'Fluid' | `Fluid` |
| navTheme | Navigation theme | 'light' \|'dark' | `'light'` |
| headerTheme | Header Bar theme | 'light' \|'dark' | `'light'` |
| menuData | Vue-router `routes` prop | Object | `[{}]` |
| collapsed | control menu's collapse and expansion | boolean | true |
| selectedKeys | menu selected keys | string[] | `[]` |
| openKeys | menu openKeys | string[] | `[]` |
| isMobile | is mobile | boolean | false |
| onCollapse \| @collapse | folding collapse event of menu | (collapsed: boolean) => void | - |
| menuHeaderRender | render header logo and title | v-slot \| VNode \| (logo,title)=>VNode \| false | - |
| menuExtraRender | render extra menu item | v-slot \| VNode \| (props)=>VNode \| false | - |
| menuFooterRender | render footer menu item | v-slot \| VNode \| (props)=>VNode \| false | - |
| headerContentRender | custom header render method | `slot` \| (props: BasicLayoutProps) => VNode | - |
| rightContentRender | header right content render method | `slot` \| (props: HeaderViewProps) => VNode | - |
| collapsedButtonRender | custom collapsed button method | `slot` \| (collapsed: boolean) => VNode | - |
| footerRender | custom footer render method | `slot` \| (props: BasicLayoutProps) => VNode | `false` |
| breadcrumbRender | custom breadcrumb render method | `slot` \| ({ route, params, routes, paths, h }) => VNode[] | - |
| menuItemRender | custom render Menu.Item | v-slot#menuItemRender="{ item, icon }" \| ({ item, icon }) => VNode | null |
| subMenuItemRender | custom render Menu.SubItem | v-slot#subMenuItemRender="{ item, icon }" \| ({ item, icon }) => VNode | null |
| locale | i18n | Function (key: string) => string \| `false` | `false` |

> Menu generation requires `getMenuData` and `clearMenuItem` function
> e.g. `const { menuData } = getMenuData(clearMenuItem(routes))`

### PageContainer

| Property | Description | Type | Default Value |
| -------------- | ------------------------------------------------ | ---------------------------------- | ------------- |
| content | Content area | VNode \| v-slot | - |
| extra | Extra content area, on the right side of content | VNode \| v-slot | - |
| extraContent | Extra content area, on the right side of content | VNode \| v-slot | - |
| tabList | Tabs title list | `Array<{key: string, tab: sting}>` | - |
| tab-change | Switch panel callback | (key) => void | - |
| tab-active-key | The currently highlighted tab item | string | - |

### WaterMark

| Property | Description | Type | Default Value |
| ------------- | ------------------------------------- | ---------------- | ---------------------- |
| markStyle | mark style | CSSProperties | - |
| markClassName | mark class | string | - |
| gapX | Horizontal spacing between water-mark | number | 212 |
| gapY | Vertical spacing between watermark | number | 222 |
| offsetLeft | Horizontal offset | number | `offsetTop = gapX / 2` |
| offsetTop | Vertical offset | number | `offsetTop = gapY / 2` |
| | | | |
| width | | number | 120 |
| height | | number | 64 |
| rotate | Angle of rotation, unit ° | number | -22 |
| image | image src | string | - |
| zIndex | water-mark z-index | number | 9 |
| content | water-mark Content | string | - |
| fontColor | font-color | string | `rgba(0,0,0,.15)` |
| fontSize | font-size | string`\|`number | 16 |

### Custom Render

#### Custom rightContentRender

```vue
<template #rightContentRender>
<div style="margin-right: 12px">
<a-avatar shape="square" size="small">
<template #icon>
<UserOutlined />
</template>
</a-avatar>
</div>
</template>
```

#### Custom menu.item

```vue
<template #menuItemRender="{ item, icon }">
<a-menu-item
:key="item.path"
:disabled="item.meta?.disabled"
:danger="item.meta?.danger"
:icon="icon"
>
<router-link :to="{ path: item.path }">
<span class="ant-pro-menu-item">
<a-badge count="5" dot>
<span class="ant-pro-menu-item-title">{{ item.meta.title }}</span>
</a-badge>
</span>
</router-link>
</a-menu-item>
</template>
```

#### Custom menuExtraRender

```vue
<template #menuExtraRender="{ collapsed }">
<a-input-search v-if="!collapsed" />
</template>
```

#### Custom menuFooterRender

```vue
<template #menuFooterRender>
<div>menu footer</div>
</template>
```

#### Custom breadcrumbRender

```vue
<template #breadcrumbRender="{ route, params, routes }">
<span v-if="routes.indexOf(route) === routes.length - 1">
{{ route.breadcrumbName }}
</span>
<router-link v-else :to="{ path: route.path, params }">
{{ route.breadcrumbName }}
</router-link>
</template>
```

#### Custom collapsedButtonRender

```vue
<template #collapsedButtonRender="collapsed">
<HeartOutlined v-if="collapsed" />
<SmileOutlined v-else />
</template>
```

#### Custom footer with slot

```vue
<GlobalFooter>
<template #links>
<a>链接1</a>
<a>链接2</a>
<a>链接3</a>
<a>链接4</a>
</template>
<template #copyright>
<span>Pro Layout &copy; 2021 Sendya.</span>
</template>
</GlobalFooter>
```

#### Custom footer with props

```vue
<GlobalFooter
:links="[
{ title: 'Link 1', href: '#' },
{ title: 'Link 2', href: '#' }
]"
copyright="Pro Layout &copy; 2021 Sendya."
/>
```

### Use WaterMark

```vue
<router-view v-slot="{ Component }">
<WaterMark content="Pro Layout">
<component :is="Component" />
</WaterMark>
</router-view>
```

## Build project

```bash
pnpm build # Build library and .d.ts
```
1 change: 1 addition & 0 deletions packages/pro-table/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/// <reference types="vite/client" />
26 changes: 26 additions & 0 deletions packages/pro-table/examples/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script setup lang="ts">
import { RouterView } from 'vue-router';
import { ConfigProvider } from 'ant-design-vue';

const getPopupContainer = (triggerNode?: HTMLElement): HTMLElement => {
// if (dialogContext) {
// return dialogContext.getDialogWrap()
// }
if (triggerNode) {
return (triggerNode?.parentNode as HTMLElement) || document.body;
}
return document.body;
};
</script>

<template>
<ConfigProvider :get-popup-container="getPopupContainer">
<RouterView />
</ConfigProvider>
</template>

<style>
#app {
height: 100%;
}
</style>
18 changes: 18 additions & 0 deletions packages/pro-table/examples/icons.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as Icons from '@ant-design/icons-vue/es';
import type { App } from 'vue';
import type { IconType } from '@ant-design/icons-vue/es/components/Icon';

type AllIcon = {
[key: string]: IconType;
};

export const filterIcons = ['default', 'createFromIconfontCN', 'getTwoToneColor', 'setTwoToneColor'];

export default (app: App) => {
const allIcon: AllIcon = Icons as any;
Object.keys(Icons)
.filter((k) => !filterIcons.includes(k))
.forEach((k) => {
app.component(allIcon[k].displayName, allIcon[k]);
});
};
37 changes: 37 additions & 0 deletions packages/pro-table/examples/layouts/BasicLayout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<template>
<pro-table :request="request" :columns="columns" :bordered="true" :pagination="pagination"></pro-table>
</template>

<script setup lang="ts">
import { reactive, ref } from "vue";

import type { ColumnsType } from "ant-design-vue/lib/vc-table/interface";
const pagination = reactive({
pageSize: 10,
});
const columns = reactive<ColumnsType>([
{
dataIndex: "name",
title: "姓名",
key: "name",
},
{
dataIndex: "age",
title: "年龄",
key: "age",
},
]);
const request = async (params: any) => {
let data: any[] = [];

console.log("params", params);
for (let i = 0; i < params.pageSize; i++) {
data.push({ name: "第" + params.current + "页的" + (i + 1), age: 18 });
}
return {
data,
success: true,
total: 100,
};
};
</script>
Loading