mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-11-04 10:57:08 +08:00 
			
		
		
		
	feat: GiForm 组件替换为 Grid 布局(同步 GiDemo 更新)
This commit is contained in:
		@@ -1,10 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <a-form ref="formRef" :auto-label-width="true" v-bind="options.form" :model="modelValue">
 | 
			
		||||
    <a-row :gutter="14" v-bind="options.row" class="w-full">
 | 
			
		||||
    <a-grid class="w-full" :col-gap="8" v-bind="options.grid" :collapsed="collapsed">
 | 
			
		||||
      <template v-for="(item, index) in columns" :key="item.field">
 | 
			
		||||
        <a-col v-if="!isHide(item.hide)" v-show="colVShow(index)" :span="item.span || 12"
 | 
			
		||||
               v-bind="item.col || item.span ? item.col : options.col">
 | 
			
		||||
          <a-form-item v-bind="item.item" :label="item.label" :field="item.field" :rules="item.rules"
 | 
			
		||||
        <a-grid-item v-if="!isHide(item.hide)" v-show="colVShow(index)" v-bind="item.gridItemProps || props.options.gridItem"
 | 
			
		||||
                     :span="item.span || options.gridItem?.span">
 | 
			
		||||
          <a-form-item v-bind="item.formItemProps" :label="item.label" :field="item.field" :rules="item.rules"
 | 
			
		||||
                       :disabled="isDisabled(item.disabled)">
 | 
			
		||||
            <slot v-if="!['group-title'].includes(item.type || '')" :name="item.field"
 | 
			
		||||
                  v-bind="{ disabled: isDisabled(item.disabled) }">
 | 
			
		||||
@@ -21,9 +21,9 @@
 | 
			
		||||
              <a-alert v-bind="item.props">{{ item.label }}</a-alert>
 | 
			
		||||
            </slot>
 | 
			
		||||
          </a-form-item>
 | 
			
		||||
        </a-col>
 | 
			
		||||
        </a-grid-item>
 | 
			
		||||
      </template>
 | 
			
		||||
      <a-col v-if="!options.btns?.hide" :span="options.btns?.span || 12" v-bind="options.btns?.col">
 | 
			
		||||
      <a-grid-item v-if="!options.btns?.hide" :suffix="options.fold?.enable">
 | 
			
		||||
        <a-space wrap :size="[8, 16]" style="flex-wrap: nowrap">
 | 
			
		||||
          <slot name="suffix">
 | 
			
		||||
            <a-button type="primary" @click="emit('search')">
 | 
			
		||||
@@ -34,7 +34,8 @@
 | 
			
		||||
              <template #icon><icon-refresh /></template>
 | 
			
		||||
              <template #default>重置</template>
 | 
			
		||||
            </a-button>
 | 
			
		||||
            <a-button v-if="options.fold?.enable" type="text" size="mini" @click="collapsed = !collapsed">
 | 
			
		||||
            <a-button v-if="options.fold?.enable" class="gi-form__fold-btn" type="text" size="mini"
 | 
			
		||||
                      @click="collapsed = !collapsed">
 | 
			
		||||
              <template #icon>
 | 
			
		||||
                <icon-up v-if="!collapsed" />
 | 
			
		||||
                <icon-down v-else />
 | 
			
		||||
@@ -43,23 +44,24 @@
 | 
			
		||||
            </a-button>
 | 
			
		||||
          </slot>
 | 
			
		||||
        </a-space>
 | 
			
		||||
      </a-col>
 | 
			
		||||
    </a-row>
 | 
			
		||||
      </a-grid-item>
 | 
			
		||||
    </a-grid>
 | 
			
		||||
  </a-form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { cloneDeep } from 'lodash-es'
 | 
			
		||||
import type { Columns, ColumnsItem, ColumnsItemDisabled, ColumnsItemHide, Options } from './type'
 | 
			
		||||
import DateRangePicker from '@/components/DateRangePicker/index.vue'
 | 
			
		||||
import type { ColumnsItem, ColumnsItemDisabled, ColumnsItemHide, Options } from './type'
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  modelValue: any
 | 
			
		||||
  options: Options
 | 
			
		||||
  columns: Columns
 | 
			
		||||
  options?: Options
 | 
			
		||||
  columns: ColumnsItem[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {})
 | 
			
		||||
const props = withDefaults(defineProps<Props>(), {
 | 
			
		||||
  options: () => ({})
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits<{
 | 
			
		||||
  (e: 'update:modelValue', value: any): void
 | 
			
		||||
@@ -67,7 +69,14 @@ const emit = defineEmits<{
 | 
			
		||||
  (e: 'reset'): void
 | 
			
		||||
}>()
 | 
			
		||||
 | 
			
		||||
const formRef = ref('formRef')
 | 
			
		||||
const options = computed(() => ({
 | 
			
		||||
  grid: { cols: 1 },
 | 
			
		||||
  gridItem: { span: { xs: 2, sm: 1 } },
 | 
			
		||||
  ...props.options
 | 
			
		||||
}
 | 
			
		||||
))
 | 
			
		||||
 | 
			
		||||
const formRef = useTemplateRef('formRef')
 | 
			
		||||
const collapsed = ref(props.options.fold?.defaultCollapsed ?? false)
 | 
			
		||||
const dicData: Record<string, any> = reactive({})
 | 
			
		||||
 | 
			
		||||
@@ -80,12 +89,15 @@ const colVShow = (index: number) => {
 | 
			
		||||
const getComponentBindProps = (item: ColumnsItem) => {
 | 
			
		||||
  const obj: Partial<ColumnsItem['props'] & { placeholder: string }> = {}
 | 
			
		||||
  if (item.type === 'input') {
 | 
			
		||||
    obj.allowClear = true
 | 
			
		||||
    obj.placeholder = `请输入${item.label}`
 | 
			
		||||
  }
 | 
			
		||||
  if (item.type === 'input-password') {
 | 
			
		||||
    obj.allowClear = true
 | 
			
		||||
    obj.placeholder = `请输入${item.label}`
 | 
			
		||||
  }
 | 
			
		||||
  if (item.type === 'input-number') {
 | 
			
		||||
    obj.allowClear = true
 | 
			
		||||
    obj.placeholder = `请输入${item.label}`
 | 
			
		||||
  }
 | 
			
		||||
  if (item.type === 'textarea') {
 | 
			
		||||
@@ -93,14 +105,17 @@ const getComponentBindProps = (item: ColumnsItem) => {
 | 
			
		||||
    obj.maxLength = 200
 | 
			
		||||
  }
 | 
			
		||||
  if (item.type === 'select') {
 | 
			
		||||
    obj.allowClear = true
 | 
			
		||||
    obj.placeholder = `请选择${item.label}`
 | 
			
		||||
    obj.options = dicData[item.field] || item.options
 | 
			
		||||
  }
 | 
			
		||||
  if (item.type === 'cascader') {
 | 
			
		||||
    obj.allowClear = true
 | 
			
		||||
    obj.placeholder = `请选择${item.label}`
 | 
			
		||||
    obj.options = dicData[item.field] || item.options
 | 
			
		||||
  }
 | 
			
		||||
  if (item.type === 'tree-select') {
 | 
			
		||||
    obj.allowClear = true
 | 
			
		||||
    obj.placeholder = `请选择${item.label}`
 | 
			
		||||
    obj.data = dicData[item.field] || item.data
 | 
			
		||||
  }
 | 
			
		||||
@@ -146,6 +161,7 @@ props.columns.forEach((item) => {
 | 
			
		||||
  if (item.request && typeof item.request === 'function' && item?.init) {
 | 
			
		||||
    item.request(props.modelValue).then((res) => {
 | 
			
		||||
      dicData[item.field] = item.resultFormat ? item.resultFormat(res) : res.data
 | 
			
		||||
      // console.log('dicData', dicData)
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
@@ -191,4 +207,7 @@ watch(cloneForm as any, (newVal, oldVal) => {
 | 
			
		||||
:deep(.arco-form-item-layout-inline) {
 | 
			
		||||
  margin-right: 0;
 | 
			
		||||
}
 | 
			
		||||
.gi-form__fold-btn {
 | 
			
		||||
  padding: 0 5px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -71,9 +71,8 @@ export interface ColumnsItem<F = any> {
 | 
			
		||||
  type?: FormType // 类型
 | 
			
		||||
  label?: A.FormItemInstance['label'] // 标签
 | 
			
		||||
  field: A.FormItemInstance['field'] // 字段(必须唯一)
 | 
			
		||||
  span?: number // 栅格占位格数
 | 
			
		||||
  col?: A.ColProps // a-col的props, 响应式布局, 优先级大于span
 | 
			
		||||
  item?: Omit<A.FormItemInstance['$props'], 'label' | 'field'> // a-form-item的props
 | 
			
		||||
  gridItemProps?: A.GridItemProps
 | 
			
		||||
  formItemProps?: Omit<A.FormItemInstance['$props'], 'label' | 'field'> // a-form-item的props
 | 
			
		||||
  props?:
 | 
			
		||||
    & A.InputInstance['$props']
 | 
			
		||||
    & A.InputPasswordInstance['$props']
 | 
			
		||||
@@ -99,6 +98,7 @@ export interface ColumnsItem<F = any> {
 | 
			
		||||
    | A.CheckboxGroupInstance['$props']['options']
 | 
			
		||||
    | A.CascaderInstance['$props']['options']
 | 
			
		||||
  // 下拉树组件的data
 | 
			
		||||
  span?: A.GridItemProps['span']
 | 
			
		||||
  data?: A.TreeSelectInstance['$props']['data']
 | 
			
		||||
  hide?: ColumnsItemHide<F> // 是否隐藏
 | 
			
		||||
  disabled?: ColumnsItemDisabled<F> // 是否禁用
 | 
			
		||||
@@ -109,10 +109,10 @@ export interface ColumnsItem<F = any> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface Options {
 | 
			
		||||
  form: Omit<A.FormInstance['$props'], 'model'>
 | 
			
		||||
  row?: Partial<typeof import('@arco-design/web-vue')['Row']['__defaults']>
 | 
			
		||||
  col?: A.ColProps
 | 
			
		||||
  btns?: { hide?: boolean, span?: number, col?: A.ColProps, searchBtnText?: string }
 | 
			
		||||
  form?: Omit<A.FormInstance['$props'], 'model'>
 | 
			
		||||
  grid?: A.GridProps
 | 
			
		||||
  gridItem?: A.GridItemProps
 | 
			
		||||
  btns?: { hide?: boolean, searchBtnText?: string }
 | 
			
		||||
  fold?: { enable?: boolean, index?: number, defaultCollapsed?: boolean }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,5 +6,4 @@ export * from './modules/useTable'
 | 
			
		||||
export * from './modules/useForm'
 | 
			
		||||
export * from './modules/useDevice'
 | 
			
		||||
export * from './modules/useBreakpoint'
 | 
			
		||||
export * from './modules/useBreakpointIndex'
 | 
			
		||||
export * from './modules/useDownload'
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
import { useBreakpoint } from '@/hooks'
 | 
			
		||||
 | 
			
		||||
type BreakpointMap = {
 | 
			
		||||
  xs: number
 | 
			
		||||
  sm: number
 | 
			
		||||
  md: number
 | 
			
		||||
  lg: number
 | 
			
		||||
  xl: number
 | 
			
		||||
  xxl: number
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useBreakpointIndex(callback: (v: number) => void, breakpointObj?: BreakpointMap) {
 | 
			
		||||
  const { breakpoint } = useBreakpoint()
 | 
			
		||||
 | 
			
		||||
  watch(
 | 
			
		||||
    () => breakpoint.value,
 | 
			
		||||
    (v) => {
 | 
			
		||||
      const def = { xs: 0, sm: 0, md: 0, lg: 1, xl: 1, xxl: 2 }
 | 
			
		||||
      const obj = breakpointObj || def
 | 
			
		||||
      callback(obj[v as keyof typeof obj])
 | 
			
		||||
    },
 | 
			
		||||
    { immediate: true }
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
@@ -33,7 +33,6 @@ const { app_type } = useDict('app_type')
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: {},
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -78,8 +77,8 @@ const columns: Columns = reactive([
 | 
			
		||||
  {
 | 
			
		||||
    label: '应用描述',
 | 
			
		||||
    field: 'appDesc',
 | 
			
		||||
    type: 'textarea',
 | 
			
		||||
  },
 | 
			
		||||
    type: 'textarea'
 | 
			
		||||
  }
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
const { form, resetForm } = useForm({
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@ const formRef = ref<InstanceType<typeof GiForm>>()
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: { size: 'large' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,6 @@ const { width } = useWindowSize()
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: { size: 'large' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,6 @@ const formRef = ref<InstanceType<typeof GiForm>>()
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: { size: 'large' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,6 @@ const formRef = ref<InstanceType<typeof GiForm>>()
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: { size: 'large' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@ const formRef = ref<InstanceType<typeof GiForm>>()
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: { size: 'large' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ const { form, resetForm } = useForm({
 | 
			
		||||
})
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: { size: 'large' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 12, lg: 12, xl: 12, xxl: 12 },
 | 
			
		||||
  grid: { cols: 2 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,6 @@ const formRef = ref<InstanceType<typeof GiForm>>()
 | 
			
		||||
 | 
			
		||||
const options: Options = {
 | 
			
		||||
  form: { size: 'large' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
 | 
			
		||||
  btns: { hide: true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -121,8 +121,7 @@ const {
 | 
			
		||||
 | 
			
		||||
const options: Options = reactive({
 | 
			
		||||
  form: { layout: 'inline' },
 | 
			
		||||
  col: { xs: 24, sm: 24, md: 5, lg: 4, xl: 4, xxl: 4 },
 | 
			
		||||
  btns: { col: { xs: 24, sm: 24, md: 7, lg: 8, xl: 6, xxl: 6 } },
 | 
			
		||||
  grid: { cols: { xs: 1, sm: 1, md: 2, lg: 3, xl: 3, xxl: 3 } },
 | 
			
		||||
  fold: { enable: true, index: 1, defaultCollapsed: true }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@@ -130,33 +129,31 @@ const queryFormColumns: Columns = reactive([
 | 
			
		||||
  {
 | 
			
		||||
    type: 'input',
 | 
			
		||||
    field: 'description',
 | 
			
		||||
    item: {
 | 
			
		||||
    formItemProps: {
 | 
			
		||||
      hideLabel: true
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
      placeholder: '用户名/昵称/描述',
 | 
			
		||||
      allowClear: true
 | 
			
		||||
      placeholder: '用户名/昵称/描述'
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    type: 'select',
 | 
			
		||||
    field: 'status',
 | 
			
		||||
    options: DisEnableStatusList,
 | 
			
		||||
    item: {
 | 
			
		||||
    formItemProps: {
 | 
			
		||||
      hideLabel: true
 | 
			
		||||
    },
 | 
			
		||||
    props: {
 | 
			
		||||
      placeholder: '请选择状态',
 | 
			
		||||
      allowClear: true
 | 
			
		||||
      placeholder: '请选择状态'
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    type: 'range-picker',
 | 
			
		||||
    field: 'createTime',
 | 
			
		||||
    item: {
 | 
			
		||||
    span: { lg: 2, xl: 2, xxl: 1 },
 | 
			
		||||
    formItemProps: {
 | 
			
		||||
      hideLabel: true
 | 
			
		||||
    },
 | 
			
		||||
    col: { xs: 24, sm: 24, md: 10, lg: 9.5, xl: 9, xxl: 8 }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user