front/src/components/FormPro/index.vue

448 lines
11 KiB
Vue

<script lang="ts" setup>
import { PropType } from 'vue';
import { IconQuestionCircleFill } from '@arco-design/web-vue/es/icon';
import SelectPro from '../SelectPro/index.vue';
import QuantityInput from '../QuantityInput/index.vue';
import TagsAdd from '../TagsAdd/index.vue';
import Timezone from '../Timezone/index.vue';
defineOptions({
name: 'FormPro',
components: {
SelectPro,
QuantityInput,
TagsAdd,
Timezone,
},
});
type itemProps = {
comp: string;
field: string;
label: string;
required?: boolean;
span?: number;
labelAlign?: 'left' | 'right';
hideColon?: boolean;
hideLabel?: boolean;
ifShow?: any;
tooltip?: string;
rules?: any[];
compProps: {
[key: string]: any;
};
};
const formRef = ref();
const props = defineProps({
itemList: {
type: Array<itemProps>,
default: () => [],
},
labelColProps: {
type: Object,
default: () => ({ span: 5 }),
},
wrapperColProps: {
type: Object,
default: () => ({ span: 19 }),
},
buttonConfig: {
type: Object as PropType<{
showBtn: boolean;
okText: string;
resetText: string;
showReset: boolean;
showOk: boolean;
span?: number;
className?: string;
}>,
default: () => ({
showBtn: false,
okText: '查询',
resetText: '重置',
showReset: true,
showOk: true,
span: 6,
className: '',
}),
},
rowGutter: {
type: Number,
default: 20,
},
defaultSpan: {
type: Number,
default: 6,
},
formLayout: {
type: String as PropType<'horizontal' | 'vertical' | 'inline'>,
default: 'horizontal',
},
labelColProps: {
type: Object,
default: () => ({ span: 6 }),
},
wrapperColProps: {
type: Object,
default: () => ({ span: 18 }),
},
formClassName: {
type: String,
default: '',
},
rowClassName: {
type: String,
default: '',
},
density: {
type: String as PropType<'default' | 'compact' | 'dense'>,
default: 'default',
},
hideColon: {
type: Boolean,
default: false,
},
labelAlign: {
type: String as PropType<'left' | 'right'>,
default: undefined,
},
autoLabelWidth: {
type: Boolean,
default: false,
},
size: {
type: String as PropType<'mini' | 'small' | 'medium' | 'large'>,
default: 'medium',
},
layout: {
type: String as PropType<'default' | 'inline'>,
default: 'default',
},
});
const emits = defineEmits(['submit', 'reset']);
const formValue = reactive<{
[key: string]: any;
}>({});
const submitFn = async () => {
emits('submit', formValue);
};
const resetFn = async () => {
formRef.value.resetFields();
emits('reset', formValue);
};
const getValues = () => {
return formValue;
};
const setValues = (values: any) => {
Object.assign(formValue, values);
};
const validate = async () => {
return formRef.value.validate();
};
// 添加设置表单值的方法
const setFormValue = (key, value) => {
console.log('调用 setFormValue:', { key, value });
// 如果第一个参数是对象,则批量设置值
if (typeof key === 'object' && key !== null) {
const values = key;
console.log('批量设置表单值:', values);
// 记录设置前的表单值
console.log('设置前的表单值:', { ...formValue });
// 批量设置值
Object.keys(values).forEach((k) => {
// 无论字段是否已存在,都设置值
const oldValue = formValue[k];
formValue[k] = values[k];
console.log(`设置字段 ${k}: ${oldValue} -> ${values[k]}`);
});
// 记录设置后的表单值
console.log('设置后的表单值:', { ...formValue });
return;
}
// 单个字段设置,无论字段是否已存在
console.log(`设置单个字段 ${key}: ${formValue[key]} -> ${value}`);
formValue[key] = value;
};
defineExpose({
getValues,
formRef,
validate,
setValues,
setFormValue,
});
// 计算表单类名
const formClasses = computed(() => {
const classes = [props.formClassName];
if (props.density === 'compact') {
classes.push('form-pro-compact');
} else if (props.density === 'dense') {
classes.push('form-pro-dense');
}
if (props.labelAlign === 'right') {
classes.push('form-pro-aligned-right');
} else if (props.labelAlign === 'left') {
classes.push('form-pro-aligned-left');
}
if (props.hideColon) {
classes.push('form-pro-no-colon');
}
if (props.autoLabelWidth) {
classes.push('form-pro-auto-label-width');
}
return classes;
});
const getItemList = computed(() => {
return props.itemList
.map((i) => {
return {
...i,
ifShow: getShow(i.ifShow),
compProps: getCompProps(i.compProps, i.field),
};
})
.filter((i) => i.ifShow);
});
const getShow = (ifShow) => {
if (ifShow === undefined) {
return true;
}
if (typeof ifShow === 'function') {
return ifShow(formValue);
}
return ifShow;
};
const getCompProps = (compProps, field) => {
console.log('compProps:', typeof compProps === 'function', field);
if (typeof compProps === 'function') {
// const d = compProps()
// console.log(d,'dddd')
return compProps({ formModel: formValue || {}, field });
}
return compProps;
};
</script>
<template>
<a-form
ref="formRef"
v-model="formValue"
:label-col-props="labelColProps"
:layout="formLayout"
:model="formValue"
:size="size"
:wrapper-col-props="wrapperColProps"
class="bg-#ffffff"
v-bind="$attrs"
>
<a-row
v-if="layout === 'default'"
:class="rowClassName"
:gutter="rowGutter"
>
<a-col
v-for="i in getItemList"
:key="i.field"
:span="i.span || defaultSpan"
>
<a-form-item
:field="i.field"
:hide-colon="i.hideColon"
:hide-label="i.hideLabel"
:label-align="i.labelAlign"
:rules="i.rules"
>
<template #label>
<div class="label-container">
<span :class="{ 'required-label': i.required }">{{
$t(i.label)
}}</span>
<a-tooltip v-if="i.tooltip" :content="i.tooltip">
<icon-question-circle-fill class="tooltip-icon" />
</a-tooltip>
</div>
</template>
<template v-if="i.comp === 'slot'">
<slot
:name="i.field"
v-bind="{
model: formValue,
schema: i,
}"
/>
</template>
<component
:is="i.comp"
v-else
v-model="formValue[i.field]"
v-bind="getCompProps(i.compProps)"
/>
</a-form-item>
</a-col>
<a-col
v-if="props.buttonConfig.showBtn"
:span="props.buttonConfig.span || defaultSpan"
>
<a-space>
<a-button
v-if="props.buttonConfig.showOk"
type="primary"
@click="submitFn"
>
{{ $t(props.buttonConfig.okText) }}
</a-button>
<a-button v-if="props.buttonConfig.showReset" @click="resetFn"
>{{ $t(props.buttonConfig.resetText) }}
</a-button>
</a-space>
</a-col>
</a-row>
<a-row
v-if="layout === 'inline'"
:gutter="rowGutter"
class="form-pro-inline-row"
>
<a-col
v-for="i in getItemList"
:key="i.field"
:span="i.span || defaultSpan"
>
<a-form-item
:field="i.field"
:hide-colon="i.hideColon"
:hide-label="i.hideLabel"
:label-align="i.labelAlign"
:label-col-style="{
width: 'auto',
flex: 'none',
}"
:rules="i.rules"
:wrapper-col-style="{
width: 'auto',
flex: 1,
}"
style="border: 1px solid #ccc"
>
<template #label>
<div class="label-container">
<span :class="{ 'required-label': i.required }">{{
$t(i.label)
}}</span>
<a-tooltip v-if="i.tooltip" :content="i.tooltip">
<icon-question-circle-fill class="tooltip-icon" />
</a-tooltip>
</div>
</template>
<template v-if="i.comp === 'slot'">
<slot
:name="i.field"
v-bind="{
model: formValue,
schema: i,
}"
/>
</template>
<component
:is="i.comp"
v-else
v-model="formValue[i.field]"
v-bind="getCompProps(i.compProps)"
/>
</a-form-item>
</a-col>
<a-col
v-if="props.buttonConfig.showBtn"
:class="props.buttonConfig.className"
:span="props.buttonConfig.span || defaultSpan"
>
<a-space class="mt-4px" :class="props.buttonConfig.className">
<a-button
v-if="props.buttonConfig.showOk"
type="primary"
@click="submitFn"
>
{{ $t(props.buttonConfig.okText) }}
</a-button>
<a-button v-if="props.buttonConfig.showReset" @click="resetFn"
>{{ $t(props.buttonConfig.resetText) }}
</a-button>
</a-space>
</a-col>
</a-row>
</a-form>
</template>
<style lang="less" scoped>
:deep(.arco-form-item-content) {
align-items: right !important;
}
:deep(.arco-form-item) {
margin-bottom: 12px;
}
:deep(.arco-form-item-wrapper-col) {
min-height: 28px;
}
:deep(.arco-form-item-label) {
display: flex;
font-size: 12px; /* 标签文字 */
}
.form-pro-inline-row {
:deep(.arco-form-item-label-col) {
padding-right: 0px;
padding-left: 16px;
}
:deep(.arco-input-wrapper) {
background: #ffffff !important;
}
:deep(.arco-picker) {
background: #ffffff !important;
width: 100%;
}
:deep(.arco-select) {
background: #ffffff !important;
}
:deep(.arco-form-item-content-wrapper) {
.arco-input.arco-input-size-medium,
.arco-form-item-content,
.arco-select-view-value,
.arco-select-view-input {
font-size: 12px !important;
}
}
}
// .btn {
// display: flex;
// justify-content: end;
// }
</style>