form-create-designer: 表单组件新增字典选择器

This commit is contained in:
puhui999 2024-03-30 23:18:23 +08:00
parent d05c67ffaa
commit 68dae2d1fe
8 changed files with 375 additions and 4 deletions

View File

@ -0,0 +1,3 @@
import DictSelect from './src/DictSelect.vue'
export { DictSelect }

View File

@ -0,0 +1,53 @@
<template>
<el-select :model-value="modelValue" class="w-1/1" v-bind="attrs" @change="changeValue">
<template v-if="valueType === 'int'">
<el-option
v-for="(dict, index) in getIntDictOptions(dictType)"
:key="index"
:label="dict.label"
:value="dict.value"
/>
</template>
<template v-if="valueType === 'str'">
<el-option
v-for="(dict, index) in getStrDictOptions(dictType)"
:key="index"
:label="dict.label"
:value="dict.value"
/>
</template>
<template v-if="valueType === 'bool'">
<el-option
v-for="(dict, index) in getBoolDictOptions(dictType)"
:key="index"
:label="dict.label"
:value="dict.value"
/>
</template>
</el-select>
</template>
<script lang="ts" setup>
import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
//
interface Props {
modelValue?: any //
dictType: string //
valueType: string //
}
withDefaults(defineProps<Props>(), {
dictType: '',
valueType: 'str'
})
const attrs = useAttrs()
defineOptions({ name: 'DictSelect' })
const emits = defineEmits<{
(e: 'update:modelValue', v: any): void
}>()
const changeValue = (value: any) => {
console.log(value)
emits('update:modelValue', value)
}
</script>

View File

@ -1,5 +1,13 @@
import { useUploadFileRule } from './useUploadFileRule' import { useUploadFileRule } from './useUploadFileRule'
import { useUploadImgRule } from './useUploadImgRule' import { useUploadImgRule } from './useUploadImgRule'
import { useUploadImgsRule } from './useUploadImgsRule' import { useUploadImgsRule } from './useUploadImgsRule'
import { useDictSelectRule } from './useDictSelectRule'
import { useUserSelectRule } from './useUserSelectRule'
export { useUploadFileRule, useUploadImgRule, useUploadImgsRule } export {
useUploadFileRule,
useUploadImgRule,
useUploadImgsRule,
useDictSelectRule,
useUserSelectRule
}

View File

@ -0,0 +1,124 @@
import { generateUUID } from '@/utils'
import * as DictDataApi from '@/api/system/dict/dict.type'
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
export const useDictSelectRule = () => {
const label = '字典选择器'
const name = 'DictSelect'
const dictOptions = ref<{ label: string; value: string }[]>([]) // 字典类型下拉数据
onMounted(async () => {
const data = await DictDataApi.getSimpleDictTypeList()
if (!data || data.length === 0) {
return
}
dictOptions.value =
data?.map((item: DictDataApi.DictTypeVO) => ({
label: item.name,
value: item.type
})) ?? []
})
return {
icon: 'icon-select',
label,
name,
rule() {
return {
type: name,
field: generateUUID(),
title: label,
info: '',
$required: false
}
},
props(_, { t }) {
return localeProps(t, name + '.props', [
makeRequiredRule(),
{
type: 'select',
field: 'dictType',
title: '字典类型',
value: '',
options: dictOptions.value
},
{
type: 'select',
field: 'valueType',
title: '字典值类型',
value: 'str',
options: [
{ label: '数字', value: 'int' },
{ label: '字符串', value: 'str' },
{ label: '布尔值', value: 'bool' }
]
},
{ type: 'switch', field: 'multiple', title: '是否多选' },
{
type: 'switch',
field: 'disabled',
title: '是否禁用'
},
{ type: 'switch', field: 'clearable', title: '是否可以清空选项' },
{
type: 'switch',
field: 'collapseTags',
title: '多选时是否将选中值按文字的形式展示'
},
{
type: 'inputNumber',
field: 'multipleLimit',
title: '多选时用户最多可以选择的项目数,为 0 则不限制',
props: { min: 0 }
},
{
type: 'input',
field: 'autocomplete',
title: 'autocomplete 属性'
},
{ type: 'input', field: 'placeholder', title: '占位符' },
{
type: 'switch',
field: 'filterable',
title: '是否可搜索'
},
{ type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
{
type: 'input',
field: 'noMatchText',
title: '搜索条件无匹配时显示的文字'
},
{
type: 'switch',
field: 'remote',
title: '其中的选项是否从服务器远程加载'
},
{
type: 'Struct',
field: 'remoteMethod',
title: '自定义远程搜索方法'
},
{ type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
{
type: 'switch',
field: 'reserveKeyword',
title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词'
},
{
type: 'switch',
field: 'defaultFirstOption',
title: '在输入框按下回车,选择第一个匹配项'
},
{
type: 'switch',
field: 'popperAppendToBody',
title: '是否将弹出框插入至 body 元素',
value: true
},
{
type: 'switch',
field: 'automaticDropdown',
title: '对于不可搜索的 Select是否在输入框获得焦点后自动弹出选项菜单'
}
])
}
}
}

View File

@ -0,0 +1,107 @@
import { generateUUID } from '@/utils'
import * as UserApi from '@/api/system/user'
import { localeProps, makeOptionsRule, makeRequiredRule } from '@/components/FormCreate/src/utils'
export const useUserSelectRule = () => {
const label = 'aa选择器'
const name = 'select'
const userOptions = ref<{ label: string; value: number }[]>([]) // 用户下拉数据
onMounted(async () => {
const data = await UserApi.getSimpleUserList()
if (!data || data.length === 0) {
return
}
userOptions.value =
data?.map((item: UserApi.UserVO) => ({
label: item.nickname,
value: item.id
})) ?? []
})
return {
icon: 'icon-select',
label,
name,
rule() {
return {
type: name,
field: generateUUID(),
title: label,
info: '',
$required: false
}
},
props(_, { t }) {
return localeProps(t, name + '.props', [
makeRequiredRule(),
makeOptionsRule(t, 'options', userOptions.value),
{ type: 'switch', field: 'multiple', title: '是否多选' },
{
type: 'switch',
field: 'disabled',
title: '是否禁用'
},
{ type: 'switch', field: 'clearable', title: '是否可以清空选项' },
{
type: 'switch',
field: 'collapseTags',
title: '多选时是否将选中值按文字的形式展示'
},
{
type: 'inputNumber',
field: 'multipleLimit',
title: '多选时用户最多可以选择的项目数,为 0 则不限制',
props: { min: 0 }
},
{
type: 'input',
field: 'autocomplete',
title: 'autocomplete 属性'
},
{ type: 'input', field: 'placeholder', title: '占位符' },
{
type: 'switch',
field: 'filterable',
title: '是否可搜索'
},
{ type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
{
type: 'input',
field: 'noMatchText',
title: '搜索条件无匹配时显示的文字'
},
{
type: 'switch',
field: 'remote',
title: '其中的选项是否从服务器远程加载'
},
{
type: 'Struct',
field: 'remoteMethod',
title: '自定义远程搜索方法'
},
{ type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
{
type: 'switch',
field: 'reserveKeyword',
title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词'
},
{
type: 'switch',
field: 'defaultFirstOption',
title: '在输入框按下回车,选择第一个匹配项'
},
{
type: 'switch',
field: 'popperAppendToBody',
title: '是否将弹出框插入至 body 元素',
value: true
},
{
type: 'switch',
field: 'automaticDropdown',
title: '对于不可搜索的 Select是否在输入框获得焦点后自动弹出选项菜单'
}
])
}
}
}

View File

@ -1,4 +1,10 @@
import { useUploadFileRule, useUploadImgRule, useUploadImgsRule } from './config' import {
useDictSelectRule,
useUploadFileRule,
useUploadImgRule,
useUploadImgsRule,
useUserSelectRule
} from './config'
import { Ref } from 'vue' import { Ref } from 'vue'
/** /**
@ -12,11 +18,19 @@ export const useFormCreateDesigner = (designer: Ref) => {
const uploadFileRule = useUploadFileRule() const uploadFileRule = useUploadFileRule()
const uploadImgRule = useUploadImgRule() const uploadImgRule = useUploadImgRule()
const uploadImgsRule = useUploadImgsRule() const uploadImgsRule = useUploadImgsRule()
const dictSelectRule = useDictSelectRule()
const userSelectRule = useUserSelectRule()
onMounted(() => { onMounted(() => {
// 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代 // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
designer.value?.removeMenuItem('upload') designer.value?.removeMenuItem('upload')
const components = [uploadFileRule, uploadImgRule, uploadImgsRule] const components = [
uploadFileRule,
uploadImgRule,
uploadImgsRule,
dictSelectRule,
userSelectRule
]
components.forEach((component) => { components.forEach((component) => {
// 插入组件规则 // 插入组件规则
designer.value?.addComponent(component) designer.value?.addComponent(component)

View File

@ -17,3 +17,63 @@ export const localeProps = (t, prefix, rules) => {
return rule return rule
}) })
} }
export function upper(str) {
return str.replace(str[0], str[0].toLocaleUpperCase())
}
export function makeOptionsRule(t, to, userOptions) {
console.log(userOptions[0])
const options = [
{ label: t('props.optionsType.struct'), value: 0 },
{ label: t('props.optionsType.json'), value: 1 },
{ label: '用户数据', value: 2 }
]
const control = [
{
value: 0,
rule: [
{
type: 'TableOptions',
field: 'formCreate' + upper(to).replace('.', '>'),
props: { defaultValue: [] }
}
]
},
{
value: 1,
rule: [
{
type: 'Struct',
field: 'formCreate' + upper(to).replace('.', '>'),
props: { defaultValue: [] }
}
]
},
{
value: 2,
rule: [
{
type: 'TableOptions',
field: 'formCreate' + upper(to).replace('.', '>'),
props: { modelValue: [] }
}
]
}
]
options.splice(0, 0)
control.push()
return {
type: 'radio',
title: t('props.options'),
field: '_optionType',
value: 0,
options,
props: {
type: 'button'
},
control
}
}

View File

@ -19,6 +19,7 @@ import formCreate from '@form-create/element-ui'
import install from '@form-create/element-ui/auto-import' import install from '@form-create/element-ui/auto-import'
//======================= 自定义组件 ======================= //======================= 自定义组件 =======================
import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile' import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile'
import { DictSelect } from '@/components/DictSelect'
const components = [ const components = [
ElAside, ElAside,
@ -35,7 +36,8 @@ const components = [
ElTabPane, ElTabPane,
UploadImg, UploadImg,
UploadImgs, UploadImgs,
UploadFile UploadFile,
DictSelect
] ]
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档 // 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档