From 769b63d9ca797dd402861e2e6a3110d43f423123 Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Tue, 30 Apr 2024 00:07:25 +0800
Subject: [PATCH 1/7] =?UTF-8?q?form-create=EF=BC=9A=E7=A7=BB=E9=99=A4?=
 =?UTF-8?q?=E8=87=AA=E5=B8=A6=E7=9A=84=E4=B8=8B=E6=8B=89=E9=80=89=E6=8B=A9?=
 =?UTF-8?q?=E5=99=A8=E7=BB=84=E4=BB=B6=EF=BC=8C=E4=BD=BF=E7=94=A8=20curren?=
 =?UTF-8?q?cySelectRule=20=E6=9B=BF=E4=BB=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/FormCreate/index.ts            |  3 +-
 .../FormCreate/src/CurrencySelect/index.vue   | 53 +++++++++++++++++++
 src/components/FormCreate/src/config/index.ts |  4 +-
 .../src/config/useCurrencySelectRule.ts       | 36 +++++++++++++
 .../src/config/useUserSelectRule.ts           | 25 ---------
 .../FormCreate/src/useFormCreateDesigner.ts   | 10 ++--
 src/plugins/formCreate/index.ts               |  4 +-
 .../system/user/components/UserSelect.vue     | 28 ----------
 8 files changed, 101 insertions(+), 62 deletions(-)
 create mode 100644 src/components/FormCreate/src/CurrencySelect/index.vue
 create mode 100644 src/components/FormCreate/src/config/useCurrencySelectRule.ts
 delete mode 100644 src/components/FormCreate/src/config/useUserSelectRule.ts
 delete mode 100644 src/views/system/user/components/UserSelect.vue

diff --git a/src/components/FormCreate/index.ts b/src/components/FormCreate/index.ts
index d50ed3c6..66fc7e6f 100644
--- a/src/components/FormCreate/index.ts
+++ b/src/components/FormCreate/index.ts
@@ -1,3 +1,4 @@
 import { useFormCreateDesigner } from './src/useFormCreateDesigner'
+import CurrencySelect from './src/CurrencySelect/index.vue'
 
-export { useFormCreateDesigner }
+export { useFormCreateDesigner, CurrencySelect }
diff --git a/src/components/FormCreate/src/CurrencySelect/index.vue b/src/components/FormCreate/src/CurrencySelect/index.vue
new file mode 100644
index 00000000..3591a935
--- /dev/null
+++ b/src/components/FormCreate/src/CurrencySelect/index.vue
@@ -0,0 +1,53 @@
+<template>
+  <el-select class="w-1/1" v-bind="attrs">
+    <el-option
+      v-for="(item, index) in options"
+      :key="index"
+      :label="item.label"
+      :value="item.value"
+    />
+  </el-select>
+</template>
+
+<script lang="ts" setup>
+import request from '@/config/axios'
+import { isEmpty } from '@/utils/is'
+
+defineOptions({ name: 'CurrencySelect' })
+
+// 接受父组件参数
+interface Props {
+  labelField?: string // 字典类型
+  valueField?: string // 字典值类型
+  restful?: string // api 接口
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  labelField: 'nickname',
+  valueField: 'id',
+  restful: '/system/user/simple-list'
+})
+
+const attrs = useAttrs()
+const options = ref<any[]>([]) // 下拉数据
+const getOptions = async () => {
+  options.value = []
+  if (isEmpty(props.restful)) {
+    return
+  }
+  // TODO 只支持 GET 查询,复杂下拉构建条件请使用业务表单
+  const data = await request.get({ url: props.restful })
+  if (Array.isArray(data)) {
+    options.value = data.map((item: any) => ({
+      label: item[props.labelField],
+      value: item[props.valueField]
+    }))
+    return
+  }
+  console.log(`接口[${props.restful}] 返回结果不是一个数组`)
+}
+
+onMounted(() => {
+  getOptions()
+})
+</script>
diff --git a/src/components/FormCreate/src/config/index.ts b/src/components/FormCreate/src/config/index.ts
index c3939159..078bc803 100644
--- a/src/components/FormCreate/src/config/index.ts
+++ b/src/components/FormCreate/src/config/index.ts
@@ -2,7 +2,7 @@ import { useUploadFileRule } from './useUploadFileRule'
 import { useUploadImgRule } from './useUploadImgRule'
 import { useUploadImgsRule } from './useUploadImgsRule'
 import { useDictSelectRule } from './useDictSelectRule'
-import { useUserSelectRule } from './useUserSelectRule'
+import { useCurrencySelectRule } from './useCurrencySelectRule'
 import { useEditorRule } from './useEditorRule'
 
 export {
@@ -10,6 +10,6 @@ export {
   useUploadImgRule,
   useUploadImgsRule,
   useDictSelectRule,
-  useUserSelectRule,
+  useCurrencySelectRule,
   useEditorRule
 }
diff --git a/src/components/FormCreate/src/config/useCurrencySelectRule.ts b/src/components/FormCreate/src/config/useCurrencySelectRule.ts
new file mode 100644
index 00000000..7a7b2031
--- /dev/null
+++ b/src/components/FormCreate/src/config/useCurrencySelectRule.ts
@@ -0,0 +1,36 @@
+import { generateUUID } from '@/utils'
+import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
+import selectRule from '@/components/FormCreate/src/config/selectRule'
+
+export const useCurrencySelectRule = () => {
+  const label = '通用选择器'
+  const name = 'CurrencySelect'
+  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: 'input',
+          field: 'restful',
+          title: 'restful api 接口',
+          value: '/system/user/simple-list'
+        },
+        { type: 'input', field: 'labelField', title: 'label 属性', value: 'nickname' },
+        { type: 'input', field: 'valueField', title: 'value 属性', value: 'id' },
+        ...selectRule
+      ])
+    }
+  }
+}
diff --git a/src/components/FormCreate/src/config/useUserSelectRule.ts b/src/components/FormCreate/src/config/useUserSelectRule.ts
deleted file mode 100644
index dd5e51c3..00000000
--- a/src/components/FormCreate/src/config/useUserSelectRule.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { generateUUID } from '@/utils'
-import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
-import selectRule from '@/components/FormCreate/src/config/selectRule'
-
-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(), ...selectRule])
-    }
-  }
-}
diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts
index fe42b24f..81a595cc 100644
--- a/src/components/FormCreate/src/useFormCreateDesigner.ts
+++ b/src/components/FormCreate/src/useFormCreateDesigner.ts
@@ -1,10 +1,10 @@
 import {
+  useCurrencySelectRule,
   useDictSelectRule,
   useEditorRule,
   useUploadFileRule,
   useUploadImgRule,
-  useUploadImgsRule,
-  useUserSelectRule
+  useUploadImgsRule
 } from './config'
 import { Ref } from 'vue'
 
@@ -24,20 +24,22 @@ export const useFormCreateDesigner = (designer: Ref) => {
   const uploadImgRule = useUploadImgRule()
   const uploadImgsRule = useUploadImgsRule()
   const dictSelectRule = useDictSelectRule()
-  const userSelectRule = useUserSelectRule()
+  const currencySelectRule = useCurrencySelectRule()
 
   onMounted(() => {
     // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
     designer.value?.removeMenuItem('upload')
     // 移除自带的富文本组件规则,使用 editorRule 替代
     designer.value?.removeMenuItem('fc-editor')
+    // 移除自带的下拉选择器组件,使用 currencySelectRule 替代
+    designer.value?.removeMenuItem('select')
     const components = [
       editorRule,
       uploadFileRule,
       uploadImgRule,
       uploadImgsRule,
       dictSelectRule,
-      userSelectRule
+      currencySelectRule
     ]
     components.forEach((component) => {
       // 插入组件规则
diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts
index 5f8428e6..39be7946 100644
--- a/src/plugins/formCreate/index.ts
+++ b/src/plugins/formCreate/index.ts
@@ -20,7 +20,7 @@ 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'
+import { CurrencySelect } from '@/components/FormCreate'
 import { Editor } from '@/components/Editor'
 
 const components = [
@@ -40,7 +40,7 @@ const components = [
   UploadImgs,
   UploadFile,
   DictSelect,
-  UserSelect,
+  CurrencySelect,
   Editor
 ]
 
diff --git a/src/views/system/user/components/UserSelect.vue b/src/views/system/user/components/UserSelect.vue
deleted file mode 100644
index 4a94a745..00000000
--- a/src/views/system/user/components/UserSelect.vue
+++ /dev/null
@@ -1,28 +0,0 @@
-<!-- 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>

From 1f1ac1f2461b38190d3a730dc270c20182575a2a Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Tue, 30 Apr 2024 14:15:44 +0800
Subject: [PATCH 2/7] =?UTF-8?q?=E4=BD=BF=E7=94=A8=20tsx=20=E5=B0=81?=
 =?UTF-8?q?=E8=A3=85=20form-create=20=E9=80=9A=E7=94=A8=E9=80=89=E6=8B=A9?=
 =?UTF-8?q?=E7=BB=84=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/FormCreate/index.ts            |  4 +-
 .../FormCreate/src/CurrencySelect/index.vue   | 53 -----------------
 .../src/components/useCurrencySelect.tsx      | 59 +++++++++++++++++++
 src/components/FormCreate/src/config/index.ts |  6 +-
 .../src/config/useCurrencySelectRule.ts       |  3 +-
 .../src/config/useDeptSelectRule.ts           | 26 ++++++++
 .../src/config/useUserSelectRule.ts           | 26 ++++++++
 src/components/FormCreate/src/type/index.ts   | 41 +++++++++++++
 .../FormCreate/src/useFormCreateDesigner.ts   | 40 ++++++++++++-
 src/plugins/formCreate/index.ts               | 18 +++++-
 src/views/infra/build/index.vue               |  6 +-
 11 files changed, 217 insertions(+), 65 deletions(-)
 delete mode 100644 src/components/FormCreate/src/CurrencySelect/index.vue
 create mode 100644 src/components/FormCreate/src/components/useCurrencySelect.tsx
 create mode 100644 src/components/FormCreate/src/config/useDeptSelectRule.ts
 create mode 100644 src/components/FormCreate/src/config/useUserSelectRule.ts
 create mode 100644 src/components/FormCreate/src/type/index.ts

diff --git a/src/components/FormCreate/index.ts b/src/components/FormCreate/index.ts
index 66fc7e6f..9da34889 100644
--- a/src/components/FormCreate/index.ts
+++ b/src/components/FormCreate/index.ts
@@ -1,4 +1,4 @@
 import { useFormCreateDesigner } from './src/useFormCreateDesigner'
-import CurrencySelect from './src/CurrencySelect/index.vue'
+import { useCurrencySelect } from './src/components/useCurrencySelect'
 
-export { useFormCreateDesigner, CurrencySelect }
+export { useFormCreateDesigner, useCurrencySelect }
diff --git a/src/components/FormCreate/src/CurrencySelect/index.vue b/src/components/FormCreate/src/CurrencySelect/index.vue
deleted file mode 100644
index 3591a935..00000000
--- a/src/components/FormCreate/src/CurrencySelect/index.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<template>
-  <el-select class="w-1/1" v-bind="attrs">
-    <el-option
-      v-for="(item, index) in options"
-      :key="index"
-      :label="item.label"
-      :value="item.value"
-    />
-  </el-select>
-</template>
-
-<script lang="ts" setup>
-import request from '@/config/axios'
-import { isEmpty } from '@/utils/is'
-
-defineOptions({ name: 'CurrencySelect' })
-
-// 接受父组件参数
-interface Props {
-  labelField?: string // 字典类型
-  valueField?: string // 字典值类型
-  restful?: string // api 接口
-}
-
-const props = withDefaults(defineProps<Props>(), {
-  labelField: 'nickname',
-  valueField: 'id',
-  restful: '/system/user/simple-list'
-})
-
-const attrs = useAttrs()
-const options = ref<any[]>([]) // 下拉数据
-const getOptions = async () => {
-  options.value = []
-  if (isEmpty(props.restful)) {
-    return
-  }
-  // TODO 只支持 GET 查询,复杂下拉构建条件请使用业务表单
-  const data = await request.get({ url: props.restful })
-  if (Array.isArray(data)) {
-    options.value = data.map((item: any) => ({
-      label: item[props.labelField],
-      value: item[props.valueField]
-    }))
-    return
-  }
-  console.log(`接口[${props.restful}] 返回结果不是一个数组`)
-}
-
-onMounted(() => {
-  getOptions()
-})
-</script>
diff --git a/src/components/FormCreate/src/components/useCurrencySelect.tsx b/src/components/FormCreate/src/components/useCurrencySelect.tsx
new file mode 100644
index 00000000..ae9636a3
--- /dev/null
+++ b/src/components/FormCreate/src/components/useCurrencySelect.tsx
@@ -0,0 +1,59 @@
+import request from '@/config/axios'
+import { isEmpty } from '@/utils/is'
+import { CurrencySelectProps } from '@/components/FormCreate/src/type'
+
+export const useCurrencySelect = (option: CurrencySelectProps) => {
+  return defineComponent({
+    name: option.name,
+    props: {
+      // 字典类型
+      labelField: {
+        type: String,
+        default: () => option.labelField ?? ''
+      },
+      // 字典值类型
+      valueField: {
+        type: String,
+        default: () => option.valueField ?? ''
+      },
+      // api 接口
+      restful: {
+        type: String,
+        default: () => option.restful ?? ''
+      }
+    },
+    setup(props) {
+      const attrs = useAttrs()
+      const options = ref<any[]>([]) // 下拉数据
+      const getOptions = async () => {
+        options.value = []
+        if (isEmpty(props.restful)) {
+          return
+        }
+        // TODO 只支持 GET 查询,复杂下拉构建条件请使用业务表单
+        const data = await request.get({ url: props.restful })
+        if (Array.isArray(data)) {
+          options.value = data.map((item: any) => ({
+            label: item[props.labelField],
+            value: item[props.valueField]
+          }))
+          return
+        }
+        console.log(`接口[${props.restful}] 返回结果不是一个数组`)
+      }
+
+      onMounted(async () => {
+        await getOptions()
+      })
+      return () => (
+        <>
+          <el-select className="w-1/1" {...attrs}>
+            {options.value.map((item, index) => (
+              <el-option key={index} label={item.label} value={item.value} />
+            ))}
+          </el-select>
+        </>
+      )
+    }
+  })
+}
diff --git a/src/components/FormCreate/src/config/index.ts b/src/components/FormCreate/src/config/index.ts
index 078bc803..c34d831a 100644
--- a/src/components/FormCreate/src/config/index.ts
+++ b/src/components/FormCreate/src/config/index.ts
@@ -4,6 +4,8 @@ import { useUploadImgsRule } from './useUploadImgsRule'
 import { useDictSelectRule } from './useDictSelectRule'
 import { useCurrencySelectRule } from './useCurrencySelectRule'
 import { useEditorRule } from './useEditorRule'
+import { useUserSelectRule } from './useUserSelectRule'
+import { useDeptSelectRule } from './useDeptSelectRule'
 
 export {
   useUploadFileRule,
@@ -11,5 +13,7 @@ export {
   useUploadImgsRule,
   useDictSelectRule,
   useCurrencySelectRule,
-  useEditorRule
+  useEditorRule,
+  useUserSelectRule,
+  useDeptSelectRule
 }
diff --git a/src/components/FormCreate/src/config/useCurrencySelectRule.ts b/src/components/FormCreate/src/config/useCurrencySelectRule.ts
index 7a7b2031..3ac65a66 100644
--- a/src/components/FormCreate/src/config/useCurrencySelectRule.ts
+++ b/src/components/FormCreate/src/config/useCurrencySelectRule.ts
@@ -1,8 +1,9 @@
 import { generateUUID } from '@/utils'
 import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
 import selectRule from '@/components/FormCreate/src/config/selectRule'
+import { DragRule } from '@/components/FormCreate/src/type'
 
-export const useCurrencySelectRule = () => {
+export const useCurrencySelectRule = (): DragRule => {
   const label = '通用选择器'
   const name = 'CurrencySelect'
   return {
diff --git a/src/components/FormCreate/src/config/useDeptSelectRule.ts b/src/components/FormCreate/src/config/useDeptSelectRule.ts
new file mode 100644
index 00000000..e46165c1
--- /dev/null
+++ b/src/components/FormCreate/src/config/useDeptSelectRule.ts
@@ -0,0 +1,26 @@
+import { generateUUID } from '@/utils'
+import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
+import selectRule from '@/components/FormCreate/src/config/selectRule'
+import { DragRule } from '@/components/FormCreate/src/type'
+
+export const useDeptSelectRule = (): DragRule => {
+  const label = '部门选择器'
+  const name = 'DeptSelect'
+  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(), ...selectRule])
+    }
+  }
+}
diff --git a/src/components/FormCreate/src/config/useUserSelectRule.ts b/src/components/FormCreate/src/config/useUserSelectRule.ts
new file mode 100644
index 00000000..ff5bc869
--- /dev/null
+++ b/src/components/FormCreate/src/config/useUserSelectRule.ts
@@ -0,0 +1,26 @@
+import { generateUUID } from '@/utils'
+import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
+import selectRule from '@/components/FormCreate/src/config/selectRule'
+import { DragRule } from '@/components/FormCreate/src/type'
+
+export const useUserSelectRule = (): DragRule => {
+  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(), ...selectRule])
+    }
+  }
+}
diff --git a/src/components/FormCreate/src/type/index.ts b/src/components/FormCreate/src/type/index.ts
new file mode 100644
index 00000000..38205073
--- /dev/null
+++ b/src/components/FormCreate/src/type/index.ts
@@ -0,0 +1,41 @@
+import { Rule } from '@form-create/element-ui' //左侧拖拽按钮
+
+//左侧拖拽按钮
+export interface MenuItem {
+  label: string
+  name: string
+  icon: string
+}
+
+//左侧拖拽按钮分类
+export interface Menu {
+  title: string
+  name: string
+  list: MenuItem[]
+}
+
+export interface MenuList extends Array<Menu> {}
+
+//拖拽组件的规则
+export interface DragRule {
+  icon: string
+  name: string
+  label: string
+  children?: string
+  inside?: true
+  drag?: true | String
+  dragBtn?: false
+  mask?: false
+
+  rule(): Rule
+
+  props(v: any, v1: any): Rule[]
+}
+
+// 通用下拉组件 Props 类型
+export interface CurrencySelectProps {
+  name: string // 组件名称
+  labelField?: string // 字典类型
+  valueField?: string // 字典值类型
+  restful?: string // api 接口
+}
diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts
index 81a595cc..1601b1c4 100644
--- a/src/components/FormCreate/src/useFormCreateDesigner.ts
+++ b/src/components/FormCreate/src/useFormCreateDesigner.ts
@@ -1,12 +1,15 @@
 import {
   useCurrencySelectRule,
+  useDeptSelectRule,
   useDictSelectRule,
   useEditorRule,
   useUploadFileRule,
   useUploadImgRule,
-  useUploadImgsRule
+  useUploadImgsRule,
+  useUserSelectRule
 } from './config'
 import { Ref } from 'vue'
+import { Menu } from '@/components/FormCreate/src/type'
 
 /**
  * 表单设计器增强 hook
@@ -26,7 +29,10 @@ export const useFormCreateDesigner = (designer: Ref) => {
   const dictSelectRule = useDictSelectRule()
   const currencySelectRule = useCurrencySelectRule()
 
-  onMounted(() => {
+  /**
+   * 构建表单组件
+   */
+  const buildFormComponents = () => {
     // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
     designer.value?.removeMenuItem('upload')
     // 移除自带的富文本组件规则,使用 editorRule 替代
@@ -51,5 +57,35 @@ export const useFormCreateDesigner = (designer: Ref) => {
         label: component.label
       })
     })
+  }
+
+  const userSelectRule = useUserSelectRule()
+  const deptSelectRule = useDeptSelectRule()
+  /**
+   * 构建系统字段菜单
+   */
+  const buildSystemMenu = () => {
+    const components = [userSelectRule, deptSelectRule]
+    const menu: Menu = {
+      name: 'system',
+      title: '系统字段',
+      list: components.map((component) => {
+        // 插入组件规则
+        designer.value?.addComponent(component)
+        // 插入拖拽按钮到 `system` 分类下
+        return {
+          icon: component.icon,
+          name: component.name,
+          label: component.label
+        }
+      })
+    }
+    designer.value?.addMenu(menu)
+  }
+
+  onMounted(async () => {
+    await nextTick()
+    buildFormComponents()
+    buildSystemMenu()
   })
 }
diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts
index 39be7946..da74a7bf 100644
--- a/src/plugins/formCreate/index.ts
+++ b/src/plugins/formCreate/index.ts
@@ -20,9 +20,22 @@ import install from '@form-create/element-ui/auto-import'
 //======================= 自定义组件 =======================
 import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile'
 import { DictSelect } from '@/components/DictSelect'
-import { CurrencySelect } from '@/components/FormCreate'
+import { useCurrencySelect } from '@/components/FormCreate'
 import { Editor } from '@/components/Editor'
 
+const UserSelect = useCurrencySelect({
+  name: 'UserSelect',
+  labelField: 'nickname',
+  valueField: 'id',
+  restful: '/system/user/simple-list'
+})
+const DeptSelect = useCurrencySelect({
+  name: 'DeptSelect',
+  labelField: 'name',
+  valueField: 'id',
+  restful: '/system/dept/simple-list'
+})
+
 const components = [
   ElAside,
   ElPopconfirm,
@@ -40,7 +53,8 @@ const components = [
   UploadImgs,
   UploadFile,
   DictSelect,
-  CurrencySelect,
+  UserSelect,
+  DeptSelect,
   Editor
 ]
 
diff --git a/src/views/infra/build/index.vue b/src/views/infra/build/index.vue
index 9cee56f0..571acffe 100644
--- a/src/views/infra/build/index.vue
+++ b/src/views/infra/build/index.vue
@@ -8,11 +8,9 @@
           <el-button size="small" type="danger" @click="showTemplate">生成组件</el-button>
         </div>
       </el-col>
-      <!-- 表单设计器 -->
-      <el-col>
-        <FcDesigner ref="designer" height="780px" />
-      </el-col>
     </el-row>
+    <!-- 表单设计器 -->
+    <FcDesigner ref="designer" height="780px" />
   </ContentWrap>
 
   <!-- 弹窗:表单预览 -->

From c1c21d826d72ffa75223378bef68d99419d3b167 Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Wed, 1 May 2024 17:29:13 +0800
Subject: [PATCH 3/7] =?UTF-8?q?form-create:=20=E5=B0=81=E8=A3=85=E9=80=9A?=
 =?UTF-8?q?=E7=94=A8=E9=80=89=E6=8B=A9=E5=99=A8=20hook?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/FormCreate/src/config/index.ts |  8 +---
 .../src/config/useCurrencySelectRule.ts       | 37 --------------
 .../src/config/useDictSelectRule.ts           |  3 ++
 ...{useDeptSelectRule.ts => useSelectRule.ts} | 17 +++++--
 .../src/config/useUserSelectRule.ts           | 26 ----------
 src/components/FormCreate/src/type/index.ts   |  7 +++
 .../FormCreate/src/useFormCreateDesigner.ts   | 48 +++++++++++++++----
 src/plugins/formCreate/index.ts               |  5 +-
 8 files changed, 66 insertions(+), 85 deletions(-)
 delete mode 100644 src/components/FormCreate/src/config/useCurrencySelectRule.ts
 rename src/components/FormCreate/src/config/{useDeptSelectRule.ts => useSelectRule.ts} (59%)
 delete mode 100644 src/components/FormCreate/src/config/useUserSelectRule.ts

diff --git a/src/components/FormCreate/src/config/index.ts b/src/components/FormCreate/src/config/index.ts
index c34d831a..b1e2ddea 100644
--- a/src/components/FormCreate/src/config/index.ts
+++ b/src/components/FormCreate/src/config/index.ts
@@ -2,18 +2,14 @@ import { useUploadFileRule } from './useUploadFileRule'
 import { useUploadImgRule } from './useUploadImgRule'
 import { useUploadImgsRule } from './useUploadImgsRule'
 import { useDictSelectRule } from './useDictSelectRule'
-import { useCurrencySelectRule } from './useCurrencySelectRule'
 import { useEditorRule } from './useEditorRule'
-import { useUserSelectRule } from './useUserSelectRule'
-import { useDeptSelectRule } from './useDeptSelectRule'
+import { useSelectRule } from './useSelectRule'
 
 export {
   useUploadFileRule,
   useUploadImgRule,
   useUploadImgsRule,
   useDictSelectRule,
-  useCurrencySelectRule,
   useEditorRule,
-  useUserSelectRule,
-  useDeptSelectRule
+  useSelectRule
 }
diff --git a/src/components/FormCreate/src/config/useCurrencySelectRule.ts b/src/components/FormCreate/src/config/useCurrencySelectRule.ts
deleted file mode 100644
index 3ac65a66..00000000
--- a/src/components/FormCreate/src/config/useCurrencySelectRule.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { generateUUID } from '@/utils'
-import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
-import selectRule from '@/components/FormCreate/src/config/selectRule'
-import { DragRule } from '@/components/FormCreate/src/type'
-
-export const useCurrencySelectRule = (): DragRule => {
-  const label = '通用选择器'
-  const name = 'CurrencySelect'
-  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: 'input',
-          field: 'restful',
-          title: 'restful api 接口',
-          value: '/system/user/simple-list'
-        },
-        { type: 'input', field: 'labelField', title: 'label 属性', value: 'nickname' },
-        { type: 'input', field: 'valueField', title: 'value 属性', value: 'id' },
-        ...selectRule
-      ])
-    }
-  }
-}
diff --git a/src/components/FormCreate/src/config/useDictSelectRule.ts b/src/components/FormCreate/src/config/useDictSelectRule.ts
index 7a1afc60..3350cb8e 100644
--- a/src/components/FormCreate/src/config/useDictSelectRule.ts
+++ b/src/components/FormCreate/src/config/useDictSelectRule.ts
@@ -3,6 +3,9 @@ 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'
 
+/**
+ * 字典选择器规则,如果规则使用到动态数据则需要单独配置不能使用 useSelectRule
+ */
 export const useDictSelectRule = () => {
   const label = '字典选择器'
   const name = 'DictSelect'
diff --git a/src/components/FormCreate/src/config/useDeptSelectRule.ts b/src/components/FormCreate/src/config/useSelectRule.ts
similarity index 59%
rename from src/components/FormCreate/src/config/useDeptSelectRule.ts
rename to src/components/FormCreate/src/config/useSelectRule.ts
index e46165c1..fd23ac3a 100644
--- a/src/components/FormCreate/src/config/useDeptSelectRule.ts
+++ b/src/components/FormCreate/src/config/useSelectRule.ts
@@ -1,11 +1,15 @@
 import { generateUUID } from '@/utils'
 import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
 import selectRule from '@/components/FormCreate/src/config/selectRule'
-import { DragRule } from '@/components/FormCreate/src/type'
+import { SelectRuleOption } from '@/components/FormCreate/src/type'
 
-export const useDeptSelectRule = (): DragRule => {
-  const label = '部门选择器'
-  const name = 'DeptSelect'
+/**
+ * 通用选择器规则 hook
+ * @param option 规则配置
+ */
+export const useSelectRule = (option: SelectRuleOption) => {
+  const label = option.label
+  const name = option.name
   return {
     icon: 'icon-select',
     label,
@@ -20,7 +24,10 @@ export const useDeptSelectRule = (): DragRule => {
       }
     },
     props(_, { t }) {
-      return localeProps(t, name + '.props', [makeRequiredRule(), ...selectRule])
+      if (!option.props) {
+        option.props = []
+      }
+      return localeProps(t, name + '.props', [makeRequiredRule(), ...option.props, ...selectRule])
     }
   }
 }
diff --git a/src/components/FormCreate/src/config/useUserSelectRule.ts b/src/components/FormCreate/src/config/useUserSelectRule.ts
deleted file mode 100644
index ff5bc869..00000000
--- a/src/components/FormCreate/src/config/useUserSelectRule.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { generateUUID } from '@/utils'
-import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
-import selectRule from '@/components/FormCreate/src/config/selectRule'
-import { DragRule } from '@/components/FormCreate/src/type'
-
-export const useUserSelectRule = (): DragRule => {
-  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(), ...selectRule])
-    }
-  }
-}
diff --git a/src/components/FormCreate/src/type/index.ts b/src/components/FormCreate/src/type/index.ts
index 38205073..8500d776 100644
--- a/src/components/FormCreate/src/type/index.ts
+++ b/src/components/FormCreate/src/type/index.ts
@@ -39,3 +39,10 @@ export interface CurrencySelectProps {
   valueField?: string // 字典值类型
   restful?: string // api 接口
 }
+
+// 选择组件规则配置类型
+export interface SelectRuleOption {
+  label: string // label 名称
+  name: string // 组件名称
+  props?: Rule[] // 组件规则
+}
diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts
index 1601b1c4..7c4e48b8 100644
--- a/src/components/FormCreate/src/useFormCreateDesigner.ts
+++ b/src/components/FormCreate/src/useFormCreateDesigner.ts
@@ -1,12 +1,10 @@
 import {
-  useCurrencySelectRule,
-  useDeptSelectRule,
   useDictSelectRule,
   useEditorRule,
+  useSelectRule,
   useUploadFileRule,
   useUploadImgRule,
-  useUploadImgsRule,
-  useUserSelectRule
+  useUploadImgsRule
 } from './config'
 import { Ref } from 'vue'
 import { Menu } from '@/components/FormCreate/src/type'
@@ -18,16 +16,46 @@ import { Menu } from '@/components/FormCreate/src/type'
  * - 单图上传
  * - 多图上传
  * - 字典选择器
- * - 系统用户选择器
+ * - 用户选择器
+ * - 部门选择器
  * - 富文本
  */
-export const useFormCreateDesigner = (designer: Ref) => {
+export const useFormCreateDesigner = async (designer: Ref) => {
   const editorRule = useEditorRule()
   const uploadFileRule = useUploadFileRule()
   const uploadImgRule = useUploadImgRule()
   const uploadImgsRule = useUploadImgsRule()
   const dictSelectRule = useDictSelectRule()
-  const currencySelectRule = useCurrencySelectRule()
+  const restfulSelectRule = useSelectRule({
+    name: 'RestfulSelect',
+    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'
+        }
+      }
+    ]
+  })
 
   /**
    * 构建表单组件
@@ -45,7 +73,7 @@ export const useFormCreateDesigner = (designer: Ref) => {
       uploadImgRule,
       uploadImgsRule,
       dictSelectRule,
-      currencySelectRule
+      restfulSelectRule
     ]
     components.forEach((component) => {
       // 插入组件规则
@@ -59,8 +87,8 @@ export const useFormCreateDesigner = (designer: Ref) => {
     })
   }
 
-  const userSelectRule = useUserSelectRule()
-  const deptSelectRule = useDeptSelectRule()
+  const userSelectRule = useSelectRule({ name: 'UserSelect', label: '用户选择器' })
+  const deptSelectRule = useSelectRule({ name: 'DeptSelect', label: '部门选择器' })
   /**
    * 构建系统字段菜单
    */
diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts
index da74a7bf..9fc716c3 100644
--- a/src/plugins/formCreate/index.ts
+++ b/src/plugins/formCreate/index.ts
@@ -35,7 +35,9 @@ const DeptSelect = useCurrencySelect({
   valueField: 'id',
   restful: '/system/dept/simple-list'
 })
-
+const RestfulSelect = useCurrencySelect({
+  name: 'RestfulSelect'
+})
 const components = [
   ElAside,
   ElPopconfirm,
@@ -55,6 +57,7 @@ const components = [
   DictSelect,
   UserSelect,
   DeptSelect,
+  RestfulSelect,
   Editor
 ]
 

From b4d85b782e0c8f3a79202cfba26712e78bdca075 Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Wed, 1 May 2024 19:53:35 +0800
Subject: [PATCH 4/7] =?UTF-8?q?form-create:=20=E5=B0=81=E8=A3=85=E9=80=9A?=
 =?UTF-8?q?=E7=94=A8=E9=80=89=E6=8B=A9=E5=99=A8=20hook?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/DictSelect/index.ts            |   3 -
 src/components/DictSelect/src/DictSelect.vue  |  46 --------
 .../src/components/useCurrencySelect.tsx      | 103 ++++++++++++++++--
 .../FormCreate/src/config/selectRule.ts       |  11 ++
 .../src/config/useDictSelectRule.ts           |   2 +-
 src/components/FormCreate/src/type/index.ts   |   5 +-
 .../FormCreate/src/useFormCreateDesigner.ts   |   2 +
 src/plugins/formCreate/index.ts               |   5 +-
 8 files changed, 114 insertions(+), 63 deletions(-)
 delete mode 100644 src/components/DictSelect/index.ts
 delete mode 100644 src/components/DictSelect/src/DictSelect.vue

diff --git a/src/components/DictSelect/index.ts b/src/components/DictSelect/index.ts
deleted file mode 100644
index 164035fd..00000000
--- a/src/components/DictSelect/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import DictSelect from './src/DictSelect.vue'
-
-export { DictSelect }
diff --git a/src/components/DictSelect/src/DictSelect.vue b/src/components/DictSelect/src/DictSelect.vue
deleted file mode 100644
index 2d59e23c..00000000
--- a/src/components/DictSelect/src/DictSelect.vue
+++ /dev/null
@@ -1,46 +0,0 @@
-<!-- 数据字典 Select 选择器 -->
-<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 {
-  dictType: string // 字典类型
-  valueType: string // 字典值类型
-}
-
-withDefaults(defineProps<Props>(), {
-  dictType: '',
-  valueType: 'str'
-})
-const attrs = useAttrs()
-defineOptions({ name: 'DictSelect' })
-</script>
diff --git a/src/components/FormCreate/src/components/useCurrencySelect.tsx b/src/components/FormCreate/src/components/useCurrencySelect.tsx
index ae9636a3..8da0b696 100644
--- a/src/components/FormCreate/src/components/useCurrencySelect.tsx
+++ b/src/components/FormCreate/src/components/useCurrencySelect.tsx
@@ -1,25 +1,41 @@
 import request from '@/config/axios'
 import { isEmpty } from '@/utils/is'
 import { CurrencySelectProps } from '@/components/FormCreate/src/type'
+import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
 
 export const useCurrencySelect = (option: CurrencySelectProps) => {
   return defineComponent({
     name: option.name,
     props: {
-      // 字典类型
+      // 选项标签
       labelField: {
         type: String,
-        default: () => option.labelField ?? ''
+        default: () => option.labelField ?? 'label'
       },
-      // 字典值类型
+      // 选项的值
       valueField: {
         type: String,
-        default: () => option.valueField ?? ''
+        default: () => option.valueField ?? 'value'
       },
       // api 接口
       restful: {
         type: String,
         default: () => option.restful ?? ''
+      },
+      // 字典类型
+      dictType: {
+        type: String,
+        default: ''
+      },
+      // 字典值类型 'str' | 'int' | 'bool'
+      dictValueType: {
+        type: String,
+        default: 'str'
+      },
+      // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio
+      selectType: {
+        type: String,
+        default: 'select'
       }
     },
     setup(props) {
@@ -27,6 +43,12 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
       const options = ref<any[]>([]) // 下拉数据
       const getOptions = async () => {
         options.value = []
+        // 字典选择器
+        if (option.isDict) {
+          options.value = getDictOptions()
+          return
+        }
+        // 接口选择器
         if (isEmpty(props.restful)) {
           return
         }
@@ -41,17 +63,78 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
         }
         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 []
+        }
+      }
       onMounted(async () => {
         await getOptions()
       })
+      const buildSelect = () => {
+        return (
+          <>
+            <el-select class="w-1/1" {...attrs}>
+              {options.value.map((item, index) => (
+                <el-option key={index} label={item.label} value={item.value} />
+              ))}
+            </el-select>
+          </>
+        )
+      }
+      const buildCheckbox = () => {
+        if (isEmpty(options.value)) {
+          options.value = [
+            { label: '选项1', value: '选项1' },
+            { label: '选项2', value: '选项2' }
+          ]
+        }
+        return (
+          <>
+            <el-checkbox-group class="w-1/1" {...attrs}>
+              {options.value.map((item, index) => (
+                <el-checkbox key={index} label={item.label} value={item.value} />
+              ))}
+            </el-checkbox-group>
+          </>
+        )
+      }
+      const buildRadio = () => {
+        if (isEmpty(options.value)) {
+          options.value = [
+            { label: '选项1', value: '选项1' },
+            { label: '选项2', value: '选项2' }
+          ]
+        }
+        return (
+          <>
+            <el-radio-group class="w-1/1" {...attrs}>
+              {options.value.map((item, index) => (
+                <el-radio key={index} value={item.value}>
+                  {item.label}
+                </el-radio>
+              ))}
+            </el-radio-group>
+          </>
+        )
+      }
       return () => (
         <>
-          <el-select className="w-1/1" {...attrs}>
-            {options.value.map((item, index) => (
-              <el-option key={index} label={item.label} value={item.value} />
-            ))}
-          </el-select>
+          {props.selectType === 'select'
+            ? buildSelect()
+            : props.selectType === 'radio'
+              ? buildRadio()
+              : props.selectType === 'checkbox'
+                ? buildCheckbox()
+                : buildSelect()}
         </>
       )
     }
diff --git a/src/components/FormCreate/src/config/selectRule.ts b/src/components/FormCreate/src/config/selectRule.ts
index 0974139e..3137aac7 100644
--- a/src/components/FormCreate/src/config/selectRule.ts
+++ b/src/components/FormCreate/src/config/selectRule.ts
@@ -1,4 +1,15 @@
 const selectRule = [
+  {
+    type: 'select',
+    field: 'selectType',
+    title: '选择器类型',
+    value: 'select',
+    options: [
+      { label: '下拉框', value: 'select' },
+      { label: '单选框', value: 'radio' },
+      { label: '多选框', value: 'checkbox' }
+    ]
+  },
   { type: 'switch', field: 'multiple', title: '是否多选' },
   {
     type: 'switch',
diff --git a/src/components/FormCreate/src/config/useDictSelectRule.ts b/src/components/FormCreate/src/config/useDictSelectRule.ts
index 3350cb8e..d2a9c467 100644
--- a/src/components/FormCreate/src/config/useDictSelectRule.ts
+++ b/src/components/FormCreate/src/config/useDictSelectRule.ts
@@ -46,7 +46,7 @@ export const useDictSelectRule = () => {
         },
         {
           type: 'select',
-          field: 'valueType',
+          field: 'dictValueType',
           title: '字典值类型',
           value: 'str',
           options: [
diff --git a/src/components/FormCreate/src/type/index.ts b/src/components/FormCreate/src/type/index.ts
index 8500d776..ac248f11 100644
--- a/src/components/FormCreate/src/type/index.ts
+++ b/src/components/FormCreate/src/type/index.ts
@@ -35,9 +35,10 @@ export interface DragRule {
 // 通用下拉组件 Props 类型
 export interface CurrencySelectProps {
   name: string // 组件名称
-  labelField?: string // 字典类型
-  valueField?: string // 字典值类型
+  labelField?: string // 选项标签
+  valueField?: string // 选项的值
   restful?: string // api 接口
+  isDict?: boolean // 是否字典选择器
 }
 
 // 选择组件规则配置类型
diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts
index 7c4e48b8..69772bd5 100644
--- a/src/components/FormCreate/src/useFormCreateDesigner.ts
+++ b/src/components/FormCreate/src/useFormCreateDesigner.ts
@@ -67,6 +67,8 @@ export const useFormCreateDesigner = async (designer: Ref) => {
     designer.value?.removeMenuItem('fc-editor')
     // 移除自带的下拉选择器组件,使用 currencySelectRule 替代
     designer.value?.removeMenuItem('select')
+    designer.value?.removeMenuItem('radio')
+    designer.value?.removeMenuItem('checkbox')
     const components = [
       editorRule,
       uploadFileRule,
diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts
index 9fc716c3..3a647e11 100644
--- a/src/plugins/formCreate/index.ts
+++ b/src/plugins/formCreate/index.ts
@@ -19,7 +19,6 @@ 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 { useCurrencySelect } from '@/components/FormCreate'
 import { Editor } from '@/components/Editor'
 
@@ -38,6 +37,10 @@ const DeptSelect = useCurrencySelect({
 const RestfulSelect = useCurrencySelect({
   name: 'RestfulSelect'
 })
+const DictSelect = useCurrencySelect({
+  name: 'DictSelect',
+  isDict: true
+})
 const components = [
   ElAside,
   ElPopconfirm,

From c77526e8c5e5156a705d3f2f92ce9b01acafc678 Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Wed, 1 May 2024 20:42:53 +0800
Subject: [PATCH 5/7] =?UTF-8?q?form-create:=20=E5=B0=81=E8=A3=85=E9=80=9A?=
 =?UTF-8?q?=E7=94=A8=E9=80=89=E6=8B=A9=E5=99=A8=20hook?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/components/useCurrencySelect.tsx      | 46 ++++++-------
 .../FormCreate/src/useFormCreateDesigner.ts   | 65 +++++++++----------
 2 files changed, 52 insertions(+), 59 deletions(-)

diff --git a/src/components/FormCreate/src/components/useCurrencySelect.tsx b/src/components/FormCreate/src/components/useCurrencySelect.tsx
index 8da0b696..82d708c6 100644
--- a/src/components/FormCreate/src/components/useCurrencySelect.tsx
+++ b/src/components/FormCreate/src/components/useCurrencySelect.tsx
@@ -37,6 +37,11 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
         type: String,
         default: 'select'
       }
+      // // 是否多选
+      // multiple: {
+      //   type: Boolean,
+      //   default: false
+      // }
     },
     setup(props) {
       const attrs = useAttrs()
@@ -79,15 +84,14 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
       onMounted(async () => {
         await getOptions()
       })
+      // TODO puhui999: 单下拉多选的时候页面会卡住报错,下次解决
       const buildSelect = () => {
         return (
-          <>
-            <el-select class="w-1/1" {...attrs}>
-              {options.value.map((item, index) => (
-                <el-option key={index} label={item.label} value={item.value} />
-              ))}
-            </el-select>
-          </>
+          <el-select class="w-1/1" {...attrs}>
+            {options.value.map((item, index) => (
+              <el-option key={index} label={item.label} value={item.value} />
+            ))}
+          </el-select>
         )
       }
       const buildCheckbox = () => {
@@ -98,13 +102,11 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
           ]
         }
         return (
-          <>
-            <el-checkbox-group class="w-1/1" {...attrs}>
-              {options.value.map((item, index) => (
-                <el-checkbox key={index} label={item.label} value={item.value} />
-              ))}
-            </el-checkbox-group>
-          </>
+          <el-checkbox-group class="w-1/1" {...attrs}>
+            {options.value.map((item, index) => (
+              <el-checkbox key={index} label={item.label} value={item.value} />
+            ))}
+          </el-checkbox-group>
         )
       }
       const buildRadio = () => {
@@ -115,15 +117,13 @@ export const useCurrencySelect = (option: CurrencySelectProps) => {
           ]
         }
         return (
-          <>
-            <el-radio-group class="w-1/1" {...attrs}>
-              {options.value.map((item, index) => (
-                <el-radio key={index} value={item.value}>
-                  {item.label}
-                </el-radio>
-              ))}
-            </el-radio-group>
-          </>
+          <el-radio-group class="w-1/1" {...attrs}>
+            {options.value.map((item, index) => (
+              <el-radio key={index} value={item.value}>
+                {item.label}
+              </el-radio>
+            ))}
+          </el-radio-group>
         )
       }
       return () => (
diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts
index 69772bd5..3875b72c 100644
--- a/src/components/FormCreate/src/useFormCreateDesigner.ts
+++ b/src/components/FormCreate/src/useFormCreateDesigner.ts
@@ -25,6 +25,30 @@ export const useFormCreateDesigner = async (designer: Ref) => {
   const uploadFileRule = useUploadFileRule()
   const uploadImgRule = useUploadImgRule()
   const uploadImgsRule = useUploadImgsRule()
+
+  /**
+   * 构建表单组件
+   */
+  const buildFormComponents = () => {
+    // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
+    designer.value?.removeMenuItem('upload')
+    // 移除自带的富文本组件规则,使用 editorRule 替代
+    designer.value?.removeMenuItem('fc-editor')
+    const components = [editorRule, uploadFileRule, uploadImgRule, uploadImgsRule]
+    components.forEach((component) => {
+      // 插入组件规则
+      designer.value?.addComponent(component)
+      // 插入拖拽按钮到 `main` 分类下
+      designer.value?.appendMenuItem('main', {
+        icon: component.icon,
+        name: component.name,
+        label: component.label
+      })
+    })
+  }
+
+  const userSelectRule = useSelectRule({ name: 'UserSelect', label: '用户选择器' })
+  const deptSelectRule = useSelectRule({ name: 'DeptSelect', label: '部门选择器' })
   const dictSelectRule = useDictSelectRule()
   const restfulSelectRule = useSelectRule({
     name: 'RestfulSelect',
@@ -56,46 +80,15 @@ export const useFormCreateDesigner = async (designer: Ref) => {
       }
     ]
   })
-
-  /**
-   * 构建表单组件
-   */
-  const buildFormComponents = () => {
-    // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
-    designer.value?.removeMenuItem('upload')
-    // 移除自带的富文本组件规则,使用 editorRule 替代
-    designer.value?.removeMenuItem('fc-editor')
-    // 移除自带的下拉选择器组件,使用 currencySelectRule 替代
-    designer.value?.removeMenuItem('select')
-    designer.value?.removeMenuItem('radio')
-    designer.value?.removeMenuItem('checkbox')
-    const components = [
-      editorRule,
-      uploadFileRule,
-      uploadImgRule,
-      uploadImgsRule,
-      dictSelectRule,
-      restfulSelectRule
-    ]
-    components.forEach((component) => {
-      // 插入组件规则
-      designer.value?.addComponent(component)
-      // 插入拖拽按钮到 `main` 分类下
-      designer.value?.appendMenuItem('main', {
-        icon: component.icon,
-        name: component.name,
-        label: component.label
-      })
-    })
-  }
-
-  const userSelectRule = useSelectRule({ name: 'UserSelect', label: '用户选择器' })
-  const deptSelectRule = useSelectRule({ name: 'DeptSelect', label: '部门选择器' })
   /**
    * 构建系统字段菜单
    */
   const buildSystemMenu = () => {
-    const components = [userSelectRule, deptSelectRule]
+    // 移除自带的下拉选择器组件,使用 currencySelectRule 替代
+    designer.value?.removeMenuItem('select')
+    designer.value?.removeMenuItem('radio')
+    designer.value?.removeMenuItem('checkbox')
+    const components = [userSelectRule, deptSelectRule, dictSelectRule, restfulSelectRule]
     const menu: Menu = {
       name: 'system',
       title: '系统字段',

From 7c158af3cc572c7b0b40bee178595660de215ae2 Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Mon, 6 May 2024 01:24:25 +0800
Subject: [PATCH 6/7] =?UTF-8?q?form-create:=20=E5=AD=97=E5=85=B8=E9=80=89?=
 =?UTF-8?q?=E6=8B=A9=E5=99=A8=E5=88=86=E7=A6=BB=EF=BC=8C=E9=87=8D=E6=96=B0?=
 =?UTF-8?q?=E5=B0=81=E8=A3=85=20api=20=E9=80=89=E6=8B=A9=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/FormCreate/index.ts            |  4 +-
 .../FormCreate/src/components/DictSelect.vue  | 59 ++++++++++++++
 ...useCurrencySelect.tsx => useApiSelect.tsx} | 81 ++++++++++---------
 .../FormCreate/src/config/selectRule.ts       | 67 ++++++++++++++-
 .../src/config/useDictSelectRule.ts           |  2 +-
 .../FormCreate/src/config/useSelectRule.ts    |  2 +-
 src/components/FormCreate/src/type/index.ts   | 12 +--
 .../FormCreate/src/useFormCreateDesigner.ts   | 35 ++------
 src/plugins/formCreate/index.ts               | 22 +++--
 src/utils/index.ts                            | 14 ++++
 10 files changed, 206 insertions(+), 92 deletions(-)
 create mode 100644 src/components/FormCreate/src/components/DictSelect.vue
 rename src/components/FormCreate/src/components/{useCurrencySelect.tsx => useApiSelect.tsx} (67%)

diff --git a/src/components/FormCreate/index.ts b/src/components/FormCreate/index.ts
index 9da34889..9d32778b 100644
--- a/src/components/FormCreate/index.ts
+++ b/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 }
diff --git a/src/components/FormCreate/src/components/DictSelect.vue b/src/components/FormCreate/src/components/DictSelect.vue
new file mode 100644
index 00000000..204746d1
--- /dev/null
+++ b/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>
diff --git a/src/components/FormCreate/src/components/useCurrencySelect.tsx b/src/components/FormCreate/src/components/useApiSelect.tsx
similarity index 67%
rename from src/components/FormCreate/src/components/useCurrencySelect.tsx
rename to src/components/FormCreate/src/components/useApiSelect.tsx
index 82d708c6..54c0a33b 100644
--- a/src/components/FormCreate/src/components/useCurrencySelect.tsx
+++ b/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: 'GET'
+      },
+      // 请求参数
+      data: {
         type: String,
         default: ''
       },
-      // 字典值类型 'str' | 'int' | 'bool'
-      dictValueType: {
-        type: String,
-        default: 'str'
-      },
       // 选择器类型,下拉框 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) => (
diff --git a/src/components/FormCreate/src/config/selectRule.ts b/src/components/FormCreate/src/config/selectRule.ts
index 3137aac7..281d3739 100644
--- a/src/components/FormCreate/src/config/selectRule.ts
+++ b/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 }
diff --git a/src/components/FormCreate/src/config/useDictSelectRule.ts b/src/components/FormCreate/src/config/useDictSelectRule.ts
index d2a9c467..d158842c 100644
--- a/src/components/FormCreate/src/config/useDictSelectRule.ts
+++ b/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
diff --git a/src/components/FormCreate/src/config/useSelectRule.ts b/src/components/FormCreate/src/config/useSelectRule.ts
index fd23ac3a..93c6e8d5 100644
--- a/src/components/FormCreate/src/config/useSelectRule.ts
+++ b/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'
 
 /**
diff --git a/src/components/FormCreate/src/type/index.ts b/src/components/FormCreate/src/type/index.ts
index ac248f11..07e011cf 100644
--- a/src/components/FormCreate/src/type/index.ts
+++ b/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[] // 组件规则
 }
diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts
index 3875b72c..266739c7 100644
--- a/src/components/FormCreate/src/useFormCreateDesigner.ts
+++ b/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: '系统字段',
diff --git a/src/plugins/formCreate/index.ts b/src/plugins/formCreate/index.ts
index 3a647e11..adc12136 100644
--- a/src/plugins/formCreate/index.ts
+++ b/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
 ]
 
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 274ab7ca..5b29e41a 100644
--- a/src/utils/index.ts
+++ b/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 ''
+  }
+}

From 9f704fb8ffad0592aadda82b69b2aab37f98c79c Mon Sep 17 00:00:00 2001
From: puhui999 <puhui999@163.com>
Date: Mon, 6 May 2024 02:12:09 +0800
Subject: [PATCH 7/7] =?UTF-8?q?form-create:=20=E5=AD=97=E4=BD=93=E5=9B=BE?=
 =?UTF-8?q?=E6=A0=87=E6=89=A9=E5=B1=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/config/useDictSelectRule.ts           |   2 +-
 .../FormCreate/src/config/useSelectRule.ts    |   2 +-
 src/components/FormCreate/src/type/index.ts   |   1 +
 .../FormCreate/src/useFormCreateDesigner.ts   |  13 +++++++++--
 src/styles/FormCreate/fonts/fontello.woff     | Bin 0 -> 4252 bytes
 src/styles/FormCreate/index.scss              |  22 ++++++++++++++++++
 src/styles/index.scss                         |   1 +
 7 files changed, 37 insertions(+), 4 deletions(-)
 create mode 100644 src/styles/FormCreate/fonts/fontello.woff
 create mode 100644 src/styles/FormCreate/index.scss

diff --git a/src/components/FormCreate/src/config/useDictSelectRule.ts b/src/components/FormCreate/src/config/useDictSelectRule.ts
index d158842c..3db630bc 100644
--- a/src/components/FormCreate/src/config/useDictSelectRule.ts
+++ b/src/components/FormCreate/src/config/useDictSelectRule.ts
@@ -22,7 +22,7 @@ export const useDictSelectRule = () => {
       })) ?? []
   })
   return {
-    icon: 'icon-select',
+    icon: 'icon-doc-text',
     label,
     name,
     rule() {
diff --git a/src/components/FormCreate/src/config/useSelectRule.ts b/src/components/FormCreate/src/config/useSelectRule.ts
index 93c6e8d5..732c5269 100644
--- a/src/components/FormCreate/src/config/useSelectRule.ts
+++ b/src/components/FormCreate/src/config/useSelectRule.ts
@@ -11,7 +11,7 @@ export const useSelectRule = (option: SelectRuleOption) => {
   const label = option.label
   const name = option.name
   return {
-    icon: 'icon-select',
+    icon: option.icon,
     label,
     name,
     rule() {
diff --git a/src/components/FormCreate/src/type/index.ts b/src/components/FormCreate/src/type/index.ts
index 07e011cf..42dccc76 100644
--- a/src/components/FormCreate/src/type/index.ts
+++ b/src/components/FormCreate/src/type/index.ts
@@ -45,5 +45,6 @@ export interface ApiSelectProps {
 export interface SelectRuleOption {
   label: string // label 名称
   name: string // 组件名称
+  icon: string // 组件图标
   props?: any[] // 组件规则
 }
diff --git a/src/components/FormCreate/src/useFormCreateDesigner.ts b/src/components/FormCreate/src/useFormCreateDesigner.ts
index 266739c7..69c8f314 100644
--- a/src/components/FormCreate/src/useFormCreateDesigner.ts
+++ b/src/components/FormCreate/src/useFormCreateDesigner.ts
@@ -48,12 +48,21 @@ export const useFormCreateDesigner = async (designer: Ref) => {
     })
   }
 
-  const userSelectRule = useSelectRule({ name: 'UserSelect', label: '用户选择器' })
-  const deptSelectRule = useSelectRule({ name: 'DeptSelect', label: '部门选择器' })
+  const userSelectRule = useSelectRule({
+    name: 'UserSelect',
+    label: '用户选择器',
+    icon: 'icon-user-o'
+  })
+  const deptSelectRule = useSelectRule({
+    name: 'DeptSelect',
+    label: '部门选择器',
+    icon: 'icon-address-card-o'
+  })
   const dictSelectRule = useDictSelectRule()
   const apiSelectRule0 = useSelectRule({
     name: 'ApiSelect',
     label: '接口选择器',
+    icon: 'icon-server',
     props: [...apiSelectRule]
   })
 
diff --git a/src/styles/FormCreate/fonts/fontello.woff b/src/styles/FormCreate/fonts/fontello.woff
new file mode 100644
index 0000000000000000000000000000000000000000..1e00f4995b27e72b52d7d0fcf602d0206c3d6c89
GIT binary patch
literal 4252
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

literal 0
HcmV?d00001

diff --git a/src/styles/FormCreate/index.scss b/src/styles/FormCreate/index.scss
new file mode 100644
index 00000000..bb620005
--- /dev/null
+++ b/src/styles/FormCreate/index.scss
@@ -0,0 +1,22 @@
+// 使用字体图标来源 https://fontello.com/
+
+@font-face {
+  font-family: 'fc-icon';
+  src: url('@/styles/FormCreate/fonts/fontello.woff') format('woff');
+}
+
+.icon-doc-text:before {
+  content: '\f0f6';
+}
+
+.icon-server:before {
+  content: '\f233';
+}
+
+.icon-address-card-o:before {
+  content: '\f2bc';
+}
+
+.icon-user-o:before {
+  content: '\f2c0';
+}
diff --git a/src/styles/index.scss b/src/styles/index.scss
index 0952bd07..fbe76f23 100644
--- a/src/styles/index.scss
+++ b/src/styles/index.scss
@@ -1,4 +1,5 @@
 @import './var.css';
+@import './FormCreate/index.scss';
 @import 'element-plus/theme-chalk/dark/css-vars.css';
 
 .reset-margin [class*='el-icon'] + span {