mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 14:31:41 +08:00
feat(form-page): add form page demo
This commit is contained in:
@@ -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(),
|
||||
},
|
||||
{
|
||||
|
1
src/components/Footer/index.ts
Normal file
1
src/components/Footer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default as AppFooter } from './src/index.vue';
|
57
src/components/Footer/src/index.vue
Normal file
57
src/components/Footer/src/index.vue
Normal 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>
|
@@ -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:设置表单
|
||||
*/
|
||||
|
@@ -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 = () => {
|
||||
|
@@ -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 (
|
||||
|
@@ -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 },
|
||||
};
|
||||
});
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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}
|
||||
|
@@ -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';
|
||||
|
@@ -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}
|
||||
|
@@ -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;
|
@@ -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;
|
||||
|
@@ -1,5 +1,4 @@
|
||||
export interface ActionItem {
|
||||
on?: any;
|
||||
onClick?: any;
|
||||
label: string;
|
||||
disabled?: boolean;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user