Browse Source

form-create: 字典选择器分离,重新封装 api 选择器

puhui999 10 months ago
parent
commit
7c158af3cc

+ 2 - 2
src/components/FormCreate/index.ts

@@ -1,4 +1,4 @@
 import { useFormCreateDesigner } from './src/useFormCreateDesigner'
-import { useCurrencySelect } from './src/components/useCurrencySelect'
+import { useApiSelect } from './src/components/useApiSelect'
 
-export { useFormCreateDesigner, useCurrencySelect }
+export { useFormCreateDesigner, useApiSelect }

+ 59 - 0
src/components/FormCreate/src/components/DictSelect.vue

@@ -0,0 +1,59 @@
+<!-- 数据字典 Select 选择器 -->
+<template>
+  <el-select v-if="selectType === 'select'" class="w-1/1" v-bind="attrs">
+    <el-option
+      v-for="(dict, index) in getDictOptions"
+      :key="index"
+      :label="dict.label"
+      :value="dict.value"
+    />
+  </el-select>
+  <el-radio-group v-if="selectType === 'radio'" class="w-1/1" v-bind="attrs">
+    <el-radio v-for="(dict, index) in getDictOptions" :key="index" :value="dict.value">
+      {{ dict.label }}
+    </el-radio>
+  </el-radio-group>
+  <el-checkbox-group v-if="selectType === 'checkbox'" class="w-1/1" v-bind="attrs">
+    <el-checkbox
+      v-for="(dict, index) in getDictOptions"
+      :key="index"
+      :label="dict.label"
+      :value="dict.value"
+    />
+  </el-checkbox-group>
+</template>
+
+<script lang="ts" setup>
+import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
+
+defineOptions({ name: 'DictSelect' })
+
+const attrs = useAttrs()
+
+// 接受父组件参数
+interface Props {
+  dictType: string // 字典类型
+  valueType?: 'str' | 'int' | 'bool' // 字典值类型
+  selectType?: 'select' | 'radio' | 'checkbox' // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
+  formCreateInject?: any
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  valueType: 'str',
+  selectType: 'select'
+})
+
+// 获得字典配置
+const getDictOptions = computed(() => {
+  switch (props.valueType) {
+    case 'str':
+      return getStrDictOptions(props.dictType)
+    case 'int':
+      return getIntDictOptions(props.dictType)
+    case 'bool':
+      return getBoolDictOptions(props.dictType)
+    default:
+      return []
+  }
+})
+</script>

+ 40 - 39
src/components/FormCreate/src/components/useCurrencySelect.tsx → src/components/FormCreate/src/components/useApiSelect.tsx

@@ -1,9 +1,9 @@
 import request from '@/config/axios'
 import { isEmpty } from '@/utils/is'
-import { CurrencySelectProps } from '@/components/FormCreate/src/type'
-import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
+import { ApiSelectProps } from '@/components/FormCreate/src/type'
+import { jsonParse } from '@/utils'
 
-export const useCurrencySelect = (option: CurrencySelectProps) => {
+export const useApiSelect = (option: ApiSelectProps) => {
   return defineComponent({
     name: option.name,
     props: {
@@ -18,47 +18,50 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
         default: () => option.valueField ?? 'value'
       },
       // api 接口
-      restful: {
+      url: {
         type: String,
-        default: () => option.restful ?? ''
+        default: () => option.url ?? ''
       },
-      // 字典类型
-      dictType: {
+      // 请求类型
+      method: {
         type: String,
-        default: ''
+        default: 'GET'
       },
-      // 字典值类型 'str' | 'int' | 'bool'
-      dictValueType: {
+      // 请求参数
+      data: {
         type: String,
-        default: 'str'
+        default: ''
       },
       // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
       selectType: {
         type: String,
         default: 'select'
+      },
+      // 是否多选
+      multiple: {
+        type: Boolean,
+        default: false
       }
-      // // 是否多选
-      // multiple: {
-      //   type: Boolean,
-      //   default: false
-      // }
     },
     setup(props) {
       const attrs = useAttrs()
       const options = ref<any[]>([]) // 下拉数据
       const getOptions = async () => {
         options.value = []
-        // 字典选择器
-        if (option.isDict) {
-          options.value = getDictOptions()
-          return
-        }
         // 接口选择器
-        if (isEmpty(props.restful)) {
+        if (isEmpty(props.url)) {
           return
         }
-        // TODO 只支持 GET 查询,复杂下拉构建条件请使用业务表单
-        const data = await request.get({ url: props.restful })
+        let data = []
+        switch (props.method) {
+          case 'GET':
+            data = await request.get({ url: props.url })
+            break
+          case 'POST':
+            data = await request.post({ url: props.url, data: jsonParse(props.data) })
+            break
+        }
+
         if (Array.isArray(data)) {
           options.value = data.map((item: any) => ({
             label: item[props.labelField],
@@ -66,26 +69,24 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
           }))
           return
         }
-        console.log(`接口[${props.restful}] 返回结果不是一个数组`)
-      }
-      // 获得字典配置
-      const getDictOptions = () => {
-        switch (props.dictValueType) {
-          case 'str':
-            return getStrDictOptions(props.dictType)
-          case 'int':
-            return getIntDictOptions(props.dictType)
-          case 'bool':
-            return getBoolDictOptions(props.dictType)
-          default:
-            return []
-        }
+        console.log(`接口[${props.url}] 返回结果不是一个数组`)
       }
+
       onMounted(async () => {
         await getOptions()
       })
-      // TODO puhui999: 单下拉多选的时候页面会卡住报错,下次解决
+
       const buildSelect = () => {
+        if (props.multiple) {
+          // fix:多写此步是为了解决 multiple 属性问题
+          return (
+            <el-select class="w-1/1" {...attrs} multiple>
+              {options.value.map((item, index) => (
+                <el-option key={index} label={item.label} value={item.value} />
+              ))}
+            </el-select>
+          )
+        }
         return (
           <el-select class="w-1/1" {...attrs}>
             {options.value.map((item, index) => (

+ 66 - 1
src/components/FormCreate/src/config/selectRule.ts

@@ -8,6 +8,15 @@ const selectRule = [
       { label: '下拉框', value: 'select' },
       { label: '单选框', value: 'radio' },
       { label: '多选框', value: 'checkbox' }
+    ],
+    // 参考 https://www.form-create.com/v3/guide/control 组件联动,单选框和多选框不需要多选属性
+    control: [
+      {
+        value: 'select',
+        condition: '=',
+        method: 'hidden',
+        rule: ['multiple']
+      }
     ]
   },
   { type: 'switch', field: 'multiple', title: '是否多选' },
@@ -79,4 +88,60 @@ const selectRule = [
   }
 ]
 
-export default selectRule
+const apiSelectRule = [
+  {
+    type: 'input',
+    field: 'url',
+    title: 'url 地址',
+    props: {
+      placeholder: '/system/user/simple-list'
+    }
+  },
+  {
+    type: 'select',
+    field: 'method',
+    title: '请求类型',
+    value: 'GET',
+    options: [
+      { label: 'GET', value: 'GET' },
+      { label: 'POST', value: 'POST' }
+    ],
+    control: [
+      {
+        value: 'GET',
+        condition: '!=',
+        method: 'hidden',
+        rule: [
+          {
+            type: 'input',
+            field: 'data',
+            title: '请求参数 JSON 格式',
+            props: {
+              autosize: true,
+              type: 'textarea',
+              placeholder: '{"type": 1}'
+            }
+          }
+        ]
+      }
+    ]
+  },
+  {
+    type: 'input',
+    field: 'labelField',
+    title: 'label 属性',
+    props: {
+      placeholder: 'nickname'
+    }
+  },
+  {
+    type: 'input',
+    field: 'valueField',
+    title: 'value 属性',
+    props: {
+      placeholder: 'id'
+    }
+  }
+]
+
+export { selectRule, apiSelectRule }

+ 1 - 1
src/components/FormCreate/src/config/useDictSelectRule.ts

@@ -1,7 +1,7 @@
 import { generateUUID } from '@/utils'
 import * as DictDataApi from '@/api/system/dict/dict.type'
 import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
-import selectRule from '@/components/FormCreate/src/config/selectRule'
+import { selectRule } from '@/components/FormCreate/src/config/selectRule'
 
 /**
  * 字典选择器规则,如果规则使用到动态数据则需要单独配置不能使用 useSelectRule

+ 1 - 1
src/components/FormCreate/src/config/useSelectRule.ts

@@ -1,6 +1,6 @@
 import { generateUUID } from '@/utils'
 import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
-import selectRule from '@/components/FormCreate/src/config/selectRule'
+import { selectRule } from '@/components/FormCreate/src/config/selectRule'
 import { SelectRuleOption } from '@/components/FormCreate/src/type'
 
 /**

+ 6 - 6
src/components/FormCreate/src/type/index.ts

@@ -1,13 +1,13 @@
 import { Rule } from '@form-create/element-ui' //左侧拖拽按钮
 
-//左侧拖拽按钮
+// 左侧拖拽按钮
 export interface MenuItem {
   label: string
   name: string
   icon: string
 }
 
-//左侧拖拽按钮分类
+// 左侧拖拽按钮分类
 export interface Menu {
   title: string
   name: string
@@ -16,7 +16,7 @@ export interface Menu {
 
 export interface MenuList extends Array<Menu> {}
 
-//拖拽组件的规则
+// 拖拽组件的规则
 export interface DragRule {
   icon: string
   name: string
@@ -33,11 +33,11 @@ export interface DragRule {
 }
 
 // 通用下拉组件 Props 类型
-export interface CurrencySelectProps {
+export interface ApiSelectProps {
   name: string // 组件名称
   labelField?: string // 选项标签
   valueField?: string // 选项的值
-  restful?: string // api 接口
+  url?: string // url 接口
   isDict?: boolean // 是否字典选择器
 }
 
@@ -45,5 +45,5 @@ export interface CurrencySelectProps {
 export interface SelectRuleOption {
   label: string // label 名称
   name: string // 组件名称
-  props?: Rule[] // 组件规则
+  props?: any[] // 组件规则
 }

+ 6 - 29
src/components/FormCreate/src/useFormCreateDesigner.ts

@@ -8,6 +8,7 @@ import {
 } from './config'
 import { Ref } from 'vue'
 import { Menu } from '@/components/FormCreate/src/type'
+import { apiSelectRule } from '@/components/FormCreate/src/config/selectRule'
 
 /**
  * 表单设计器增强 hook
@@ -50,36 +51,12 @@ export const useFormCreateDesigner = async (designer: Ref) => {
   const userSelectRule = useSelectRule({ name: 'UserSelect', label: '用户选择器' })
   const deptSelectRule = useSelectRule({ name: 'DeptSelect', label: '部门选择器' })
   const dictSelectRule = useDictSelectRule()
-  const restfulSelectRule = useSelectRule({
-    name: 'RestfulSelect',
+  const apiSelectRule0 = useSelectRule({
+    name: 'ApiSelect',
     label: '接口选择器',
-    props: [
-      {
-        type: 'input',
-        field: 'restful',
-        title: 'restful api 接口',
-        props: {
-          placeholder: '/system/user/simple-list'
-        }
-      },
-      {
-        type: 'input',
-        field: 'labelField',
-        title: 'label 属性',
-        props: {
-          placeholder: 'nickname'
-        }
-      },
-      {
-        type: 'input',
-        field: 'valueField',
-        title: 'value 属性',
-        props: {
-          placeholder: 'id'
-        }
-      }
-    ]
+    props: [...apiSelectRule]
   })
+
   /**
    * 构建系统字段菜单
    */
@@ -88,7 +65,7 @@ export const useFormCreateDesigner = async (designer: Ref) => {
     designer.value?.removeMenuItem('select')
     designer.value?.removeMenuItem('radio')
     designer.value?.removeMenuItem('checkbox')
-    const components = [userSelectRule, deptSelectRule, dictSelectRule, restfulSelectRule]
+    const components = [userSelectRule, deptSelectRule, dictSelectRule, apiSelectRule0]
     const menu: Menu = {
       name: 'system',
       title: '系统字段',

+ 10 - 12
src/plugins/formCreate/index.ts

@@ -19,28 +19,26 @@ import formCreate from '@form-create/element-ui'
 import install from '@form-create/element-ui/auto-import'
 //======================= 自定义组件 =======================
 import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile'
-import { useCurrencySelect } from '@/components/FormCreate'
+import { useApiSelect } from '@/components/FormCreate'
 import { Editor } from '@/components/Editor'
+import DictSelect from '@/components/FormCreate/src/components/DictSelect.vue'
 
-const UserSelect = useCurrencySelect({
+const UserSelect = useApiSelect({
   name: 'UserSelect',
   labelField: 'nickname',
   valueField: 'id',
-  restful: '/system/user/simple-list'
+  url: '/system/user/simple-list'
 })
-const DeptSelect = useCurrencySelect({
+const DeptSelect = useApiSelect({
   name: 'DeptSelect',
   labelField: 'name',
   valueField: 'id',
-  restful: '/system/dept/simple-list'
+  url: '/system/dept/simple-list'
 })
-const RestfulSelect = useCurrencySelect({
-  name: 'RestfulSelect'
-})
-const DictSelect = useCurrencySelect({
-  name: 'DictSelect',
-  isDict: true
+const ApiSelect = useApiSelect({
+  name: 'ApiSelect'
 })
+
 const components = [
   ElAside,
   ElPopconfirm,
@@ -60,7 +58,7 @@ const components = [
   DictSelect,
   UserSelect,
   DeptSelect,
-  RestfulSelect,
+  ApiSelect,
   Editor
 ]
 

+ 14 - 0
src/utils/index.ts

@@ -435,3 +435,17 @@ export const areaReplace = (areaName: string) => {
     .replace('自治区', '')
     .replace('省', '')
 }
+
+/**
+ * 解析 JSON 字符串
+ *
+ * @param str
+ */
+export function jsonParse(str: string) {
+  try {
+    return JSON.parse(str)
+  } catch (e) {
+    console.log(`str[${str}] 不是一个 JSON 字符串`)
+    return ''
+  }
+}