!413 form-create-designer: 表单组件新增字典选择器和用户选择器
Merge pull request !413 from puhui999/dev-crm
This commit is contained in:
commit
9ede8ffe4d
3
src/components/DictSelect/index.ts
Normal file
3
src/components/DictSelect/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import DictSelect from './src/DictSelect.vue'
|
||||||
|
|
||||||
|
export { DictSelect }
|
46
src/components/DictSelect/src/DictSelect.vue
Normal file
46
src/components/DictSelect/src/DictSelect.vue
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<template>
|
||||||
|
<el-select class="w-1/1" v-bind="attrs">
|
||||||
|
<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' })
|
||||||
|
</script>
|
@ -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
|
||||||
|
}
|
||||||
|
124
src/components/FormCreate/src/config/useDictSelectRule.ts
Normal file
124
src/components/FormCreate/src/config/useDictSelectRule.ts
Normal 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,是否在输入框获得焦点后自动弹出选项菜单'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
src/components/FormCreate/src/config/useUserSelectRule.ts
Normal file
93
src/components/FormCreate/src/config/useUserSelectRule.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import { generateUUID } from '@/utils'
|
||||||
|
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
|
||||||
|
|
||||||
|
export const useUserSelectRule = () => {
|
||||||
|
const label = '用户选择器'
|
||||||
|
const name = 'UserSelect'
|
||||||
|
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: '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,是否在输入框获得焦点后自动弹出选项菜单'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -19,6 +19,8 @@ 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'
|
||||||
|
import UserSelect from '@/views/system/user/components/UserSelect.vue'
|
||||||
|
|
||||||
const components = [
|
const components = [
|
||||||
ElAside,
|
ElAside,
|
||||||
@ -35,7 +37,9 @@ const components = [
|
|||||||
ElTabPane,
|
ElTabPane,
|
||||||
UploadImg,
|
UploadImg,
|
||||||
UploadImgs,
|
UploadImgs,
|
||||||
UploadFile
|
UploadFile,
|
||||||
|
DictSelect,
|
||||||
|
UserSelect
|
||||||
]
|
]
|
||||||
|
|
||||||
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
|
// 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档
|
||||||
|
@ -137,7 +137,7 @@ export enum DICT_TYPE {
|
|||||||
INFRA_FILE_STORAGE = 'infra_file_storage',
|
INFRA_FILE_STORAGE = 'infra_file_storage',
|
||||||
|
|
||||||
// ========== BPM 模块 ==========
|
// ========== BPM 模块 ==========
|
||||||
BPM_MODEL_FORM_TYPE = 'bpm_model_form_type',
|
BPM_MODEL_FORM_TYPE = 'bpm_model_category',
|
||||||
BPM_TASK_CANDIDATE_STRATEGY = 'bpm_task_candidate_strategy',
|
BPM_TASK_CANDIDATE_STRATEGY = 'bpm_task_candidate_strategy',
|
||||||
BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
|
BPM_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
|
||||||
BPM_TASK_STATUS = 'bpm_task_status',
|
BPM_TASK_STATUS = 'bpm_task_status',
|
||||||
|
28
src/views/system/user/components/UserSelect.vue
Normal file
28
src/views/system/user/components/UserSelect.vue
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!-- TODO puhui999: 先单独一个后面封装成通用选择组件 -->
|
||||||
|
<template>
|
||||||
|
<el-select class="w-1/1" v-bind="attrs">
|
||||||
|
<el-option
|
||||||
|
v-for="(dict, index) in userOptions"
|
||||||
|
:key="index"
|
||||||
|
:label="dict.nickname"
|
||||||
|
:value="dict.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import * as UserApi from '@/api/system/user'
|
||||||
|
|
||||||
|
defineOptions({ name: 'UserSelect' })
|
||||||
|
|
||||||
|
const attrs = useAttrs()
|
||||||
|
const userOptions = ref<UserApi.UserVO[]>([]) // 用户下拉数据
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const data = await UserApi.getSimpleUserList()
|
||||||
|
if (!data || data.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userOptions.value = data
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
Reference in New Issue
Block a user