济菏高速业务端
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.
 
 
 
 
 

370 lines
8.4 KiB

<template>
<ElForm
:style="getStyle()"
:model="modelFormData"
:label-width="labelWidth"
class="FormConfig"
ref="ElFormRef"
size="mini"
>
<template v-for="(item, index) in formList">
<ElFormItem
class="formItem"
:rules="getRules(item)"
v-if="formItemVisible(item)"
:key="`${item.key}|${index}`"
:label="item.label"
:style="gridStyle(item, index)"
:prop="item.type !== 'MultipleLabelItem' ? item.key : void 0"
:required="item.required"
>
<slot :name="item.key" :data="item" :formData="modelFormData">
<ProxyCom
:value="getValue(item)"
:item="item"
@update:value="(data) => updateValue(item, data)"
/>
<!-- <component :is="getComponent(item.type)" v-bind="getBindData(item)" v-model="modelFormData[item.key]"
v-on="resolveListeners(item.ons)" /> -->
</slot>
</ElFormItem>
</template>
</ElForm>
</template>
<script>
import { resolveName, defaultComponentOptions, RegexpMap } from "./utils/index";
import { reduceDefaultValue } from "./utils/defaultValue";
import { set as pathSet, get as pathGet, cloneDeep } from "lodash";
import ProxyCom from "./Proxy.vue";
const files = require.context(
"./components",
true,
/^.\/[^/]+\/index\.vue$|^.\/[^/]+.vue$/
);
const components = files.keys().reduce(
(prev, key) => {
prev[key.match(/[^./]+/g)[0]] = files(key).default;
return prev;
},
{
Empty: "div",
}
);
export default {
name: "FormConfig",
components: {
ProxyCom,
},
provide() {
return {
getComponent: this.getComponent,
getBindData: this.getBindData,
getFormData: () => this.modelFormData,
getValue: this.getValue,
updateValue: this.updateValue,
parent: this,
getRules: this.getRules,
};
},
props: {
/**
* {
* label: String;
* key: String;
* type: 'input' | 'timePicker',
* gridArea?: "",
* gridColumn?: "",
* gridRow?: "",
* options?: {}
* }[]
*/
dFormData: {
type: Object,
default: () => {},
},
/**
* {
* label: String;
* key: String;
* type: 'input' | 'timePicker',
* gridArea?: "",
* gridColumn?: "",
* gridRow?: "",
* options?: {}
* }[]
*/
value: Object,
formList: {
type: Array,
default: () => [],
},
labelWidth: {
type: String,
default: "auto",
},
rules: {
type: Object,
default: null,
},
column: {
type: [String, Number],
default: "3",
},
},
model: {
prop: "value",
event: "update:value",
},
data() {
return {
formData: {},
};
},
watch: {
formList: {
immediate: true,
handler() {
this.reset(true);
},
},
},
// created() {
// this.reset(true);
// },
computed: {
modelFormData: {
get() {
return this.formData;
},
set(data) {
this.formData = data;
this.$emit("update:value", this.formData);
},
},
gridStyle() {
return (item, index) => ({
gridRow: `span ${item.gridRow || 1}`,
gridColumn: `span ${
item.gridColumn || (item.isAlone && this.column) || 1
}`,
});
},
formItemVisible() {
return (item) => {
const result =
item && item.visible ? item.visible(this.modelFormData) : true;
// if (!result) {
// delete this.formData[item.key];
// }
return result;
};
},
},
methods: {
getValue(item) {
return pathGet(this.formData, item.key);
},
updateValue(item, data) {
// 对于数组的修改无响应式 采用此方法
this.modelFormData = { ...pathSet(this.modelFormData, item.key, data) };
},
reset(isFirst) {
return (this.modelFormData = reduceDefaultValue(
this.formList,
isFirst ? this.value || this.dFormData : {}
));
},
getStyle() {
return {
gridTemplateColumns: `repeat(${this.column}, 1fr)`,
};
},
getBindData(item) {
let componentKey = resolveName(item.type || "input");
return {
placeholder: "请输入",
...defaultComponentOptions[componentKey],
...item.options,
};
},
getComponent(type) {
if (!type) type = "input";
const componentKey = resolveName(type);
const ElComponentKey = `El${componentKey}`;
return (
components[componentKey] || components[ElComponentKey] || ElComponentKey
);
},
getRules(item) {
// 自定义的没添加
/**
* validator 手动情况下 此方法需要改传参 ({
* // 当前总数据
* data,
* // 当前字段的值
* value,
* // 回调跟 el 一样
* callback,
* // 当前的item config
* config
* })
*/
const ruleMatch = (value) => {
/**
* @type {{ callback?: (value: any) => boolean; type?: "phone"; message: string; }[]}
*/
const rules = item.rules || [];
for (let index = 0; index < rules.length; index++) {
const rule = rules[index];
// 返回值 true 通过 false 异常
if (typeof rule.callback === "function") {
if (!rule.callback(value, this.modelFormData))
return new Error(rule.message || "内容错误");
} else if (RegexpMap[rule.type]) {
if (!RegexpMap[rule.type].reg.test(value))
return new Error(rule.message || RegexpMap[rule.type].message);
}
}
};
if (item.required)
return [
{
validator: (_, __, callback) => {
const value = pathGet(this.modelFormData, item.key);
if (
(!value && typeof value != "number") ||
(typeof value === "string" && !value.trim()) ||
(typeof value === "object" && value.length == 0)
)
return callback(
new Error(
`${item.options?.placeholder || `${item.label}不能为空`}`
)
);
const err = ruleMatch(value);
if (err) return callback(err);
callback();
},
trigger: ["blur", "change"],
},
];
else if (item.rules?.length)
return [
{
validator: (_, __, callback) => {
/**
* @type {{ callback?: (value: any) => boolean; type?: "phone"; message: string; }[]}
*/
const err = ruleMatch(pathGet(this.modelFormData, item.key));
if (err) return callback(err);
callback();
},
trigger: ["blur", "change"],
},
];
},
validate() {
return new Promise((resolve, reject) => {
this.$refs.ElFormRef.validate((bool) => {
if (bool) resolve(cloneDeep(this.modelFormData));
else reject("表单验证未通过");
});
});
},
},
};
</script>
<style lang="scss" scoped>
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active,
.list-leave-active {
transition: all 1s;
position: absolute;
}
.list-enter,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
.FormConfig {
display: grid;
align-content: start;
width: 100%;
gap: 15px 15px;
// overflow-x: hidden;
padding-right: 9px;
.formItem {
display: flex;
align-items: center;
}
::v-deep {
.el-form-item {
align-items: center;
margin: 0;
height: 100%;
width: 100%;
&:first-child {
.el-form-item__label-wrap {
// padding-top: 9px;
margin: 0 !important;
}
}
.el-form-item__label-wrap {
width: fit-content;
}
.el-form-item__label {
height: 22px;
font-size: 15px;
// font-family: PingFang SC, PingFang SC;
font-weight: 400;
color: #3de8ff;
line-height: unset;
// -webkit-background-clip: text;
// -webkit-text-fill-color: transparent;
}
.el-form-item__content {
margin: 0 !important;
flex: 1;
height: 100%;
.el-input__prefix {
color: #fff;
}
}
}
}
}
</style>