first commit

This commit is contained in:
2026-04-16 14:16:41 +08:00
commit 5c33491ae2
586 changed files with 58153 additions and 0 deletions

View 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>

View 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>
}

View 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>