You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

371 lines
10 KiB

<!-- @format -->
<template>
<a-form
class="ns-form"
:class="getFormClass.class"
v-bind="getBindValue"
ref="formElRef"
:model="formModel">
<div v-if="showAction && showExpandAll" class="ns-form-title ns-title-extra-box">
<span> {{ title ? title : '查询' }}</span>
<a-button type="link" class="ns-operate-expand" @click="expandAll = !expandAll">
<template v-if="expandAll">
收起筛选
<UpOutlined />
</template>
<template v-else>
展开筛选
<DownOutlined />
</template>
</a-button>
</div>
<a-row
v-show="expandAll"
class="ns-form-body"
:justify="getFormClass.justify"
:gutter="getFormClass.gutter">
<template v-for="(schema, index) in getSchema" :key="schema.field">
<ns-form-item
:show="expandRef || index < splitNumber"
:span="getComponentSpan(schema)"
:schema="schema"
:index="index"
:formModel="formModel">
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</ns-form-item>
</template>
<!-- space-between兼容 -->
<a-col
v-if="
getFormClass.justify === 'space-between' && showAction
? expandRef && getSchema.length % splitNumber === 0
: (getSchema.length + 1) % splitNumber === 0
"
:span="getFormClass.span" />
<a-col v-if="showAction" :span="getFormClass.span" class="ns-operate">
<a-button @click="reset">重置</a-button>
<a-button type="primary" html-type="submit" :loading="loading">搜索</a-button>
<a-button
type="link"
class="ns-operate-expand"
@click="expandRef = !expandRef"
v-if="getSchema.length > splitNumber && showExpand">
<template v-if="expandRef">
收起
<UpOutlined />
</template>
<template v-else>
展开
<DownOutlined />
</template>
</a-button>
</a-col>
</a-row>
<!-- <template v-for="(schema, index) in getSchema" :key="schema.field">-->
<!-- <ns-form-item :schema="schema" :index="index" :formModel="formModel">-->
<!-- <template #[item]="data" v-for="item in Object.keys($slots)">-->
<!-- <slot :name="item" v-bind="data || {}"></slot>-->
<!-- </template>-->
<!-- </ns-form-item>-->
<!-- </template>-->
<!-- <a-row-->
<!-- v-if="showAction"-->
<!-- style="text-align: right; margin-left: auto"-->
<!-- class="ns-form-item ns-form-item-op">-->
<!-- <a-col span="24" class="operate">-->
<!-- <a-button @click="reset">重置</a-button>-->
<!-- <a-button style="margin-left: 10px" type="primary" html-type="submit" :loading="loading"-->
<!-- >搜索</a-button-->
<!-- >-->
<!-- </a-col>-->
<!-- </a-row>-->
</a-form>
</template>
<!--suppress TypeScriptCheckImport -->
<script lang="ts">
import { computed, defineComponent, nextTick, provide, ref, toRefs, watch } from 'vue';
import { DownOutlined, UpOutlined } from '@ant-design/icons-vue';
import NsFormItem from '/nerv-lib/component/form/form/form-item.vue';
import { useFormModel } from '/nerv-lib/component/form/form/use-form-model';
import { formConfig } from '/nerv-base/config/form.config';
import { formProps } from '/nerv-lib/component/form/form/props';
import { isBoolean, isFunction } from 'lodash-es';
import FormValidator from 'async-validator';
import { watchDebounced, useElementSize } from '@vueuse/core';
export default defineComponent({
name: 'NsForm',
components: { NsFormItem, DownOutlined, UpOutlined },
props: formProps,
emits: ['finish', 'submit', 'reset'],
setup(props, { attrs, emit }) {
const formElRef = ref<null | Recordable>(null);
const validateResult = ref(false);
const { schemas } = toRefs(props);
const isInitDefaultValueRef = ref(false);
const expandRef = ref(props.expand);
const expandAll = ref(props.expandAll);
const title = ref(props.title);
const formModel = computed(() => {
return props.model;
});
const childForms = ref<any[]>([]);
function addChildForm(form: any) {
childForms.value.push(form);
}
let splitNumber = ref(4);
const { width: formWidth } = useElementSize(formElRef);
provide('addChildForm', addChildForm);
const getFormClass = computed(() => {
if (props.formLayout) {
return formConfig.formLayout[props.formLayout as keyof typeof formConfig.formLayout];
}
return formConfig.formLayout.vertical;
});
const FULL_SPAN_LIST = ['NsChildForm'];
const getComponentSpan = (schema: FormSchema) => {
if (FULL_SPAN_LIST.includes(schema.component || '')) {
return 24;
}
if (schema.class?.includes('ns-form-item-full')) {
return 24;
}
return getFormClass.value.span;
};
provide('formLayout', getFormClass.value);
const getBindValue = computed(() => ({
...getFormClass.value,
...attrs,
...props,
title: '',
onSubmit: submit,
onFinish: finish,
}));
const getSchema = computed((): FormSchema[] => {
const { schemas } = props;
const formSchemas = schemas.filter((schema) => schema.component);
return formSchemas as FormSchema[];
});
watchDebounced(
() => formModel.value,
() => {
const rules = {};
setRules(rules, getSchema.value);
const validator = new FormValidator(rules);
validator
.validate(formModel.value)
.then(() => {
validateResult.value = true;
})
.catch(() => {
validateResult.value = false;
});
},
{
debounce: 100,
deep: true,
},
);
watchDebounced(
() => formWidth.value,
(val) => {
if (val <= 768 && getFormClass.value['sm']) {
getFormClass.value.span = getFormClass.value['sm'];
splitNumber.value = 2;
}
if (val > 768 && getFormClass.value['lg']) {
getFormClass.value.span = getFormClass.value['lg'];
splitNumber.value = 3;
}
},
{
debounce: 100,
deep: true,
},
);
function setRules(rules: Recordable, schemas: any[]) {
schemas.forEach((schema) => {
if (schema.rules) {
const { ifShow } = schema;
let isIfShow = true;
if (isBoolean(ifShow)) {
isIfShow = ifShow;
}
if (isFunction(ifShow)) {
isIfShow = ifShow(formModel.value);
}
// console.log('ifShow', isIfShow);
if (isIfShow) {
rules[schema.field] = schema.rules;
}
}
if (schema.componentProps?.schemas) {
setRules(rules, schema.componentProps.schemas);
}
});
}
const {
handleFormModel,
initFormModel,
resetFormModel,
setFormModel,
unsetFormModel,
getFormModel,
} = useFormModel({
schemas,
formModel,
formElRef,
emit,
});
watch(
() => getSchema.value,
() => {
if (isInitDefaultValueRef.value) {
return;
}
initFormModel();
isInitDefaultValueRef.value = true;
},
{
immediate: true,
deep: true,
},
);
/**
* 数据验证成功后回调
*/
const submit = () => {
emit('submit', formModel.value);
return new Promise((resolve) => {
resolve(formModel.value);
});
};
/**
* 提交表单且数据验证成功后回调事件
*/
function finish() {
const data = handleFormModel();
emit('finish', data);
return new Promise((resolve) => {
resolve(data);
});
}
/**
* 主动执行提交
* @param val
*/
function triggerSubmit(val?: string[]) {
return submit().then(() => {
return nextTick().then(() => {
return new Promise((resolve, reject) => {
(formElRef as Recordable).value
.validate(val)
.then((_) => {
resolve(finish());
})
.catch((e) => {
reject(e);
});
});
});
});
}
/**
* 重置表单
*/
const reset = () => {
resetFormModel();
};
provide('setFormModel', setFormModel);
provide('unsetFormModel', unsetFormModel);
provide('getFormModel', getFormModel);
provide('submit', triggerSubmit);
return {
expandRef,
formElRef,
getBindValue,
getSchema,
formModel,
setFormModel,
getFormClass,
validateResult,
reset,
triggerSubmit,
getComponentSpan,
splitNumber,
finish,
expandAll,
title,
};
},
});
</script>
<style lang="less" scoped>
@gap: 16px;
.ns-form {
.ant-row {
flex: 1;
}
.ns-operate {
margin-bottom: @gap;
text-align: right;
margin-left: auto;
.ns-operate-expand {
display: inline-block;
padding: 4px 2px;
border: unset !important;
.anticon {
margin-left: 4px;
}
&:hover {
border: unset !important;
}
&:focus {
border: unset !important;
}
}
.ant-btn {
margin-left: 6px;
}
}
.ns-form-title {
text-align: left;
height: 22px;
// line-height: 32px;
//font-size: 16px;
font-weight: bold;
user-select: text;
margin-bottom: calc(@gap - 0px);
display: flex;
justify-content: space-between;
align-items: center;
:deep(.ant-btn) {
padding: 0;
}
}
}
</style>