Skip to content

IkPageFull

通过一个js文件生成搜索表单、表格、分页、表格字段筛选、新增修改弹窗页面及新UI的标准页面

未命名1686566491.png

props说明

除了以下列举的props,IkPageFull组件支持el-table的所有属性

例:<IkPageFull border stripe highlight-current-row />

javascript
const defaultProps = {
 // iking-global-mainapp@1.1.19.1以上支持 是否启用默认的新增、编辑功能
  isEdit: {
    type: Boolean,
    default: false
  },
  // 左侧标题
  headerTitle: {
    type: String,
    default: ''
  },
  // 是否需要底部边框
  headerBorder: {
    type: Boolean,
    default: false
  },
  // tabs
  tabs: {
    type: Array as PropType<Array<{ label: string; id: string | number; }>>,
    default: () => []
  },
  // 字段名列表
  fields: {
    required: true,
    type: Array as PropType<ISearchForm[]>,
    default: () => []
  },
  // 表格数据
  tableData: {
    type: Array as PropType<Array<any>>,
    default: () => []
  },
  // 初始首次查询
  immediate: {
    type: Boolean,
    default: true
  },
  // 表格数据总条数
  total: {
    type: Number,
    default: 0
  },
  // 查询方法
  search: {
    type: Function,
    default: null
  },
  // 显示表格复选框
  selection: {
    type: Boolean,
    default: false
  },
  // 序号
  serial: {
    type: Boolean,
    default: true
  },
  loading: {
    type: Boolean,
    default: false
  },
  // tab页id,用于保存用户配置
  tabId: {
    type: Number || String,
    default: 0
  },
  // 显示导出按钮
  exportBtn: {
    type: Boolean,
    default: false
  },
  // 显示刷新按钮
  refreshBtn: {
    type: Boolean,
    default: true
  },
  // 显示设置按钮
  settingBtn: {
    type: Boolean,
    default: true
  },
  // 选中项数量
  chooseLen: {
    type: Number,
    default: 0
  },
  // 当前页
  current: {
    type: Number,
    default: 1
  },
  // 是否默认展开搜索条件
  defaultShow: {
    type: Boolean,
    default: false
  },
  // 新增弹框的尺寸   sl l xl
  dialogSize: {
    type: String,
    default: 'l'
  },
  // 弹框标题(不传的话,默认为“新增”/“修改”)
  title: {
    type: String,
    default: ''
  },
  // 按钮权限
  role: {
    type: String,
    default: ''
  },
  // formKey
  formKey: {
    type: Object,
    default: () => {
      return {}
    }
  },
  // 新增修改弹框的默认属性
  dialogProp: {
    type: Object as PropType<any>,
    default: () => { }
  }
}

fields<ISearchForm>字段说明

iking-global-mainapp >= V1.0.15 为新版

1.0.15版本以上对字段做了修改,更容易配置;兼容旧版本写法,可以放心升级

javascript
export interface ISearchForm {
  // iking-global-mainapp@1.1.19.1以上支持 是否做为新增、编辑的字段
  isEdit?: boolean
  /**
   * @description: 支持element组件的所有属性
   * @return {
   * 默认类型为el-input,如果是el-input可以不传type字段
   * 例:element组件支持的所有属性都可以写在formProp这个对象中
   * 例如描述el-input:
   * formProp: { 
   *    type: 'el-input', 
   *    clearable: true, 
   *    maxlength: 15, 
   *    placeholder: '请输入内容'
   *  }
   * }
   */
  formProp?: {
    [key: string]: any,
    // 使用el-radio-group或el-checkbox-group时,使用button形式
    radioType?: 'button'
  }
  // 必填校验
  rules?: Array<{
    required: Boolean
    message: string
    trigger: any
  }>
  // 字段名称
  label: string
  // 绑定的字段名
  key: string
  // 特殊组件左侧select的key值(一个组件由左侧下拉,右侧input框组成的情况)或是用于选人组件的input值回显
  // 展现如下图
  selectKey?: string
  // 人员选择组件用户存放/回显选中的人
  handList?: Array<any>
  // 渲染类型 当为特殊组件(可输入可下拉)时 type应当传递'select-input'
  type?: string // element组件名称;如:el-input, el-select等等
  // 默认值
  value?: any
  // 是否做为查询项 默认false
  search?: boolean
  // 是否作为新增修改的字段 默认false
  isEdit?: boolean
  // 在表格列中 是否默认显示
  show?: boolean
  // 搜索条件插槽
  formSlot?: string
  // 表格插槽
  tableSlot?: string
  // 下拉选项 select  treeselect radio checkbox等
  options?: Array<{
    label: string
    value: any
  }>
  emptyText?: string
  // 占得列数 默认 1
  col?: number
  // 任意字段
  [key: string]: any

  // ************************************以下参数是为了兼容之前版本**************************************
  placeholder?: string
  // 最大输入长度
  maxlength?: number
  // 最大输入长度
  minlength?: number
  // 最多多少行, 超出后显示省略号
  maxline?: number
  // 输入状态 默认 false
  disabled?: boolean

  // number checkbox
  // 最大值
  max?: number
  // 最小值
  min?: number

  // ===========================input-number=============================================
  // 步长 默认 1
  step?: number
  // 数值精度 小数位数 默认0
  precision?: number
  // ===========================input-number end=============================================

  // ===========================select  treeselect=============================================
  // 多选  默认false
  multiple?: boolean
  // 过滤  默认true
  filterable?: boolean
  // ===========================select  treeselect end=============================================

  // ==============================================treeselect==================================
  // 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
  nodeKey?: string
  // 配置选项
  props?: {
    // 指定节点标签为节点对象的某个属性值
    label?: string | Function
    // 指定子树为节点对象的某个属性值
    children?: string
    // 指定节点选择框是否禁用为节点对象的某个属性值
    disabled?: string | Function
    // 自定义节点类名
    class?: string | Function
  }
  // 是否高亮当前选中节点,默认值是 true
  highlightCurrent?: boolean
  // 是否默认展开所有节点 默认值是 false
  defaultExpandAll?: boolean
  // 是否在点击节点的时候展开或者收缩节点 如果为 false,则只有点箭头图标的时候才会展开或者收缩节点 默认值是 false
  expandOnClickNode?: boolean
  // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。
  checkOnClickNode?: boolean
  // 默认展开的节点的 key 的数组
  defaultExpandedKeys?: Array<string>
  // 节点是否可被选择 默认值是 true
  showCheckbox?: boolean
  // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 true
  checkStrictly?: boolean
  // 默认勾选的节点的 key 的数组
  defaultCheckedKeys?: Array<string>
  // 当前选中的节点
  currentNodeKey?: string | number
  // 是否每次只打开一个同级树节点展开 默认false
  accordion?: boolean
  // ==============================================treeselect  end==================================
  // ============================================= 日期选择 Date Picker================================
  // 文本框可输入
  editable?: boolean
  // 范围选择时开始日期的占位内容
  startPlaceholder?: string
  // 范围选择时结束日期的占位内容
  endplaceholder?: string
  // 显示类型
  dateType?: EDateType
  // 显示在输入框中的格式
  format?: string
  // DatePicker 下拉框的类名
  popperclass?: string
  // 选择范围时的分隔符
  rangeSeparator?: string
  // 可选,选择器打开时默认显示的时间
  defaultValue?: Date | [Date, Date]
  // 范围选择时选中日期所使用的当日内具体时刻
  defaultTime?: Date | [Date, Date]
  // 可选,绑定值的格式。 不指定则绑定值为 Date 对象
  valueFormat?: string
  // 一个用来判断该日期是否被禁用的函数,接受一个 Date 对象作为参数。 应该返回一个 Boolean 值
  disabledDate?: (time: Date) => boolean
  typeOption?: Object
  _list?: Array<any>
  // ============================================= 日期选择 Date Picker  end================================

  // ============================================= 表格中的配置================================
  // 支持el-table所有属性配置  直接在组件中使用即可  例:<IkPageFull  stripe  border />
  // 表格 列宽度
  width?: number
  // 表格 列最小宽度
  minWidth?: number
  // 表格内容位置
  align?: keyof typeof EAlign
}
javascript
// 组件类型
export enum EFormType {
  // 输入框
  'input' = 'input',
  // text area
  'area' = 'area',
  // 数字输入框
  'number' = 'number',
  // 下拉
  'select' = 'select',
  // 日期选择
  'date' = 'date',
  // 日期时间选择
  'datetime' = 'datetime',
  // 多选checkbox
  'checkbox' = 'checkbox',
  // 单选radio
  'radio' = 'radio',
  // 树形下拉
  'treeselect' = 'treeselect',
  'choosePerson' = 'choosePerson' // 选择人员和部门
}
// 日期类型
export enum EDateType {
  // 年
  'year' = 'year',
  // 月
  'month' = 'month',
  // 日
  'date' = 'date',
  // 多个日期
  'dates' = 'dates',
  // 日期时间
  'datetime' = 'datetime',
  // 周
  'week' = 'week',
  // 日期时间区间
  'datetimerange' = 'datetimerange',
  // 日期区间
  'daterange' = 'daterange',
  // 月区间
  'monthrange' = 'monthrange'
}

export interface ISearchForm {
  // 字段
  key: string
  // 字段名称
  label: string
  // 渲染类型
  type?: keyof typeof EFormType // element组件名称;如:el-input, el-select等等
  // 默认值
  value?: any
  // 是否做为查询项 默认false
  search?: boolean
  // 在表格列中 是否默认显示
  show?: boolean
  // 搜索条件插槽
  formSlot?: string
  // 表格插槽
  tableSlot?: string
  // 下拉选项 select  treeselect radio checkbox等
  options?: Array<{
    label: string
    value: any
  }>
  emptyText?: string
  // 占得列数 默认 1
  col?: number

  placeholder?: string
  // 最大输入长度
  maxlength?: number
  // 最大输入长度
  minlength?: number
  // 最多多少行, 超出后显示省略号
  maxline?: number
  // 输入状态 默认 false
  disabled?: boolean

  // number checkbox
  // 最大值
  max?: number
  // 最小值
  min?: number

  // ===========================input-number=============================================
  // 步长 默认 1
  step?: number
  // 数值精度 小数位数 默认0
  precision?: number
  // ===========================input-number end=============================================

  // ===========================select  treeselect=============================================
  // 多选  默认false
  multiple?: boolean
  // 过滤  默认true
  filterable?: boolean
  // ===========================select  treeselect end=============================================

  // ==============================================treeselect==================================
  // 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
  nodeKey?: string
  // 配置选项
  props?: {
    // 指定节点标签为节点对象的某个属性值
    label?: string | Function
    // 指定子树为节点对象的某个属性值
    children?: string
    // 指定节点选择框是否禁用为节点对象的某个属性值
    disabled?: string | Function
    // 自定义节点类名
    class?: string | Function
  }
  // 是否高亮当前选中节点,默认值是 true
  highlightCurrent?: boolean
  // 是否默认展开所有节点 默认值是 false
  defaultExpandAll?: boolean
  // 是否在点击节点的时候展开或者收缩节点 如果为 false,则只有点箭头图标的时候才会展开或者收缩节点 默认值是 false
  expandOnClickNode?: boolean
  // 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点。
  checkOnClickNode?: boolean
  // 默认展开的节点的 key 的数组
  defaultExpandedKeys?: Array<string>
  // 节点是否可被选择 默认值是 true
  showCheckbox?: boolean
  // 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 true
  checkStrictly?: boolean
  // 默认勾选的节点的 key 的数组
  defaultCheckedKeys?: Array<string>
  // 当前选中的节点
  currentNodeKey?: string | number
  // 是否每次只打开一个同级树节点展开 默认false
  accordion?: boolean
  // ==============================================treeselect  end==================================
  // ============================================= 日期选择 Date Picker================================
  // 文本框可输入
  editable?: boolean
  // 范围选择时开始日期的占位内容
  startPlaceholder?: string
  // 范围选择时结束日期的占位内容
  endplaceholder?: string
  // 显示类型
  dateType?: EDateType
  // 显示在输入框中的格式
  format?: string
  // DatePicker 下拉框的类名
  popperclass?: string
  // 选择范围时的分隔符
  rangeSeparator?: string
  // 可选,选择器打开时默认显示的时间
  defaultValue?: Date | [Date, Date]
  // 范围选择时选中日期所使用的当日内具体时刻
  defaultTime?: Date | [Date, Date]
  // 可选,绑定值的格式。 不指定则绑定值为 Date 对象
  valueFormat?: string
  // 一个用来判断该日期是否被禁用的函数,接受一个 Date 对象作为参数。 应该返回一个 Boolean 值
  disabledDate?: (time: Date) => boolean
  typeOption?: Object
  _list?: Array<any>
  // ============================================= 日期选择 Date Picker  end================================

  // ============================================= 表格中的配置================================
  // 支持el-table所有属性配置  直接在组件中使用即可  例:<IkPageFull  stripe  border />
  // 表格 列宽度
  width?: number
  // 表格 列最小宽度
  minWidth?: number
  // 表格内容位置
  align?: keyof typeof EAlign

type为select-input 左侧为下拉框+右侧输入框 未命名1686803119.pngtype为choosePerson 点击input框,弹出人员组件。key绑定的值为传给后端的人员数组,selectKey用于input回显逸已选人员姓名,handList用于存放/回显选中的人员数组 未命名1686803119.png

新增/修改功能具体代码示例

默认“新增”是button,支持插槽(#headerRight)。如果使用按钮,则不需要插槽#headerRight,如果采用自定义图标,“新增”调用的方法同修改一样,传参为null即可。

例:未命名1686803119.png

主页面配置

IkPageFull使用时,无需传入is-edit:true,只需要在配置fields时传入。

未命名1686803119.png未命名1686803119.png

fields页面配置

配置fields时,如果需要展示在新增修改页面的字段,则需要设置isEdit:true,然后声明组件类型(例如type: 'el-input')。formProp支持element组件的所有属性,表单校验可添加rules。(下图为输入框,人员选择组件的示例代码) 未命名1686803119.png

solt插槽说明

固定插槽

  • headerLeft:左侧内容,如果props传递了header-title的同时使用了此插槽,header-title将失效
  • headerRight:右侧内容,使用此插槽自定义右侧内容,搜索、刷新、设置按钮不会消失,只能通过props传值隐藏

插槽页面位置如下图:

未命名1686651084(1).png

  • table:表格插槽,使用此插槽时,表格内容将完全有开发自定义。 插槽页面位置如下图:

注意

当使用自定义table插槽时,选中项数据应该由开发自己获取,同时需要通过props将chooseLen传递给组件

未命名1686651592.png

  • selectionRight:选中项右侧插槽,页面默认放置删除(事件名delete)、下载(事件名download)两个按钮,点击会触发相应事件并返回选中数据

插槽页面位置如下图:

未命名1686653117.png

动态插槽

动态插槽

在定义字段列表时,我们可以针对每一个字段自定义它在form和table中的插槽

注意

自定义form和table中的插槽时,同一个IkPageFull组件不能有重复的slot名称

表格slot会通过插槽传递当前行的所有信息

示例

vue
<script lang="ts" setup name="ManageUser">
const fieldList: Ref<ISearchForm[]> = ref([
 {
    key: 'name',
    label: '姓名',
    show: true,
    width: 160,
    // 自定义表格slot
    tableSlot: 'name',
    // 自定义form的slot, 名称不可与tableSlot相同
    formSlot: 'formname'
    search: true,
    formProp: {
      clearable: true,
      placeholder: '请输入姓名',
      maxlength: 12
    }
  }
])

<template>
  <IkPageFull
      v-model="pageData"
      header-title="用户列表"
      :fields="fieldList"
      :table-data="userData"
      :total="tadaTotal"
      :loading="loading"
      :search="queryUser"
    >
      // 表单slot
      <template #formname>
        
      </template>
      // 表格slot
      <template #name="{ data }">
        
      </template>
  </IkPageFull>

</template>
</script>

完整示例

vue
<script lang="ts" setup name="ManageUser">
  // 当fieldList字段很多时,建议单独放在一个ts文件内
  const fieldList: Ref<ISearchForm[]> = ref([
    {
      key: 'name',
      label: '姓名',
      show: true,
      width: 160,
      tableSlot: 'name',
      search: true,
      formProp: {
        clearable: true,
        placeholder: '请输入姓名',
        maxlength: 5
      }
    },
    {
      key: 'postId',
      label: '岗位',
      type: 'el-select',
      options: postIdList,
      show: true,
      tableSlot: 'post',
      search: true
    },
    // input
    {
      key: 'appId',
      label: 'AppId',
      show: true,
      minWidth: 100,
      search: true,
      isEdit: true,
      type: 'el-input',
      formProp: {
        clearable: true
      },
      rules: [{
        required: true,
        message: '请输入AppId',
        trigger: 'blur'
      }]
    },
    {
      key: 'appId0',
      label: '人员',
      show: true,
      minWidth: 100,
      search: true,
      isEdit: true,
      type: 'choosePerson',
      selectKey: 'personName',
      handList: [],
      formProp: {
        clearable: true,
        readonly: true
      },
      rules: [{
        required: true,
        message: '请选择人员',
        trigger: 'blur'
      }]
    },
    {
      key: '',
      label: '操作',
      show: true,
      width: 148,
      tableSlot: 'operate',
      fixed: 'right'
    }
  ])

 // 用户数据 
 const userData = ref([])
 // 查询状态
 const loading = ref(false)
 // 总条数
 const tadaTotal = ref(0)
 // 组件返回的查询参数,包含分页和查询字段
 const pageData = ref()
 const pageFullRef = ref()
 // 查询方法
 const queryUser = async () => {
   loading.value = true
   const param = {
  ...pageData.value
   }
   const { success, data, total, msg } = await userApi.getUserList(param)
   tadaTotal.value = total ?? 0
   if (success) {
  userData.value = success ? data : []
  loading.value = false
   }
   else {
  msgError(msg)
   }
  }

  /**
 * @description: 表单提交数据
 * @param {*} form
 * @return {*}
 */
const handleAdd = async (form: any) => {
  const { msg, success, data } = form.id ? await msgApi.updateWechatMsg(form) : await msgApi.addWechatMsg(form)
  if (success)
    queryUser()
  else
    msgError(msg)
}

/**
 * @description: 修改
 * @param {*} row
 * @return {*}
 */
const handleUpdate = (row: any) => {
  pageFullRef.value?.handAdd(row)
}
</script>

<template>
 <!-- IkPageMain容器组件,见后面单独说明 -->
  <IkPageMain fixed>
   <!-- 页面组件 -->
    <IkPageFull
    ref="pageFullRef"
    v-model="pageData"
    header-title="用户列表"
    :fields="fieldList"
    :table-data="userData"
    :total="tadaTotal"
    :loading="loading"
    :search="queryUser"
    dialog-size="sl"
    role="$ADD-WECHAT"
    :form-key="formKey"
    @addFunction="handleAdd"
  >
    <!-- 搜索slot -->
    <template #headerRight>
      <el-button v-role="'$ADD-USER'" type="primary" @click="handAddUser">{{ t('user.add') }}</el-button>
    </template>
    <!-- 表格solt -->
    <template #name="{ data }">
   <IkSideText
     :avatar="data.row.avatar"
     :name="data.row.name"
     :text="data.row.name"
     :sub-text="ikArray.listToString(data?.row.departments, 'deptName', ',') "
   />
    </template>
    <template #role="{ data }">
   {{ ikArray.listToString(data.row?.roles, 'roleName', ', ') }}
    </template>
    <template #locked="{ data }">
   <el-switch
     v-model="data.row.locked"
     :loading="lockLoading && lockKey === data.row.id"
     inline-prompt
     :active-value="false"
     :inactive-value="true"
     @change="handLock(data.row)"
   />
    </template>
    <template #operate="{ data }">
   <IkBtnContent :num="4">
     <IkSvgIcon
    v-role="'UPDATE-USER'"
    name="icon-bianji"
    show-bg
    size="big"
     />
   </IkBtnContent>
    </template>
    </IkPageFull>
  </IkPageMain>

含Tab页的完整示例

Tab页

含Tab页时,可以借助IkPageTabs实现

vue
<script lang="ts" setup name="ManageLog">
import { useFields } from './fields'
import { logApi } from '@/MainApp/apis/log'

const { operationFieldList, loginFields, systemFieldList } = useFields()

const router = useRouter()
const route = useRoute()
// 组件返回的查询参数,包含分页和查询字段
const pageData = reactive({
  login: undefined,
  operate: null,
  system: null
})
const logData = reactive({
  login: [],
  operate: [],
  system: []
})

const countTotal = ref(0)
const loading = ref(false)

const activeLog = ref('login')
// 查询方法
const queryLogs = async () => {
  await nextTick()
  const ACTIVE = activeLog.value
  loading.value = true
  const log = { ...(pageData as any)[ACTIVE] }
  const { success, data, total }
    = ACTIVE === 'login'
      ? await logApi.getPageLogs(log)
      : ACTIVE === 'operate'
        ? await logApi.getOperateLogs(log)
        : await logApi.getSystemLogs(log);

  (logData as any)[ACTIVE] = success ? data : []
  countTotal.value = Number(total)
  loading.value = false
}

const handleDetail = (row: any) => {
  router.push({ path: `${route.path}/log-detail`, query: { id: row.id } })
}

const tabs = [{ id: 'login', label: '登录日志' }, { id: 'operate', label: '操作日志' }, { id: 'system', label: '系统日志' }]
</script>

<template>
  <IkPageMain fixed>
    <IkPageTabs v-model="activeLog" :tabs="tabs" @tab-click="queryLogs">
      <template #login>
        <IkPageFull
          v-model="pageData.login"
          tab-id="login"
          :fields="loginFields"
          :table-data="logData.login"
          :total="countTotal"
          :loading="loading"
          :search="queryLogs"
        >
          <template #success="{ data }">
            <IkStatu :type="data.row.success ? 'success' : 'warning'" :title="data.row.success ? '成功' : '失败'" />
          </template>
        </IkPageFull>
      </template>
      <template #operate>
        <IkPageFull
          v-model="pageData.operate"
          :immediate="false"
          tab-id="operate"
          :fields="operationFieldList"
          :table-data="logData.operate"
          :total="countTotal"
          :loading="loading"
          :search="queryLogs"
        >
          <template #success="{ data }">
            <IkStatu :type="data.row.success ? 'success' : 'warning'" :title="data.row.success ? '成功' : '失败'" />
          </template>
        </IkPageFull>
      </template>
      <template #system>
        <IkPageFull
          v-model="pageData.system"
          :immediate="false"
          tab-id="system"
          :fields="systemFieldList"
          :table-data="logData.system"
          :total="countTotal"
          :loading="loading"
          :search="queryLogs"
        >
          <template #operate="{ data }">
            <IkSvgIcon
              show-bg
              size="small"
              name="icon-ziyuanliebiao"
              @click="handleDetail(data.row)"
            />
          </template>
        </IkPageFull>
      </template>
    </IkPageTabs>
  </IkPageMain>
</template>