<template>
<div ref="formRef" class="base-form-wraper" v-if="data?.config_unique_id">
<div class="btn-wraper" v-if="data?.btns?.length > 0">
<template v-for="btn in data.btns" :key="btn.id">
<div
:class="['btn_' + btn.data.key, 'btn-group']"
v-if="isShowItem({ ...btn.data, uniqueId: data.config_unique_id })"
>
<slot
v-if="btn.data.isSlot"
v-bind="btn.data"
:name="'btn_' + btn.data.key"
></slot>
<bc-button
v-else
v-bind="btn.data"
@click="headerBtnClick(btn.data, data)"
/>
</div>
</template>
</div>
<hs-form
ref="hsFormRef"
class="hs-form-wraper"
:model="data.data"
v-bind="data.attrs"
@submit.native.prevent
>
<div
ref="formWrapRef"
class="form-wrap"
v-for="(form, keys) in data.form"
:key="keys"
:style="{ '--bg-color': form?.attrs?.bg }"
>
<!-- form 表单 -->
<div
:class="[`wrap_${form.fieldID}`]"
v-if="form.formItemType === 'form' && !_.isEmpty(form?.config)"
>
<div :class="['section_title', `title_${form.fieldID}`]">
<div v-if="form.isShowLabel" class="title">
{{ form?.label || '' }}
</div>
<slot :name="`title_${form.fieldID}`"></slot>
</div>
<div
class="grid"
:style="{
'grid-template-columns': `repeat(${form.grid}, minmax(0, 1fr))`,
'column-gap': form.attrs.columnGap + 'px',
'row-gap': form.attrs.rowGap + 'px',
}"
>
<template v-for="(formItem, key) in getFormConfig(form)" :key="key">
<hs-form-item
v-if="showFormItem(formItem)"
:label="formItem.label"
:rules="getItemRules(formItem)"
:prop="getItemProp(formItem, form.fieldID)"
:class="[getItemProp(formItem, form.fieldID, '__')]"
:style="{
'--form-item-lable-bg': formItem.layoutData.labelBg,
'--form-item-lable-text': formItem.layoutData.labelColor,
'grid-row': `span ${formItem.layoutData.rowSpan}/span ${formItem.layoutData.rowSpan}`,
'grid-column': `span ${formItem.layoutData.colSpan}/span ${formItem.layoutData.colSpan}`,
}"
>
<template #label>
<div
v-if="
!$slots[`${getItemProp(formItem, form.fieldID, '_')}`]
"
class="form-item-label__text"
>
<span>{{ formItem.label }}</span>
<el-tooltip
v-if="getItemToolTip(formItem)"
effect="dark"
placement="top"
>
<template #content>
<div style="max-width: 450px">
{{ getItemToolTip(formItem) }}
</div>
</template>
<bc-icon class="ml-1" name="ele-QuestionFilled" />
</el-tooltip>
<span>{{ data?.attrs?.labelSuffix || '' }}</span>
</div>
<div v-else class="form-item-label__text">
<slot
:name="getItemProp(formItem, form.fieldID, '_')"
v-bind="getSlotData(formItem?.config[0], form.fieldID)"
>
</slot>
</div>
</template>
<div
class="bc-form-item"
v-for="(i, k) in formItem?.config"
:key="k"
>
<template v-if="showFormItem2(i.baseData, k)">
<slot
v-if="i.baseData.isSlot"
:name="i.baseData.prop"
v-bind="getSlotData(i, form.fieldID)"
></slot>
<bc-button
v-else-if="i.baseData.el === 'hs-button'"
v-bind="getBindComponentData(i)"
/>
<el-tooltip
v-else-if="form?.attrs?.readonly"
:content="getModel(form.fieldID)[i.baseData.prop]"
placement="top"
effect="light"
>
<span
:class="['cell_' + i.baseData.prop, 'readonly-cell']"
>{{ getModel(form.fieldID)[i.baseData.prop] }}</span
>
</el-tooltip>
<component
v-else
:is="getRender(i.baseData)"
:ref="
(e) => setItemInstance(e, form.fieldID, i.baseData.prop)
"
v-model="getModel(form.fieldID)[i.baseData.prop]"
v-bind="getComponentData(i)"
:field="i.baseData.prop"
:unique-id="data!.config_unique_id"
:mid="data!.mid!"
:uid="data!.uid"
/>
</template>
</div>
</hs-form-item>
</template>
</div>
</div>
<!-- 自定义插槽 -->
<div class="slot-wraper" v-if="form.formItemType === 'slot'">
<div class="section_title">
<div v-if="form.isShowLabel" class="title">
{{ form?.label || '' }}
</div>
<slot :name="`title_${form.fieldID}`"></slot>
</div>
<slot
:name="(form.config as IFormData.FormItemTypeMap['slot']).name"
v-bind="getBindData(form)"
></slot>
</div>
<!-- 自定义组件 -->
<div class="component-wraper" v-if="form.formItemType === 'component'">
<div class="section_title">
<div v-if="form.isShowLabel" class="title">
{{ form?.label || '' }}
</div>
<slot :name="`title_${form.fieldID}`"></slot>
</div>
<component
:is="(form.config as IFormData.FormItemTypeMap['component']).name"
v-bind="getBindData(form)"
/>
</div>
</div>
</hs-form>
</div>
</template>
<script lang="ts" setup name="baseForm">
import { inject } from 'vue';
import { ICustomData, IFormData } from '/@/types';
import { getRender, isShowItem } from '/@parseTable';
import type { BcFormApi } from './type';
import { useGuide } from './useGuide';
import _ from 'lodash';
interface Props {
data: IFormData.Form | undefined;
}
const bcFormApi = inject<BcFormApi>('bcFormApi');
const props = defineProps<Props>();
const emit = defineEmits(['field-change']);
const {
formRef,
hsFormRef,
formWrapRef,
getInstance,
setItemInstance,
getItemInstance,
setQueryFocus,
} = useGuide(props);
function showFormItem(item: IFormData.ConfigFormItem) {
return isShowItem({
...item.config[0].baseData,
uniqueId: props.data?.config_unique_id,
});
}
function showFormItem2(
item: ICustomData.FormItemConfigData['baseData'],
index: number
) {
if (index == 0) {
return isShowItem({ ...item, uniqueId: props.data?.config_unique_id });
}
return true;
}
function getFormConfig<T extends IFormData.FormItemType = 'form'>(
form: IFormData.FormConfig<T>
) {
return form.config as IFormData.FormItemTypeMap['form'];
}
function getBindData<T extends IFormData.FormItemType = 'slot' | 'component'>(
form: IFormData.FormConfig<T>
) {
return {
...form,
data: props.data,
};
}
function getComponentData(data: ICustomData.FormItemConfigData) {
return getBindComponentData(data);
}
function getBindComponentData(data: ICustomData.FormItemConfigData) {
return {
...data.baseData,
...data.elData.attrs,
..._.omit(data.elData, ['attrs', 'rules']),
};
}
function headerBtnClick(btn: ICustomData.BtnData, scopeData: IFormData.Form) {
if (btn.fnName && typeof btn.fnName === 'function') {
btn.fnName(scopeData);
}
}
/** 获取表单子集规则 */
function getItemRules(item: IFormData.ConfigFormItem) {
const rules1 = _.get(item, 'config.0.elData.rules.rules');
if (Array.isArray(rules1)) {
return rules1;
}
const rules2 = _.get(item, 'config.0.elData.rules');
if (Array.isArray(rules2)) {
return rules2;
}
return [];
}
/** 获取表单子项字段 */
function getItemProp(item: IFormData.ConfigFormItem, fieldID: string, s = '.') {
const prop = _.get(item, 'config.0.baseData.prop', '');
return `${fieldID}${s}${prop}`;
}
/** 获取表单子项提示 */
function getItemToolTip(item: IFormData.ConfigFormItem) {
return _.get(item, 'config.0.baseData.tooltip', '');
}
function getModel(fieldID: string) {
return new Proxy(props.data?.data[fieldID], {
get: function (target, propKey, receiver) {
return Reflect.get(target, propKey, receiver);
},
set: function (target, key, value, receiver) {
const oldValue = Reflect.get(target, key, receiver);
emit('field-change', { target, key, value, oldValue, fieldID });
return Reflect.set(target, key, value, receiver);
},
});
}
/** 自定义插槽修改值 */
function getSlotData(item: ICustomData.FormItemConfigData, fieldID: string) {
function update(val: ICustomData.ANY) {
bcFormApi?.setData(fieldID, val, item.baseData.prop);
}
return {
row: new Proxy(props.data?.data![fieldID], {
get: function (target, propKey, receiver) {
return Reflect.get(target, propKey, receiver);
},
set: function (target, key, value, receiver) {
const oldValue = Reflect.get(target, key, receiver);
emit('field-change', { target, key, value, oldValue, fieldID });
return Reflect.set(target, key, value, receiver);
},
}),
data: props.data,
fieldID,
config: item,
update,
};
}
defineExpose({
getInstance,
getItemInstance,
setQueryFocus,
});
</script>
<style scoped lang="scss">
.grid {
display: grid;
}
.title {
height: 16px;
display: flex;
align-items: center;
position: relative;
margin-bottom: 8px;
font-weight: 700;
&::after {
content: '';
position: absolute;
left: -10px;
top: 0;
width: 5px;
bottom: 0;
background-color: #1890ff;
}
}
:deep(.el-date-editor),
:deep(.el-input-number) {
width: 100% !important;
}
.base-form-wraper {
display: flex;
flex-direction: column;
height: 100%;
}
.btn-wraper {
display: flex;
align-items: center;
flex-wrap: wrap;
margin: 10px 0;
padding-left: 8px;
padding-right: 8px;
}
.hs-form-wraper {
flex: 1;
overflow: auto;
}
.form-wrap {
background-color: var(--bg-color);
padding: 20px;
border-radius: 6px;
margin-bottom: 8px;
&:last-of-type {
margin-bottom: 0;
}
.h-4 {
height: 16px;
}
.bc-form-item {
display: flex;
align-items: center;
width: 100%;
height: 100%;
}
}
.base-form-wraper :deep(.el-form-item) {
margin-bottom: 0 !important;
}
.base-form-wraper :deep(.el-form-item__label) {
background-color: var(--form-item-lable-bg);
color: var(--form-item-lable-text);
align-items: center;
}
.base-form-wraper :deep(.el-form-item__content) {
flex-wrap: nowrap !important;
line-height: 1 !important;
}
.base-form-wraper {
:deep(.el-form-item--default.el-form-item--label-left),
:deep(.el-form-item--small.el-form-item--label-left),
:deep(.el-form-item--large.el-form-item--label-left),
:deep(.el-form-item--default.el-form-item--label-right),
:deep(.el-form-item--small.el-form-item--label-right),
:deep(.el-form-item--large.el-form-item--label-right) {
.el-form-item__label {
height: 100% !important;
position: relative;
}
}
&:deep(.el-form-item__error) {
z-index: 10 !important;
}
}
:deep(.el-form--label-top .el-form-item__label) {
position: relative;
&::before {
position: absolute !important;
left: -10px;
}
}
.form-item-label__text {
height: 100%;
display: flex;
align-items: center;
color: var(--form-item-lable-text);
}
.section_title {
display: flex;
align-items: center;
}
.btn-group {
margin-right: 10px;
}
</style>
最新发布