first commit
This commit is contained in:
304
web/src/components/formItem/createData.vue
Normal file
304
web/src/components/formItem/createData.vue
Normal file
@@ -0,0 +1,304 @@
|
||||
<!-- 一个用于创建 FormItem 数据的组件 -->
|
||||
<!-- 使用场景举例:系统配置->添加配置项 -->
|
||||
<template>
|
||||
<div>
|
||||
<!-- 本组件不被 el-form 包含,方便您在其他 el-form 的任意位置使用,且没有带了一个 el-form 时会出现的负担 -->
|
||||
<!-- formitem 已经设置了 prop 属性,以便外部 el-form 添加表单验证规则 -->
|
||||
<FormItem
|
||||
v-if="form.name.show"
|
||||
:label="form.name.title"
|
||||
type="string"
|
||||
v-model="form.name.value"
|
||||
:placeholder="t('Please input field', { field: form.name.title })"
|
||||
:input-attr="{
|
||||
onChange: updateValue,
|
||||
...props.options?.name?.inputAttr,
|
||||
}"
|
||||
prop="name"
|
||||
/>
|
||||
<FormItem
|
||||
v-if="form.title.show"
|
||||
:label="form.title.title"
|
||||
type="string"
|
||||
v-model="form.title.value"
|
||||
:placeholder="t('Please input field', { field: form.title.title })"
|
||||
:input-attr="{
|
||||
onChange: updateValue,
|
||||
...props.options?.title?.inputAttr,
|
||||
}"
|
||||
prop="title"
|
||||
/>
|
||||
<FormItem
|
||||
v-if="form.type.show"
|
||||
:label="form.type.title"
|
||||
type="select"
|
||||
v-model="form.type.value"
|
||||
:placeholder="t('Please select field', { field: form.type.title })"
|
||||
:input-attr="{
|
||||
onChange: updateValue,
|
||||
content: state.inputTypes,
|
||||
...props.options?.type?.inputAttr,
|
||||
}"
|
||||
prop="type"
|
||||
/>
|
||||
<FormItem
|
||||
v-if="form.dict.show && dictExistsType.includes(form.type.value!)"
|
||||
:label="form.dict.title"
|
||||
type="textarea"
|
||||
v-model="form.dict.value"
|
||||
:input-attr="{
|
||||
rows: 3,
|
||||
placeholder: t('utils.One line at a time, without quotation marks, for example: key1=value1'),
|
||||
onChange: updateValue,
|
||||
...props.options?.dict?.inputAttr,
|
||||
}"
|
||||
prop="dict"
|
||||
@keyup.enter.stop=""
|
||||
/>
|
||||
<FormItem
|
||||
v-if="form.tip.show"
|
||||
:label="form.tip.title"
|
||||
type="string"
|
||||
v-model="form.tip.value"
|
||||
:placeholder="t('Please input field', { field: form.tip.title })"
|
||||
:input-attr="{
|
||||
onChange: updateValue,
|
||||
...props.options?.tip?.inputAttr,
|
||||
}"
|
||||
prop="tip"
|
||||
/>
|
||||
<FormItem
|
||||
v-if="form.rule.show"
|
||||
:label="form.rule.title"
|
||||
type="selects"
|
||||
v-model="form.rule.value"
|
||||
:placeholder="t('Please select field', { field: form.rule.title })"
|
||||
:input-attr="{
|
||||
onChange: updateValue,
|
||||
content: state.validators,
|
||||
...props.options?.rule?.inputAttr,
|
||||
}"
|
||||
prop="rule"
|
||||
/>
|
||||
<FormItem
|
||||
v-if="form.extend.show"
|
||||
:label="form.extend.title"
|
||||
type="textarea"
|
||||
v-model="form.extend.value"
|
||||
:input-attr="{
|
||||
onChange: updateValue,
|
||||
placeholder: t('utils.One attribute per line without quotation marks(formitem)'),
|
||||
...props.options?.extend?.inputAttr,
|
||||
}"
|
||||
prop="extend"
|
||||
@keyup.enter.stop=""
|
||||
/>
|
||||
<FormItem
|
||||
v-if="form.inputExtend.show"
|
||||
:label="form.inputExtend.title"
|
||||
type="textarea"
|
||||
v-model="form.inputExtend.value"
|
||||
:input-attr="{
|
||||
onChange: updateValue,
|
||||
placeholder: t('utils.Extended properties of Input, one line without quotation marks, such as: size=large'),
|
||||
...props.options?.inputExtend?.inputAttr,
|
||||
}"
|
||||
prop="inputExtend"
|
||||
@keyup.enter.stop=""
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import FormItem from '/@/components/formItem/index.vue'
|
||||
import { inputTypes } from '/@/components/baInput'
|
||||
import { validatorType } from '/@/utils/validate'
|
||||
import type { InputAttr } from '/@/components/baInput'
|
||||
import { i18n } from '/@/lang'
|
||||
|
||||
const { t } = i18n.global
|
||||
|
||||
type OptionItem = {
|
||||
// 是否显示(被创建的数据是否需要这一项)
|
||||
show?: boolean
|
||||
// 被创建数据的标题(默认使用:props.dataTitle + 本 title,自定义此 title 后,单使用此 title)
|
||||
title?: string
|
||||
// 输入框额外属性
|
||||
inputAttr?: InputAttr
|
||||
}
|
||||
|
||||
type ValidatesOptionItem = Omit<OptionItem, 'value'> & {
|
||||
value?: string[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
// 被创建数据的标题,作为所有表单项的前缀(默认值)
|
||||
dataTitle?: string
|
||||
// 默认值
|
||||
modelValue: {
|
||||
name?: string
|
||||
title?: string
|
||||
type?: string
|
||||
tip?: string
|
||||
rule?: string[]
|
||||
extend?: string
|
||||
dict?: string
|
||||
inputExtend?: string
|
||||
}
|
||||
// 表单项配置
|
||||
options?: {
|
||||
// 变量名
|
||||
name?: OptionItem
|
||||
// 标题
|
||||
title?: OptionItem
|
||||
// 类型
|
||||
type?: OptionItem
|
||||
// 提示信息
|
||||
tip?: OptionItem
|
||||
// 验证规则
|
||||
rule?: ValidatesOptionItem
|
||||
// FormItem 的扩展属性
|
||||
extend?: OptionItem
|
||||
// 字典数据(单选、复选等类型的字典)
|
||||
dict?: OptionItem
|
||||
// Input 的扩展属性
|
||||
inputExtend?: OptionItem
|
||||
}
|
||||
excludeInputTypes?: string[]
|
||||
excludeValidatorRule?: string[]
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
dataTitle: i18n.global.t('utils.Var'),
|
||||
modelValue: () => {
|
||||
return {
|
||||
name: '',
|
||||
title: '',
|
||||
type: '',
|
||||
tip: '',
|
||||
rule: [],
|
||||
extend: '',
|
||||
dict: '',
|
||||
inputExtend: '',
|
||||
}
|
||||
},
|
||||
name: () => {
|
||||
return {}
|
||||
},
|
||||
title: () => {
|
||||
return {}
|
||||
},
|
||||
type: () => {
|
||||
return {}
|
||||
},
|
||||
tip: () => {
|
||||
return {}
|
||||
},
|
||||
rule: () => {
|
||||
return {}
|
||||
},
|
||||
extend: () => {
|
||||
return {}
|
||||
},
|
||||
dict: () => {
|
||||
return {}
|
||||
},
|
||||
inputExtend: () => {
|
||||
return {}
|
||||
},
|
||||
excludeInputTypes: () => [],
|
||||
excludeValidatorRule: () => [],
|
||||
})
|
||||
|
||||
const dictExistsType = ['radio', 'checkbox', 'select', 'selects']
|
||||
|
||||
const form = reactive({
|
||||
name: {
|
||||
show: props.options?.name?.show === false ? false : true,
|
||||
value: props.modelValue.name,
|
||||
title: props.options?.name?.title ?? props.dataTitle + t('utils.Name'), // 变量名
|
||||
},
|
||||
title: {
|
||||
show: props.options?.title?.show === false ? false : true,
|
||||
value: props.modelValue.title,
|
||||
title: props.options?.title?.title ?? props.dataTitle + t('utils.Title'), // 变量标题
|
||||
},
|
||||
type: {
|
||||
show: props.options?.type?.show === false ? false : true,
|
||||
value: props.modelValue.type,
|
||||
title: props.options?.type?.title ?? props.dataTitle + t('utils.type'), // 变量类型
|
||||
},
|
||||
tip: {
|
||||
show: props.options?.tip?.show === false ? false : true,
|
||||
value: props.modelValue.tip,
|
||||
title: props.options?.tip?.title ?? t('utils.Tip'), // 提示信息
|
||||
},
|
||||
rule: {
|
||||
show: props.options?.rule?.show === false ? false : true,
|
||||
value: props.modelValue.rule,
|
||||
title: props.options?.rule?.title ?? t('utils.Rule'), // 验证规则
|
||||
},
|
||||
extend: {
|
||||
show: props.options?.extend?.show === false ? false : true,
|
||||
value: props.modelValue.extend,
|
||||
title: props.options?.extend?.title ?? 'FormItem ' + t('utils.Extend'), // FormItem 扩展属性
|
||||
},
|
||||
dict: {
|
||||
show: props.options?.dict?.show === false ? false : true,
|
||||
value: props.modelValue.dict,
|
||||
title: props.options?.dict?.title ?? t('utils.Dict'), // 字典数据
|
||||
},
|
||||
inputExtend: {
|
||||
show: props.options?.inputExtend?.show === false ? false : true,
|
||||
value: props.modelValue.inputExtend,
|
||||
title: props.options?.inputExtend?.title ?? 'Input ' + t('utils.Extend'), // Input 扩展属性
|
||||
},
|
||||
})
|
||||
|
||||
const state = reactive({
|
||||
validators: {},
|
||||
inputTypes: {},
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'update:modelValue', value: Props['modelValue']): void
|
||||
}>()
|
||||
|
||||
const updateValue = () => {
|
||||
emits('update:modelValue', {
|
||||
name: form.name.value ?? '',
|
||||
title: form.title.value ?? '',
|
||||
type: form.type.value ?? '',
|
||||
tip: form.tip.value ?? '',
|
||||
rule: form.rule.value ?? [],
|
||||
extend: form.extend.value ?? '',
|
||||
dict: dictExistsType.includes(form.type.value ?? '') ? (form.dict.value ?? '') : '',
|
||||
inputExtend: form.inputExtend.value ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
const dataPretreatment = () => {
|
||||
let inputTypesKey: anyObj = {}
|
||||
for (const key in inputTypes) {
|
||||
if (!props.excludeInputTypes.includes(inputTypes[key])) {
|
||||
inputTypesKey[inputTypes[key]] = inputTypes[key]
|
||||
}
|
||||
}
|
||||
state.inputTypes = inputTypesKey
|
||||
|
||||
let validators: anyObj = {}
|
||||
for (const key in validatorType) {
|
||||
if (!props.excludeValidatorRule.includes(key)) {
|
||||
validators[key] = validatorType[key as keyof typeof validatorType]
|
||||
}
|
||||
}
|
||||
state.validators = validators
|
||||
|
||||
updateValue()
|
||||
}
|
||||
|
||||
dataPretreatment()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
13
web/src/components/formItem/index.ts
Normal file
13
web/src/components/formItem/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { CSSProperties } from 'vue'
|
||||
import type { FormItemProps, ElTooltipProps } from 'element-plus'
|
||||
|
||||
export interface FormItemAttr extends Partial<Writeable<FormItemProps>> {
|
||||
// 通用属性名称的键入提示
|
||||
id?: string
|
||||
class?: string
|
||||
style?: CSSProperties
|
||||
// 块级输入帮助信息
|
||||
blockHelp?: string
|
||||
// 输入提示信息(使用 el-tooltip 渲染)
|
||||
tip?: string | Partial<ElTooltipProps>
|
||||
}
|
||||
163
web/src/components/formItem/index.vue
Normal file
163
web/src/components/formItem/index.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<script lang="ts">
|
||||
import { formItemProps } from 'element-plus'
|
||||
import type { PropType, VNode } from 'vue'
|
||||
import { computed, createVNode, defineComponent, resolveComponent } from 'vue'
|
||||
import type { InputAttr, InputData, ModelValueTypes } from '/@/components/baInput'
|
||||
import { inputTypes } from '/@/components/baInput'
|
||||
import BaInput from '/@/components/baInput/index.vue'
|
||||
import type { FormItemAttr } from '/@/components/formItem'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'formItem',
|
||||
props: {
|
||||
// 输入框类型,支持的输入框见 inputTypes
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (value: string) => {
|
||||
return inputTypes.includes(value)
|
||||
},
|
||||
},
|
||||
// 双向绑定值
|
||||
modelValue: {
|
||||
required: true,
|
||||
},
|
||||
// 输入框的附加属性
|
||||
inputAttr: {
|
||||
type: Object as PropType<InputAttr>,
|
||||
default: () => {},
|
||||
},
|
||||
blockHelp: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
tip: [String, Object],
|
||||
// el-form-item 的附加属性(还可以直接通过当前组件的 props 传递)
|
||||
attr: {
|
||||
type: Object as PropType<FormItemAttr>,
|
||||
default: () => {},
|
||||
},
|
||||
// 额外数据(已和 props.inputAttr 合并,还可以通过它进行传递)
|
||||
data: {
|
||||
type: Object as PropType<InputData>,
|
||||
default: () => {},
|
||||
},
|
||||
// 内部输入框的 placeholder(相当于 props.inputAttr.placeholder 的别名)
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
...formItemProps,
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
setup(props, { emit, slots }) {
|
||||
// 通过 props 和 props.attr 两种方式传递的属性汇总为 attrs
|
||||
const excludeProps = ['type', 'modelValue', 'inputAttr', 'attr', 'data', 'placeholder']
|
||||
const attrs = computed(() => {
|
||||
const newAttrs = props.attr || {}
|
||||
for (const key in props) {
|
||||
const propValue: any = props[key as keyof typeof props]
|
||||
if (!excludeProps.includes(key) && (propValue || propValue === false)) {
|
||||
newAttrs[key as keyof typeof props.attr] = propValue
|
||||
}
|
||||
}
|
||||
return newAttrs
|
||||
})
|
||||
|
||||
const onValueUpdate = (value: ModelValueTypes) => {
|
||||
emit('update:modelValue', value)
|
||||
}
|
||||
|
||||
// el-form-item 的插槽
|
||||
const formItemSlots: { [key: string]: () => VNode | VNode[] } = {}
|
||||
|
||||
// default 插槽
|
||||
formItemSlots.default = () => {
|
||||
let inputNode = createVNode(
|
||||
BaInput,
|
||||
{
|
||||
type: props.type,
|
||||
attr: { placeholder: props.placeholder, ...props.inputAttr, ...props.data },
|
||||
modelValue: props.modelValue,
|
||||
'onUpdate:modelValue': onValueUpdate,
|
||||
},
|
||||
slots
|
||||
)
|
||||
|
||||
if (attrs.value.blockHelp) {
|
||||
return [
|
||||
inputNode,
|
||||
createVNode(
|
||||
'div',
|
||||
{
|
||||
class: 'block-help',
|
||||
},
|
||||
attrs.value.blockHelp
|
||||
),
|
||||
]
|
||||
}
|
||||
return inputNode
|
||||
}
|
||||
|
||||
if (attrs.value.tip) {
|
||||
const createTipNode = () => {
|
||||
const tipProps = typeof attrs.value.tip === 'string' ? { content: attrs.value.tip, placement: 'top' } : attrs.value.tip
|
||||
return createVNode(resolveComponent('el-tooltip'), tipProps, {
|
||||
default: () => [
|
||||
createVNode('i', {
|
||||
class: 'fa fal fa-question-circle',
|
||||
}),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
// label 插槽
|
||||
formItemSlots.label = () => {
|
||||
return createVNode(
|
||||
'span',
|
||||
{
|
||||
class: 'ba-form-item-label',
|
||||
},
|
||||
[
|
||||
createVNode('span', null, attrs.value.label),
|
||||
createVNode(
|
||||
'span',
|
||||
{
|
||||
class: 'ba-form-item-label-tip',
|
||||
},
|
||||
[createTipNode()]
|
||||
),
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return () =>
|
||||
createVNode(
|
||||
resolveComponent('el-form-item'),
|
||||
{
|
||||
class: 'ba-input-item-' + props.type,
|
||||
...attrs.value,
|
||||
},
|
||||
formItemSlots
|
||||
)
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.ba-form-item-label-tip {
|
||||
padding-left: 6px;
|
||||
color: var(--el-text-color-secondary);
|
||||
i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.ba-form-item-not-support {
|
||||
line-height: 15px;
|
||||
}
|
||||
.ba-input-item-array :deep(.el-form-item__content) {
|
||||
display: block;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user