Browse Source

增加ElFormTabs组件

tongshangming 1 year ago
parent
commit
d39666ff70

+ 1 - 0
.gitignore

@@ -26,6 +26,7 @@ coverage
 .vscode/*
 !.vscode/extensions.json
 !.vscode/settings.json
+!.vscode/vue.code-snippets
 .idea
 *.suo
 *.ntvs*

+ 73 - 0
.vscode/vue.code-snippets

@@ -0,0 +1,73 @@
+{
+  "vue component template": {
+    "prefix": "vcomp",
+    "body": [
+      "<script setup lang=\"ts\">",
+      "interface Props {",
+      "\t${1}",
+      "}",
+      "const props = defineProps<Props>()",
+      "${2}",
+      "</script>",
+      "",
+      "<template>",
+      "\t",
+      "</template>",
+      "",
+      "<style lang=\"scss\" scoped>",
+      "</style>"
+    ]
+  },
+  "vue view template": {
+    "prefix": "vview",
+    "body": [
+      "<script setup lang=\"ts\">",
+      "import type { BasicForm, ICRUD } from '@/types/form'",
+      "import { ${0} } from '@/api/'",
+      "",
+      "const CRUD: ICRUD = {",
+      "\tcreate(data: any) {",
+      "\t\t",
+      "\t},",
+      "\tupdate(data: any) {",
+      "\t\t",
+      "\t},",
+      "\tgetList(data: any) {",
+      "\t\t",
+      "\t},",
+      "\tdelete(data: any) {",
+      "\t\t",
+      "\t}",
+      "}",
+      "const formConfig = reactive<BasicForm>({",
+      "\tformItems: []",
+      "})",
+      "</script>",
+      "",
+      "<template>",
+      "\t<pro-table :crud=\"CRUD\" :formConfig=\"formConfig\">",
+      "\t\t<vxe-column field=\"\" title=\"\"></vxe-column>",
+      "\t</pro-table>",
+      "</template>",
+      "",
+      "<style lang=\"scss\" scoped>",
+      "</style>"
+    ]
+  },
+  "vue api template": {
+    "prefix": "vapi",
+    "body": [
+      "import request from '@/utils/request'",
+      "",
+      "export function get${0}(data?: any) {",
+      "\treturn request.get('/sys/', data)",
+      "}",
+      "export function save${0}(data: any) {",
+      "\treturn request.post('/sys/', data)",
+      "}",
+      "export function delete${0}(data: any) {",
+      "\treturn request.get('/sys/', data)",
+      "}",
+    ]
+  },
+}

+ 1 - 0
src/components.d.ts

@@ -26,6 +26,7 @@ declare module '@vue/runtime-core' {
     ElDict: typeof import('./components/ElDict.vue')['default']
     ElEditor: typeof import('./components/ElEditor.vue')['default']
     ElEmployees: typeof import('./components/ElEmployees.vue')['default']
+    ElFormTabs: typeof import('./components/ElFormTabs.vue')['default']
     EmployeesRoleDialog: typeof import('./components/workflow/dialog/employeesRoleDialog.vue')['default']
     ErrorDialog: typeof import('./components/workflow/dialog/errorDialog.vue')['default']
     Exception: typeof import('./components/Exception.vue')['default']

+ 41 - 0
src/components/ElFormTabs.vue

@@ -0,0 +1,41 @@
+<script setup lang="ts">
+import type { BasicForm, BasicFormItem } from '@/types/form'
+
+interface Props {
+  modelValue: string
+  tabs: Array<{
+    label: string
+    name: string
+    children: Array<BasicFormItem>
+  }>
+  formConfig: BasicForm
+  formData: any
+}
+const props = defineProps<Props>()
+const emits = defineEmits(['update:modelValue'])
+
+const modelValue = computed({
+  get: () => props.modelValue,
+  set: value => emits('update:modelValue', value)
+})
+</script>
+
+<template>
+  <el-tabs v-model="modelValue" style="width: 100%">
+    <el-tab-pane :label="tab.label" :name="tab.name" v-for="tab in tabs" :key="tab.name">
+      <el-row :gutter="20">
+        <el-col :span="item.span || formConfig.span || 12" v-for="(item, index) in tab.children" :key="index">
+          <el-form-item :label="item.label" :rules="item.rules" :prop="item.name">
+            <form-comp :item="item" v-model="formData[item.name]">
+              <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
+                <slot :name="slot.alias" v-bind="slotProps"></slot>
+              </template>
+            </form-comp>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-tab-pane>
+  </el-tabs>
+</template>
+
+<style lang="scss" scoped></style>

+ 1 - 1
src/components/SelIcon.vue

@@ -4,7 +4,7 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 const props = defineProps<{
   dialogVisible: boolean
 }>()
-console.log(121)
+
 const dialogVisible = ref(props.dialogVisible)
 const iconArr = ref<string[]>([])
 const emits = defineEmits(['closeIcon'])

+ 0 - 1
src/components/avatar/cropper.vue

@@ -59,7 +59,6 @@ const options = reactive<Options>({
 })
 
 const beforeUpload = (file: UploadFile) => {
-  console.log(file)
   const { size } = file
   const fileType = file.raw?.type
   const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/x-icon']

+ 0 - 1
src/components/form/AdvancedForm.vue

@@ -19,7 +19,6 @@ const formData = computed(() => {
           res[element.name] !== undefined && element.value !== undefined ? res[element.name] : element.value
       })
     })
-    console.log(res)
     return res
   }
 })

+ 36 - 3
src/components/form/BasicForm.vue

@@ -13,18 +13,51 @@ const formData = computed(() => {
   } else {
     const res = props.formData
     props.formConfig?.formItems.forEach(item => {
-      // 避免修改当前表单项value重置其他表单项的value
-      res[item.name] = res[item.name] !== undefined && item.value !== undefined ? res[item.name] : item.value
+      if (item.type !== 'form-tabs') {
+        // 避免修改当前表单项value重置其他表单项的value
+        res[item.name] = res[item.name] !== undefined && item.value !== undefined ? res[item.name] : item.value
+      } else {
+        item.children?.forEach(tab => {
+          tab.children?.forEach(child => {
+            res[child.name] = res[child.name] !== undefined && child.value !== undefined ? res[child.name] : child.value
+          })
+        })
+      }
     })
     return res
   }
 })
+
+// 构造formTabs插槽
+props.formConfig.formItems.forEach(item => {
+  if (item.type === 'form-tabs') {
+    item.slots = []
+    item.children?.forEach(tab => {
+      tab.children?.forEach((child: any) => {
+        Array.prototype.push.apply(item.slots, child.slots)
+      })
+    })
+  }
+})
 </script>
 
 <template>
   <el-row :gutter="20">
     <el-col :span="item.span || formConfig.span || 12" v-for="(item, index) in formConfig.formItems" :key="index">
-      <el-form-item :label="item.label" :rules="item.rules" :prop="item.name">
+      <el-form-tabs
+        v-if="item.type === 'form-tabs'"
+        v-model="item.value"
+        v-bind="item.props"
+        :tabs="item.children"
+        :formData="formData"
+        :formConfig="formConfig"
+        v-on="item.events || {}"
+      >
+        <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
+          <slot :name="slot.alias" v-bind="slotProps"></slot>
+        </template>
+      </el-form-tabs>
+      <el-form-item :label="item.label" :rules="item.rules" :prop="item.name" v-else>
         <form-comp :item="item" v-model="formData[item.name]">
           <template #[slot.alias]="slotProps" v-for="slot in item.slots" :key="slot.alias">
             <slot :name="slot.alias" v-bind="slotProps"></slot>

+ 1 - 0
src/components/form/FormComp.vue

@@ -101,6 +101,7 @@ const headers = reactive({
       <slot :name="slot.alias" v-bind="slotProps"></slot>
     </template>
   </el-select>
+
   <component
     v-else
     :is="'el-' + item.type"

+ 11 - 1
src/components/form/ProForm.vue

@@ -30,7 +30,17 @@ if (props.formConfig.advanced) {
     item.group.forEach((subItem: any) => Array.prototype.push.apply(formSlots.value, subItem.slots))
   })
 } else {
-  props.formConfig.formItems.forEach((item: any) => Array.prototype.push.apply(formSlots.value, item.slots))
+  props.formConfig.formItems.forEach((item: any) => {
+    if (item.type === 'form-tabs') {
+      item.children.forEach((tab: any) => {
+        tab.children.forEach((child: any) => {
+          Array.prototype.push.apply(formSlots.value, child.slots)
+        })
+      })
+    } else {
+      Array.prototype.push.apply(formSlots.value, item.slots)
+    }
+  })
 }
 
 const submit = async () => {

+ 4 - 2
src/components/index.ts

@@ -1,12 +1,14 @@
 import type { App } from 'vue'
 import ElEditor from './ElEditor.vue'
 import ElDict from './ElDict.vue'
+import ElFormTabs from './ElFormTabs.vue'
 import ImageUpload from './ImageUpload.vue'
 
-function registerCopmponent(app: App): void {
+function registerComponent(app: App): void {
   app.component('ElEditor', ElEditor)
   app.component('ElDict', ElDict)
+  app.component('ElFormTabs', ElFormTabs)
   app.component('ElImageUpload', ImageUpload)
 }
 
-export default registerCopmponent
+export default registerComponent

+ 2 - 2
src/main.ts

@@ -7,7 +7,7 @@ import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
-import registerCopmponent from '@/components/index'
+import registerComponent from '@/components/index'
 
 import { install } from '@icon-park/vue-next/es/all'
 
@@ -106,7 +106,7 @@ app.use(createPinia())
 app.use(router)
 app.use(ElementPlus)
 app.use(useTable)
-registerCopmponent(app)
+registerComponent(app)
 
 for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
   app.component(key, component)

+ 8 - 0
src/router/asyncRouter.ts

@@ -208,6 +208,14 @@ const asyncRouter: RouteRecordRaw[] = [
         meta: {
           title: '高级表单'
         }
+      },
+      {
+        path: '/form/tab',
+        name: 'formTab',
+        component: () => import('@/views/form/Tab.vue'),
+        meta: {
+          title: 'tab表单'
+        }
       }
     ]
   },

+ 172 - 0
src/views/form/Tab.vue

@@ -0,0 +1,172 @@
+<script setup lang="ts">
+import type { BasicForm, ICRUD } from '@/types/form'
+
+const formData = reactive<any>({})
+
+const create = (data: any) => {
+  console.log(data)
+}
+const update = (data: any) => {
+  console.log(data)
+}
+
+const proFormRef = ref<any>()
+const handleSave = () => {
+  console.log(formData)
+  proFormRef.value.submit()
+}
+const formConfig = reactive<BasicForm>({
+  formItems: [
+    {
+      label: '用户名',
+      value: '',
+      name: 'name',
+      type: 'input',
+      rules: [{ required: true, message: '请输入用户名', trigger: 'blur' }]
+    },
+    {
+      type: 'form-tabs',
+      label: '',
+      name: '',
+      value: 'label1',
+      span: 24,
+      children: [
+        {
+          type: 'tab-pane',
+          label: 'tab1',
+          name: 'label1',
+          value: '',
+          children: [
+            {
+              type: 'input',
+              label: '登录名',
+              name: 'loginName',
+              value: 'a',
+              rules: [{ required: true, message: '请输入登录名', trigger: 'blur' }],
+              slots: [
+                {
+                  name: 'prepend',
+                  alias: 'prepend'
+                }
+              ]
+            },
+            {
+              type: 'input',
+              label: '密码',
+              name: 'password',
+              value: ''
+            },
+            {
+              label: '启用',
+              value: '1',
+              name: 'loginFlag',
+              type: 'switch',
+              props: {
+                'active-value': '1',
+                'inactive-value': '0'
+              }
+            }
+          ]
+        },
+        {
+          type: 'tab-pane',
+          label: 'tab2',
+          name: 'label2',
+          value: '',
+          children: [
+            {
+              type: 'input',
+              label: '手机号',
+              name: 'phone',
+              value: ''
+            },
+            {
+              label: '字段名',
+              value: '',
+              name: 'field3',
+              type: 'cascader',
+              props: {
+                options: [
+                  {
+                    value: 'guide',
+                    label: 'Guide',
+                    children: [
+                      {
+                        value: 'disciplines',
+                        label: 'Disciplines',
+                        children: [
+                          {
+                            value: 'consistency',
+                            label: 'Consistency'
+                          },
+                          {
+                            value: 'feedback',
+                            label: 'Feedback'
+                          },
+                          {
+                            value: 'efficiency',
+                            label: 'Efficiency'
+                          },
+                          {
+                            value: 'controllability',
+                            label: 'Controllability'
+                          }
+                        ]
+                      },
+                      {
+                        value: 'navigation',
+                        label: 'Navigation',
+                        children: [
+                          {
+                            value: 'side nav',
+                            label: 'Side Navigation'
+                          },
+                          {
+                            value: 'top nav',
+                            label: 'Top Navigation'
+                          }
+                        ]
+                      }
+                    ]
+                  },
+                  {
+                    value: 'resource',
+                    label: 'Resource',
+                    children: [
+                      {
+                        value: 'axure',
+                        label: 'Axure Components'
+                      },
+                      {
+                        value: 'sketch',
+                        label: 'Sketch Templates'
+                      },
+                      {
+                        value: 'docs',
+                        label: 'Design Documentation'
+                      }
+                    ]
+                  }
+                ]
+              }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+})
+</script>
+
+<template>
+  <div class="h-full bg-white p-16px overflow-auto">
+    <pro-form :formConfig="formConfig" :formData="formData" :create="create" :update="update" ref="proFormRef">
+      <template #prepend>test</template>
+    </pro-form>
+    <div class="text-center">
+      <el-button type="primary" @click="handleSave">保存</el-button>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped></style>