feat: 表单节点添加多选框,选项卡等组件 (#1651)

This commit is contained in:
shaohuzhang1 2024-11-19 14:24:38 +08:00 committed by GitHub
parent 1bc7f70f79
commit 6801a24c38
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 528 additions and 54 deletions

View File

@ -15,6 +15,10 @@ const input_type_list = [
label: '单选框', label: '单选框',
value: 'SingleSelect' value: 'SingleSelect'
}, },
{
label: '多选框',
value: 'MultiSelect'
},
{ {
label: '日期', label: '日期',
value: 'DatePicker' value: 'DatePicker'
@ -22,6 +26,14 @@ const input_type_list = [
{ {
label: 'JSON文本框', label: 'JSON文本框',
value: 'JsonInput' value: 'JsonInput'
},
{
label: '选项卡',
value: 'RadioCard'
},
{
label: '单行选项卡',
value: 'RadioRow'
} }
] ]
export { input_type_list } export { input_type_list }

View File

@ -0,0 +1,129 @@
<template>
<el-form-item>
<template #label>
<div class="flex-between">
选项值
<el-button link type="primary" @click.stop="addOption()">
<el-icon class="mr-4">
<Plus />
</el-icon>
添加
</el-button>
</div>
</template>
<el-row style="width: 100%" :gutter="10">
<el-col :span="10"
><div class="grid-content ep-bg-purple" />
标签</el-col
>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
选项值</el-col
>
</el-row>
<el-row
style="width: 100%"
v-for="(option, $index) in formValue.option_list"
:key="$index"
:gutter="10"
>
<el-col :span="10"
><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签"
/></el-col>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值"
/></el-col>
<el-col :span="1"
><div class="grid-content ep-bg-purple" />
<el-button link class="ml-8" @click.stop="delOption($index)">
<el-icon>
<Delete />
</el-icon> </el-button
></el-col>
</el-row>
</el-form-item>
<el-form-item
label="默认值"
:required="formValue.required"
prop="default_value"
:rules="formValue.required ? [{ required: true, message: '默认值 为必填属性' }] : []"
>
<el-select
class="m-2"
multiple
collapse-tags
filterable
clearable
v-model="formValue.default_value"
:teleported="false"
popper-class="default-select"
>
<el-option
v-for="(option, index) in formValue.option_list"
:key="index"
:label="option.value"
:value="option.value"
/>
</el-select>
</el-form-item>
</template>
<script setup lang="ts">
import { computed, onMounted } from 'vue'
const props = defineProps<{
modelValue: any
}>()
const emit = defineEmits(['update:modelValue'])
const formValue = computed({
set: (item) => {
emit('update:modelValue', item)
},
get: () => {
return props.modelValue
}
})
const addOption = () => {
formValue.value.option_list.push({ value: '', label: '' })
}
const delOption = (index: number) => {
const option = formValue.value.option_list[index]
if (option.value && formValue.value.default_value == option.value) {
formValue.value.default_value = ''
}
formValue.value.option_list.splice(index, 1)
}
const getData = () => {
return {
input_type: 'MultiSelect',
attrs: {},
default_value: formValue.value.default_value,
textField: 'label',
valueField: 'value',
option_list: formValue.value.option_list
}
}
const rander = (form_data: any) => {
formValue.value.option_list = form_data.option_list || []
formValue.value.default_value = form_data.default_value
}
defineExpose({ getData, rander })
onMounted(() => {
formValue.value.option_list = []
formValue.value.default_value = ''
})
</script>
<style lang="scss" scoped>
:deep(.el-form-item__label) {
display: block;
}
:deep(.el-select-dropdown) {
max-width: 400px;
}
</style>

View File

@ -0,0 +1,122 @@
<template>
<el-form-item>
<template #label>
<div class="flex-between">
选项值
<el-button link type="primary" @click.stop="addOption()">
<el-icon class="mr-4">
<Plus />
</el-icon>
添加
</el-button>
</div>
</template>
<el-row style="width: 100%" :gutter="10">
<el-col :span="10"
><div class="grid-content ep-bg-purple" />
标签</el-col
>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
选项值</el-col
>
</el-row>
<el-row
style="width: 100%"
v-for="(option, $index) in formValue.option_list"
:key="$index"
:gutter="10"
>
<el-col :span="10"
><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签"
/></el-col>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值"
/></el-col>
<el-col :span="1"
><div class="grid-content ep-bg-purple" />
<el-button link class="ml-8" @click.stop="delOption($index)">
<el-icon>
<Delete />
</el-icon> </el-button
></el-col>
</el-row>
</el-form-item>
<el-form-item
label="默认值"
:required="formValue.required"
prop="default_value"
:rules="formValue.required ? [{ required: true, message: '默认值 为必填属性' }] : []"
>
<RadioCard
:form-field="formField"
v-model="formValue.default_value"
:other-params="{}"
field="default_value"
>
</RadioCard>
</el-form-item>
</template>
<script setup lang="ts">
import { computed, onMounted } from 'vue'
import RadioCard from '@/components/dynamics-form/items/radio/RadioCard.vue'
const props = defineProps<{
modelValue: any
}>()
const emit = defineEmits(['update:modelValue'])
const formValue = computed({
set: (item) => {
emit('update:modelValue', item)
},
get: () => {
return props.modelValue
}
})
const addOption = () => {
formValue.value.option_list.push({ value: '', label: '' })
}
const delOption = (index: number) => {
const option = formValue.value.option_list[index]
if (option.value && formValue.value.default_value == option.value) {
formValue.value.default_value = ''
}
formValue.value.option_list.splice(index, 1)
}
const formField = computed(() => {
return getData()
})
const getData = () => {
return {
input_type: 'RadioCard',
attrs: {},
default_value: formValue.value.default_value,
text_field: 'label',
value_field: 'value',
option_list: formValue.value.option_list
}
}
const rander = (form_data: any) => {
formValue.value.option_list = form_data.option_list || []
formValue.value.default_value = form_data.default_value
}
defineExpose({ getData, rander })
onMounted(() => {
formValue.value.option_list = []
formValue.value.default_value = ''
})
</script>
<style lang="scss" scoped>
:deep(.el-form-item__label) {
display: block;
}
:deep(.el-select-dropdown) {
max-width: 400px;
}
</style>

View File

@ -0,0 +1,122 @@
<template>
<el-form-item>
<template #label>
<div class="flex-between">
选项值
<el-button link type="primary" @click.stop="addOption()">
<el-icon class="mr-4">
<Plus />
</el-icon>
添加
</el-button>
</div>
</template>
<el-row style="width: 100%" :gutter="10">
<el-col :span="10"
><div class="grid-content ep-bg-purple" />
标签</el-col
>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
选项值</el-col
>
</el-row>
<el-row
style="width: 100%"
v-for="(option, $index) in formValue.option_list"
:key="$index"
:gutter="10"
>
<el-col :span="10"
><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签"
/></el-col>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值"
/></el-col>
<el-col :span="1"
><div class="grid-content ep-bg-purple" />
<el-button link class="ml-8" @click.stop="delOption($index)">
<el-icon>
<Delete />
</el-icon> </el-button
></el-col>
</el-row>
</el-form-item>
<el-form-item
label="默认值"
:required="formValue.required"
prop="default_value"
:rules="formValue.required ? [{ required: true, message: '默认值 为必填属性' }] : []"
>
<RadioRow
:form-field="formField"
v-model="formValue.default_value"
:other-params="{}"
field="default_value"
>
</RadioRow>
</el-form-item>
</template>
<script setup lang="ts">
import { computed, onMounted } from 'vue'
import RadioRow from '@/components/dynamics-form/items/radio/RadioRow.vue'
const props = defineProps<{
modelValue: any
}>()
const emit = defineEmits(['update:modelValue'])
const formValue = computed({
set: (item) => {
emit('update:modelValue', item)
},
get: () => {
return props.modelValue
}
})
const addOption = () => {
formValue.value.option_list.push({ value: '', label: '' })
}
const delOption = (index: number) => {
const option = formValue.value.option_list[index]
if (option.value && formValue.value.default_value == option.value) {
formValue.value.default_value = ''
}
formValue.value.option_list.splice(index, 1)
}
const formField = computed(() => {
return getData()
})
const getData = () => {
return {
input_type: 'RadioCard',
attrs: {},
default_value: formValue.value.default_value,
text_field: 'label',
value_field: 'value',
option_list: formValue.value.option_list
}
}
const rander = (form_data: any) => {
formValue.value.option_list = form_data.option_list || []
formValue.value.default_value = form_data.default_value
}
defineExpose({ getData, rander })
onMounted(() => {
formValue.value.option_list = []
formValue.value.default_value = ''
})
</script>
<style lang="scss" scoped>
:deep(.el-form-item__label) {
display: block;
}
:deep(.el-select-dropdown) {
max-width: 400px;
}
</style>

View File

@ -12,18 +12,38 @@
</div> </div>
</template> </template>
<div <el-row style="width: 100%" :gutter="10">
class="w-full flex-between mb-8" <el-col :span="10"
><div class="grid-content ep-bg-purple" />
标签</el-col
>
<el-col :span="12"
><div class="grid-content ep-bg-purple" />
选项值</el-col
>
</el-row>
<el-row
style="width: 100%"
v-for="(option, $index) in formValue.option_list" v-for="(option, $index) in formValue.option_list"
:key="$index" :key="$index"
:gutter="10"
> >
<el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值" /> <el-col :span="10"
<el-button link class="ml-8" @click.stop="delOption($index)"> ><div class="grid-content ep-bg-purple" />
<el-icon> <el-input v-model="formValue.option_list[$index].label" placeholder="请输入选项标签"
<Delete /> /></el-col>
</el-icon> <el-col :span="12"
</el-button> ><div class="grid-content ep-bg-purple" />
</div> <el-input v-model="formValue.option_list[$index].value" placeholder="请输入选项值"
/></el-col>
<el-col :span="1"
><div class="grid-content ep-bg-purple" />
<el-button link class="ml-8" @click.stop="delOption($index)">
<el-icon>
<Delete />
</el-icon> </el-button
></el-col>
</el-row>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="默认值" label="默认值"
@ -58,7 +78,7 @@ const formValue = computed({
}) })
const addOption = () => { const addOption = () => {
formValue.value.option_list.push({ value: '' }) formValue.value.option_list.push({ value: '', label: '' })
} }
const delOption = (index: number) => { const delOption = (index: number) => {
@ -74,8 +94,8 @@ const getData = () => {
input_type: 'SingleSelect', input_type: 'SingleSelect',
attrs: {}, attrs: {},
default_value: formValue.value.default_value, default_value: formValue.value.default_value,
text_field: 'value', textField: 'label',
value_field: 'value', valueField: 'value',
option_list: formValue.value.option_list option_list: formValue.value.option_list
} }
} }

View File

@ -1,18 +1,19 @@
<template> <template>
<div class="radio_content"> <div class="radio_content" v-resize="resize" :style="radioContentStyle">
<div <el-card
v-for="item in option_list" v-for="item in option_list"
:key="item.value" :key="item.value"
class="item" class="item"
shadow="never"
:class="[modelValue == item[valueField] ? 'active' : '']" :class="[modelValue == item[valueField] ? 'active' : '']"
@click="selected(item[valueField])" @click="selected(item[valueField])"
> >
{{ item[textField] }} {{ item[textField] }}
</div> </el-card>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { watch, computed } from 'vue' import { computed, ref } from 'vue'
import type { FormField } from '@/components/dynamics-form/type' import type { FormField } from '@/components/dynamics-form/type'
const props = defineProps<{ const props = defineProps<{
formValue?: any formValue?: any
@ -29,7 +30,24 @@ const selected = (activeValue: string | number) => {
emit('update:modelValue', activeValue) emit('update:modelValue', activeValue)
} }
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const width = ref<number>()
const radioContentStyle = computed(() => {
if (width.value) {
if (width.value < 350) {
return { '--maxkb-radio-card-width': '316px' }
} else if (width.value > 770) {
return { '--maxkb-radio-card-width': '378px' }
} else {
return { '--maxkb-radio-card-width': '100%' }
}
}
return {}
})
const resize = (wh: any) => {
if (wh.height) {
width.value = wh.width
}
}
const textField = computed(() => { const textField = computed(() => {
return props.formField.text_field ? props.formField.text_field : 'key' return props.formField.text_field ? props.formField.text_field : 'key'
}) })
@ -41,52 +59,24 @@ const valueField = computed(() => {
const option_list = computed(() => { const option_list = computed(() => {
return props.formField.option_list ? props.formField.option_list : [] return props.formField.option_list ? props.formField.option_list : []
}) })
watch(
option_list,
() => {
if (
(option_list.value &&
option_list.value.length > 0 &&
!option_list.value.some((item) => item.value === props.modelValue)) ||
!props.modelValue
) {
emit('update:modelValue', option_list.value[0][valueField.value])
}
},
{ immediate: true }
)
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.radio_content { .radio_content {
height: 32px; display: flex;
display: inline-flex; flex-wrap: wrap;
border: 1px solid #bbbfc4; justify-content: flex-start;
border-radius: 4px; width: 100%;
font-weight: 400;
font-size: 14px;
color: #1f2329;
padding: 3px 4px;
box-sizing: border-box;
.active { .active {
border-radius: 4px; border: 1px solid var(--el-color-primary);
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
} }
.item { .item {
cursor: pointer; cursor: pointer;
margin: 0px 2px; height: 38px;
padding: 2px 8px;
height: 20px;
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
&:last-child { width: var(--maxkb-radio-card-width, 100%);
margin: 0 4px 0 2px; margin: 4px;
}
&:first-child {
margin: 0 2px 0 4px;
}
} }
} }
</style> </style>

View File

@ -0,0 +1,79 @@
<template>
<div class="radio_content">
<div
v-for="item in option_list"
:key="item.value"
class="item"
:class="[modelValue == item[valueField] ? 'active' : '']"
@click="selected(item[valueField])"
>
{{ item[textField] }}
</div>
</div>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import type { FormField } from '@/components/dynamics-form/type'
const props = defineProps<{
formValue?: any
formfieldList?: Array<FormField>
field: string
otherParams: any
formField: FormField
view?: boolean
//
modelValue?: any
}>()
const selected = (activeValue: string | number) => {
emit('update:modelValue', activeValue)
}
const emit = defineEmits(['update:modelValue'])
const textField = computed(() => {
return props.formField.text_field ? props.formField.text_field : 'key'
})
const valueField = computed(() => {
return props.formField.value_field ? props.formField.value_field : 'value'
})
const option_list = computed(() => {
return props.formField.option_list ? props.formField.option_list : []
})
</script>
<style lang="scss" scoped>
.radio_content {
height: 32px;
display: inline-flex;
border: 1px solid #bbbfc4;
border-radius: 4px;
font-weight: 400;
font-size: 14px;
color: #1f2329;
padding: 3px 4px;
box-sizing: border-box;
white-space: nowrap;
.active {
border-radius: 4px;
background: var(--el-color-primary-light-9);
color: var(--el-color-primary);
}
.item {
cursor: pointer;
margin: 0px 2px;
padding: 2px 8px;
height: 20px;
display: flex;
justify-content: center;
align-items: center;
&:last-child {
margin: 0 4px 0 2px;
}
&:first-child {
margin: 0 2px 0 4px;
}
}
}
</style>

View File

@ -76,7 +76,7 @@
<el-table-column label="必填"> <el-table-column label="必填">
<template #default="{ row }"> <template #default="{ row }">
<div @click.stop> <div @click.stop>
<el-switch disabled size="small" v-model="row.is_required" /> <el-switch disabled size="small" v-model="row.required" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column>