Skip to content

feat: ProForm #241

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 9 commits into from
Aug 22, 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
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module.exports = {
},
rules: {
'prettier/prettier': ['error', { semi: true, singleQuote: true, printWidth: 120 }],
'@typescript-eslint/no-unused-vars': ['error', { "ignoreRestSiblings": true }],
'@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
// 临时关掉
'@typescript-eslint/no-explicit-any': 'off',
},
Expand Down
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
shamefully-hoist=true
strict-peer-dependencies=false
28 changes: 28 additions & 0 deletions packages/pro-form/.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?
155 changes: 155 additions & 0 deletions packages/pro-form/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
<h1 align="center">
Ant Design Pro Form
</h1>

<div align="center">

[![NPM version](https://img.shields.io/npm/v/@ant-design-vue/pro-layout/latest?style=flat)](https://npmjs.org/package/@ant-design-vue/pro-layout) [![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-layout.svg?style=flat)](https://npmjs.org/package/@ant-design-vue/pro-layout) [![License](https://img.shields.io/github/license/vueComponent/pro-layout)](./LICENSE)

</div>
## Basic Usage

Recommend look [Examples](./examples/)

## 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-form
# npm
npm i @ant-design-vue/pro-form -S
```

### Simple Usage

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

```vue
<template>
<QueryFilter
:model="formModel"
@finish="handleSubmit"
@collapsed="onCollapsed"
>
<FormItem name="name" label="应用名称" required>
<Input v-model:value="formModel.name" placeholder="请输入" allow-clear />
</FormItem>
<FormItem name="creater" label="创建人" required>
<Input v-model:value="formModel.creater" placeholder="请输入" />
</FormItem>
<FormItem name="sex" label="性别" required>
<Select v-model:value="formModel.sex">
<SelectOption
v-for="item in sex"
:key="item.value"
:value="item.value"
>{{ item.label }}</SelectOption
>
</Select>
</FormItem>
<FormItem name="status" label="应用状态">
<Input v-model:value="formModel.status" placeholder="请输入" />
</FormItem>
<FormItem name="startDate" label="响应日期">
<DatePicker v-model:value="formModel.startDate" placeholder="请输入" />
</FormItem>
<FormItem name="create" label="创建时间">
<RangePicker
v-model:value="formModel.create"
:placeholder="['开始时间', '结束时间']"
/>
</FormItem>
</QueryFilter>
</template>

<script lang="ts" setup>
import { ref, reactive } from "vue";
import { QueryFilter } from "@ant-design-vue/pro-form";
import dayjs, { type Dayjs } from "dayjs";
import {
FormItem,
Input,
Select,
SelectOption,
RangePicker,
DatePicker,
} from "ant-design-vue";

const formModel = reactive({
name: "123",
creater: "11",
sex: "男",
status: "",
startDate: "",
create: [
dayjs("2015/01/01", "YYYY/MM/DD"),
dayjs("2016/01/01", "YYYY/MM/DD"),
] as [Dayjs, Dayjs],
});
const sex = ref([
{
value: "男",
label: "男",
},
{
value: "女",
label: "女",
},
]);

function handleSubmit(params: any) {
console.log(params);
}
function onCollapsed(collapsed: boolean) {
console.log(collapsed);
}
</script>
```

## API

### ProLayout

| Property | Description | Type | Default Value |
| ----------------- | ------------------------------------------------------------------------------------------------------------ | ---------------------------- | ------------- |
| formProps | antd 基础表单 props | object | |
| searchGutter | 表单 gutter | number | 24 |
| style | 自定义样式 | object | undefined |
| defaultColsNumber | 自定义折叠状态下默认显示的表单控件数量,没有设置或小于 0,则显示一行控件; 数量大于等于控件数量则隐藏展开按钮 | Number | undefined |
| collapsed | 是否折叠超出的表单项,用于受控模式 | Boolean | undefined |
| defaultCollapsed | 默认状态下是否折叠超出的表单项 | Boolean | true |
| preserve | 是否能够查询收起的数据,如果设置为 false,收起后的表单数据将会丢失 | Boolean | true |
| split | 每一行是否有分割线 | Boolean | undefined |
| submitButtonProps | 提交按钮的 props | Object | undefined |
| submitter | 重置、查询、展开收起按钮 props | SubmitterProps | undefined |
| onCollapsed | 切换表单折叠状态时的回调 | (collapsed: boolean) => void | undefined |
| onFinish | 表单提交 | (fromModel: any) => void | undefined |
| onSubmit | 表单提交 | (fromModel: any) => void | undefined |
| onReset | 重置表单 | (fromModel: any) => void | undefined |

## Build project

```bash
pnpm build # Build library and .d.ts
```

## TODO:

- [x] BaseFrom
- [x] QueryFilter
- [ ] ProForm
- [ ] ProField
- [ ] LoginFrom
- [ ] ModalFrom
- [ ] DrawerFrom
- [ ] ProTable
- [ ] 一个像样的文档(VitePress)
1 change: 1 addition & 0 deletions packages/pro-form/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-form/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-form/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]);
});
};
120 changes: 120 additions & 0 deletions packages/pro-form/examples/layouts/BasicLayout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<template>
<pro-layout
v-model:collapsed="baseState.collapsed"
v-model:selectedKeys="baseState.selectedKeys"
v-model:openKeys="baseState.openKeys"
v-bind="state"
:loading="loading"
:breadcrumb="{ routes: breadcrumb }"
style="min-height: 100vh"
iconfont-url="//at.alicdn.com/t/font_2804900_nzigh7z84gc.js"
>
<template #menuHeaderRender>
<a>
<img src="/favicon.svg" />
<h1>Pro Layout</h1>
</a>
</template>

<!-- custom right-content -->
<template #rightContentRender>
<div style="margin-right: 12px">
<a-avatar shape="square" size="small">
<template #icon>
<UserOutlined />
</template>
</a-avatar>
</div>
</template>
<!-- custom breadcrumb itemRender -->
<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>
<template #menuFooterRender>
<a
:style="{
lineHeight: '48rpx',
display: 'flex',
height: '48px',
alignItems: 'center',
}"
href="https://preview.pro.antdv.com/dashboard/analysis"
target="_blank"
rel="noreferrer"
>
<img
alt="pro-logo"
src="https://procomponents.ant.design/favicon.ico"
:style="{
width: '16px',
height: '16px',
margin: '0 16px',
marginRight: '10px',
}"
/>
<span v-if="!baseState.collapsed">Preview Pro</span>
</a>
</template>

<!-- content begin -->
<router-view v-slot="{ Component }">
<!-- <WaterMark :content="watermarkContent"> -->
<component :is="Component" />
<!-- </WaterMark> -->
</router-view>
</pro-layout>
</template>

<script setup lang="ts">
import { computed, reactive, ref, watchEffect, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { getMenuData, clearMenuItem, type RouteContextProps } from '@ant-design-vue/pro-layout';

const watermarkContent = ref('Pro Layout');
const loading = ref(false);
const router = useRouter();
const { menuData } = getMenuData(clearMenuItem(router.getRoutes()));

const baseState = reactive<Omit<RouteContextProps, 'menuData'>>({
selectedKeys: [],
openKeys: [],
// default
collapsed: false,
});

const state = reactive({
menuData,
splitMenus: true,
// title: 'ProLayout',
// logo: 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg',
navTheme: 'realDark',
layout: 'mix',
fixSiderbar: true,
fixedHeader: true,
});
const breadcrumb = computed(() =>
router.currentRoute.value.matched.concat().map((item) => {
return {
path: item.path,
breadcrumbName: item.meta.title || '',
};
})
);

watchEffect(() => {
if (router.currentRoute) {
const matched = router.currentRoute.value.matched.concat();
baseState.selectedKeys = matched.filter((r) => r.name !== 'index').map((r) => r.path);
baseState.openKeys = matched.filter((r) => r.path !== router.currentRoute.value.path).map((r) => r.path);
}
});

onMounted(() => {
setTimeout(() => {
watermarkContent.value = 'New Mark';
}, 2000);
});
</script>
3 changes: 3 additions & 0 deletions packages/pro-form/examples/layouts/BlankLayout.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<template>
<router-view />
</template>
Loading