Sfoglia il codice sorgente

!413 form-create-designer: 表单组件新增字典选择器和用户选择器
Merge pull request !413 from puhui999/dev-crm

芋道源码 1 anno fa
parent
commit
9ede8ffe4d

+ 3 - 0
src/components/DictSelect/index.ts

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

+ 46 - 0
src/components/DictSelect/src/DictSelect.vue

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

+ 9 - 1
src/components/FormCreate/src/config/index.ts

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

+ 124 - 0
src/components/FormCreate/src/config/useDictSelectRule.ts

@@ -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 - 0
src/components/FormCreate/src/config/useUserSelectRule.ts

@@ -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,是否在输入框获得焦点后自动弹出选项菜单'
+        }
+      ])
+    }
+  }
+}

+ 16 - 2
src/components/FormCreate/src/useFormCreateDesigner.ts

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

+ 60 - 0
src/components/FormCreate/src/utils/index.ts

@@ -17,3 +17,63 @@ export const localeProps = (t, prefix, rules) => {
     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
+  }
+}

+ 5 - 1
src/plugins/formCreate/index.ts

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

+ 3 - 3
src/utils/dict.ts

@@ -1,8 +1,8 @@
 /**
  * 数据字典工具类
  */
-import { useDictStoreWithOut } from '@/store/modules/dict'
-import { ElementPlusInfoType } from '@/types/elementPlus'
+import {useDictStoreWithOut} from '@/store/modules/dict'
+import {ElementPlusInfoType} from '@/types/elementPlus'
 
 const dictStore = useDictStoreWithOut()
 
@@ -137,7 +137,7 @@ export enum DICT_TYPE {
   INFRA_FILE_STORAGE = 'infra_file_storage',
 
   // ========== 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_PROCESS_INSTANCE_STATUS = 'bpm_process_instance_status',
   BPM_TASK_STATUS = 'bpm_task_status',

+ 28 - 0
src/views/system/user/components/UserSelect.vue

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