feat(form-page): add form page demo

This commit is contained in:
vben
2020-11-17 17:02:42 +08:00
parent 4ddee05dee
commit 0b6110a8fc
43 changed files with 1389 additions and 116 deletions

View File

@@ -48,6 +48,10 @@
bottom: 0,
}),
},
placement: {
type: String as PropType<string>,
defualt: 'right',
},
},
setup(props, { slots }) {
const getOverlayStyleRef = computed(() => {
@@ -97,7 +101,7 @@
overlayClassName: 'base-help__wrap',
autoAdjustOverflow: true,
overlayStyle: unref(getOverlayStyleRef),
placement: 'right',
placement: props.placement,
getPopupContainer: () => getPopupContainer(),
},
{

View File

@@ -0,0 +1 @@
export { default as AppFooter } from './src/index.vue';

View File

@@ -0,0 +1,57 @@
<template>
<div class="app-footer" :style="{ width: getWidth }">
<div class="app-footer__left">
<slot name="left" />
</div>
<div class="app-footer__right">
<slot name="right" />
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, unref } from 'vue';
import { SIDE_BAR_MINI_WIDTH, SIDE_BAR_SHOW_TIT_MINI_WIDTH } from '/@/enums/appEnum';
import { appStore } from '/@/store/modules/app';
import { menuStore } from '/@/store/modules/menu';
export default defineComponent({
name: 'AppFooter',
setup() {
const getMiniWidth = computed(() => {
const {
menuSetting: { collapsedShowTitle },
} = appStore.getProjectConfig;
return collapsedShowTitle ? SIDE_BAR_SHOW_TIT_MINI_WIDTH : SIDE_BAR_MINI_WIDTH;
});
const getWidth = computed(() => {
const { getCollapsedState, getMenuWidthState } = menuStore;
const width = getCollapsedState ? unref(getMiniWidth) : getMenuWidthState;
return `calc(100% - ${width}px)`;
});
return { getWidth };
},
});
</script>
<style lang="less" scoped>
.app-footer {
position: fixed;
right: 0;
bottom: 0;
z-index: 99;
display: flex;
width: 100%;
align-items: center;
padding: 0 24px;
line-height: 44px;
background: #fff;
border-top: 1px solid #f0f0f0;
box-shadow: 0 -6px 16px -8px rgba(0, 0, 0, 0.08), 0 -9px 28px 0 rgba(0, 0, 0, 0.05),
0 -12px 48px 16px rgba(0, 0, 0, 0.03);
transition: width 0.3s;
&__left {
flex: 1 1;
}
}
</style>

View File

@@ -1,5 +1,5 @@
<template>
<Form v-bind="$attrs" ref="formElRef" :model="formModel">
<Form v-bind="{ ...$attrs, ...$props }" ref="formElRef" :model="formModel">
<Row :class="getProps.compact ? 'compact-form-row' : ''">
<slot name="formHeader" />
<template v-for="schema in getSchema" :key="schema.field">
@@ -54,8 +54,8 @@
const formModel = reactive({});
const actionState = reactive({
resetAction: () => {},
submitAction: () => {},
resetAction: {},
submitAction: {},
});
const advanceState = reactive<AdvanceState>({
@@ -67,7 +67,7 @@
const defaultValueRef = ref<any>({});
const propsRef = ref<Partial<FormProps>>({});
const schemaRef = ref<FormSchema[] | null>(null);
const schemaRef = ref<Nullable<FormSchema[]>>(null);
const formElRef = ref<Nullable<FormActionType>>(null);
const getMergePropsRef = computed(
@@ -98,7 +98,15 @@
for (const schema of schemas) {
const { defaultValue, component } = schema;
if (defaultValue && dateItemType.includes(component!)) {
schema.defaultValue = moment(defaultValue);
if (!Array.isArray(defaultValue)) {
schema.defaultValue = moment(defaultValue);
} else {
const def: moment.Moment[] = [];
defaultValue.forEach((item) => {
def.push(moment(item));
});
schema.defaultValue = def;
}
}
}
return schemas as FormSchema[];
@@ -139,8 +147,8 @@
formModel,
getSchema,
defaultValueRef,
formElRef: formElRef as any,
schemaRef: schemaRef as any,
formElRef: formElRef as Ref<FormActionType>,
schemaRef: schemaRef as Ref<FormSchema[]>,
handleFormValues,
actionState,
});
@@ -156,6 +164,13 @@
}
);
watch(
() => getSchema.value,
() => {
initDefault();
}
);
/**
* @description:设置表单
*/

View File

@@ -250,14 +250,21 @@ export default defineComponent({
}
function renderLabelHelpMessage() {
const { label, helpMessage, helpComponentProps } = props.schema;
const { label, helpMessage, helpComponentProps, subLabel } = props.schema;
const renderLabel = subLabel ? (
<span>
{label} <span style="color:#00000073">{subLabel}</span>
</span>
) : (
label
);
if (!helpMessage || (Array.isArray(helpMessage) && helpMessage.length === 0)) {
return label;
return renderLabel;
}
return (
<span>
{label}
<BasicHelp class="mx-1" text={helpMessage} {...helpComponentProps} />
{renderLabel}
<BasicHelp placement="top" class="mx-1" text={helpMessage} {...helpComponentProps} />
</span>
);
}
@@ -291,6 +298,7 @@ export default defineComponent({
const { colProps = {}, colSlot, renderColContent, component } = props.schema;
if (!componentMap.has(component)) return null;
const { baseColProps = {} } = props.formProps;
const realColProps = { ...baseColProps, ...colProps };
const { isIfShow, isShow } = getShow();
const getContent = () => {

View File

@@ -7,7 +7,7 @@ export function createPlaceholderMessage(component: ComponentType) {
if (component.includes('Input') || component.includes('Complete')) {
return '请输入';
}
if (component.includes('Picker') && !component.includes('Range')) {
if (component.includes('Picker')) {
return '请选择';
}
if (

View File

@@ -25,19 +25,26 @@ export function useItemLabelWidth(schemaItemRef: Ref<FormSchema>, propsRef: Ref<
const { labelCol = {}, wrapperCol = {} } = schemaItem.itemProps || {};
const { labelWidth, disabledLabelWidth } = schemaItem;
const { labelWidth: globalLabelWidth } = unref(propsRef) as any;
const {
labelWidth: globalLabelWidth,
labelCol: globalLabelCol,
wrapperCol: globWrapperCol,
} = unref(propsRef) as any;
// 如果全局有设置labelWidth, 则所有item使用
if ((!globalLabelWidth && !labelWidth) || disabledLabelWidth) {
if ((!globalLabelWidth && !labelWidth && !globalLabelCol) || disabledLabelWidth) {
return { labelCol, wrapperCol };
}
let width = labelWidth || globalLabelWidth;
const col = { ...globalLabelCol, ...labelCol };
const wrapCol = { ...globWrapperCol, ...wrapperCol };
if (width) {
width = isNumber(width) ? `${width}px` : width;
}
return {
labelCol: { style: { width }, ...labelCol },
wrapperCol: { style: { width: `calc(100% - ${width})` }, ...wrapperCol },
labelCol: { style: { width }, ...col },
wrapperCol: { style: { width: `calc(100% - ${width})` }, ...wrapCol },
};
});
}

View File

@@ -41,6 +41,7 @@ export type RegisterFn = (formInstance: FormActionType) => void;
export type UseFormReturnType = [RegisterFn, FormActionType];
export interface FormProps {
// layout?: 'vertical' | 'inline' | 'horizontal';
// 表单值
model?: any;
// 整个表单所有项宽度
@@ -108,6 +109,8 @@ export interface FormSchema {
valueField?: string;
// 标签名
label: string;
// 辅助文本
subLabel?: string;
// 文本右侧帮助文本
helpMessage?: string | string[];
// BaseHelp组件props

View File

@@ -136,9 +136,9 @@ export default defineComponent({
const flag = await beforeClickFn(menu);
if (!flag) return;
}
emit('menuClick', menu);
const { path } = menu;
menuState.selectedKeys = [path];
emit('menuClick', menu);
}
function handleMenuChange() {
@@ -219,7 +219,6 @@ export default defineComponent({
: {};
return (
<Menu
forceSubMenuRender={props.isAppMenu}
selectedKeys={selectedKeys}
defaultSelectedKeys={defaultSelectedKeys}
mode={mode}

View File

@@ -1,7 +1,7 @@
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 { renderEditableCell, renderEditableRow } from './src/components/renderEditable';
export { default as EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue';
export * from './src/types/table';
@@ -11,3 +11,5 @@ export * from './src/types/tableAction';
export { useTable } from './src/hooks/useTable';
export type { FormSchema, FormProps } from '/@/components/Form/src/types/form';
export type { EditRecordRow } from './src/components/renderEditable';

View File

@@ -32,7 +32,7 @@ export default defineComponent({
disabled={disabled}
color={color}
{...action}
key={index}
key={`${index}-${label}`}
>
{() => (
<>
@@ -60,7 +60,7 @@ export default defineComponent({
} = popConfirm;
return (
<Popconfirm
key={`P-${index}`}
key={`p-${index}-${title}`}
title={title}
onConfirm={confirm}
onCancel={cancel}

View File

@@ -1,15 +1,15 @@
import { defineComponent, PropType, ref, unref, nextTick } from 'vue';
import { injectTable } from '../hooks/useProvinceTable';
import { defineComponent, PropType, ref, unref, nextTick, watchEffect } from 'vue';
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';
import '../style/editable-cell.less';
const prefixCls = 'editable-cell';
const EditableCell = defineComponent({
name: 'EditableCell',
@@ -37,14 +37,35 @@ const EditableCell = defineComponent({
type: String as PropType<ComponentType>,
default: 'Input',
},
editable: {
type: Boolean as PropType<boolean>,
default: false,
},
editRow: {
type: Boolean as PropType<boolean>,
default: false,
},
record: {
type: Object as PropType<EditRecordRow>,
},
placeholder: {
type: String as PropType<string>,
},
},
emits: ['submit', 'cancel'],
setup(props, { attrs, emit }) {
const table = injectTable();
const elRef = ref<any>(null);
const isEditRef = ref(false);
const currentValueRef = ref<string | boolean>(props.value);
const defaultValueRef = ref<string | boolean>(props.value);
watchEffect(() => {
defaultValueRef.value = props.value;
if (isBoolean(props.editable)) {
isEditRef.value = props.editable;
}
});
function handleChange(e: any) {
if (e && e.target && Reflect.has(e.target, 'value')) {
@@ -65,37 +86,55 @@ const EditableCell = defineComponent({
function handleCancel() {
isEditRef.value = false;
currentValueRef.value = defaultValueRef.value;
emit('cancel');
}
if (props.record) {
/* eslint-disable */
props.record.onCancel = handleCancel;
/* eslint-disable */
props.record.onSubmit = handleSubmit;
}
function handleSubmit() {
const { dataKey, dataIndex } = props;
if (!dataKey || !dataIndex) {
return;
if (!dataKey || !dataIndex) return;
if (props.record) {
/* eslint-disable */
props.record[dataIndex] = unref(currentValueRef) as string;
}
isEditRef.value = false;
const { getDataSource } = table;
const dataSource = getDataSource();
const target = dataSource.find((item) => item.key === dataKey);
if (target) {
target[dataIndex] = unref(currentValueRef);
emit('submit', { dataKey, dataIndex, value: unref(currentValueRef) });
}
}
function onClickOutside() {
if (props.editRow) return;
const { component } = props;
if (component && component.includes('Input')) {
handleCancel();
}
}
function renderValue() {
const { value } = props;
if (props.editRow) {
return !unref(isEditRef) ? value : null;
}
return (
!unref(isEditRef) && (
<div class={`${prefixCls}__normal`} onClick={handleEdit}>
{value}
<FormOutlined class={`${prefixCls}__normal-icon`} />
</div>
)
);
}
return () => {
const { value, component, componentProps = {} } = props;
const { component, componentProps = {} } = props;
const Comp = componentMap.get(component!) as any;
// const propsData: any = {};
return (
<div class={prefixCls}>
{unref(isEditRef) && (
@@ -103,6 +142,7 @@ const EditableCell = defineComponent({
{() => (
<div class={`${prefixCls}__wrapper`}>
<Comp
placeholder={props.placeholder}
{...{
...attrs,
...componentProps,
@@ -114,21 +154,20 @@ const EditableCell = defineComponent({
onChange={handleChange}
onPressEnter={handleSubmit}
/>
<div class={`${prefixCls}__action`}>
<CheckOutlined class={[`${prefixCls}__icon`, 'mx-2']} onClick={handleSubmit} />
<CloseOutlined class={[`${prefixCls}__icon `]} onClick={handleCancel} />
</div>
{!props.editRow && (
<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>
)}
{renderValue()}
</div>
);
};
@@ -138,15 +177,16 @@ const EditableCell = defineComponent({
export function renderEditableCell({
dataIndex,
component,
componentOn = {},
componentProps = {},
placeholder,
}: RenderEditableCellParams) {
return ({ text, record }: { text: string; record: any }) => {
return ({ text, record }: { text: string; record: EditRecordRow }) => {
return (
<EditableCell
{...componentOn}
{...componentProps}
placeholder={placeholder}
value={text}
record={record}
dataKey={record.key}
dataIndex={dataIndex}
component={component}
@@ -154,3 +194,32 @@ export function renderEditableCell({
);
};
}
export function renderEditableRow({
dataIndex,
component,
componentProps = {},
placeholder,
}: RenderEditableCellParams) {
return ({ text, record }: { text: string; record: EditRecordRow }) => {
return (
<EditableCell
{...componentProps}
value={text}
placeholder={placeholder}
editRow={true}
editable={record.editable}
dataKey={record.key}
record={record}
dataIndex={dataIndex}
component={component}
/>
);
};
}
export type EditRecordRow<T = { [key: string]: any }> = {
editable: boolean;
onCancel: Fn;
onSubmit: Fn;
} & T;

View File

@@ -68,8 +68,8 @@ export interface SorterResult {
export interface RenderEditableCellParams {
dataIndex: string;
component?: ComponentType;
componentOn?: { [key: string]: Fn };
componentProps?: any;
placeholder?: string;
}
export interface FetchParams {
@@ -88,15 +88,15 @@ export type SizeType = 'default' | 'middle' | 'small' | 'large';
export interface TableActionType {
reload: (opt?: FetchParams) => Promise<void>;
getSelectRows: () => any[];
getSelectRows: <T = any>() => T[];
clearSelectedRowKeys: () => void;
getSelectRowKeys: () => string[];
deleteSelectRowByKey: (key: string) => void;
setPagination: (info: Partial<PaginationProps>) => void;
setTableData: (values: any[]) => void;
setTableData: <T = any>(values: T[]) => void;
getColumns: (opt?: GetColumnsParams) => BasicColumn[];
setColumns: (columns: BasicColumn[] | string[]) => void;
getDataSource: () => any[];
getDataSource: <T = any>() => T[];
setLoading: (loading: boolean) => void;
setProps: (props: Partial<BasicTableProps>) => void;
redoHeight: () => void;

View File

@@ -1,5 +1,4 @@
export interface ActionItem {
on?: any;
onClick?: any;
label: string;
disabled?: boolean;

View File

@@ -1,5 +1,6 @@
import Icon from './Icon/index';
import Button from './Button/index.vue';
import { AppFooter } from './Footer';
import {
// Need
Button as AntButton,
@@ -28,10 +29,12 @@ import {
Upload,
Transfer,
Steps,
PageHeader,
Result,
} from 'ant-design-vue';
import { getApp } from '/@/useApp';
const compList = [Icon, Button, AntButton.Group];
const compList = [Icon, Button, AntButton.Group, AppFooter];
// Fix hmr multiple registered components
let registered = false;
@@ -70,5 +73,7 @@ export function registerGlobComp() {
.use(Upload)
.use(Transfer)
.use(Steps)
.use(PageHeader)
.use(Result)
.use(Tabs);
}