perf: tree table

pull/2/head
xingyu 2022-11-17 22:55:09 +08:00
parent 5d4673ac00
commit bc97bd30a0
4 changed files with 141 additions and 203 deletions

View File

@ -17,10 +17,10 @@ import { ComponentOptions } from '@/types/components'
export type VxeCrudSchema = { export type VxeCrudSchema = {
primaryKey?: string // 主键ID primaryKey?: string // 主键ID
primaryTitle?: string // 主键标题 默认为序号 primaryTitle?: string // 主键标题 默认为序号
primaryType?: VxeColumnPropTypes.Type // 不填写为数据库编号 还支持 "seq" | "radio" | "checkbox" | "expand" | "html" | null primaryType?: VxeColumnPropTypes.Type // 不填写为数据库编号 null为不显示 还支持 "seq" | "radio" | "checkbox" | "expand" | "html" | null
action?: boolean // 是否开启操作栏插槽 action?: boolean // 是否开启操作栏插槽
actionTitle?: string // 操作栏标题 默认为操作 actionTitle?: string // 操作栏标题 默认为操作
actionWidth?: string // 操作栏插槽宽度一般2个字带图标 text 类型按钮 50-70 actionWidth?: string // 操作栏插槽宽度,一般2个字带图标 text 类型按钮 50-70
columns: VxeCrudColumns[] columns: VxeCrudColumns[]
} }
type VxeCrudColumns = Omit<VxeTableColumn, 'children'> & { type VxeCrudColumns = Omit<VxeTableColumn, 'children'> & {
@ -170,7 +170,7 @@ const filterTableSchema = (crudSchema: VxeCrudSchema): VxeGridPropTypes.Columns
const { t } = useI18n() const { t } = useI18n()
const tableSchema: VxeGridPropTypes.Columns = [] const tableSchema: VxeGridPropTypes.Columns = []
// 主键ID // 主键ID
if (crudSchema.primaryKey) { if (crudSchema.primaryKey && crudSchema.primaryType) {
const tableSchemaItem = { const tableSchemaItem = {
title: crudSchema.primaryTitle ? crudSchema.primaryTitle : t('common.index'), title: crudSchema.primaryTitle ? crudSchema.primaryTitle : t('common.index'),
field: crudSchema.primaryKey, field: crudSchema.primaryKey,
@ -179,6 +179,7 @@ const filterTableSchema = (crudSchema: VxeCrudSchema): VxeGridPropTypes.Columns
} }
tableSchema.push(tableSchemaItem) tableSchema.push(tableSchemaItem)
} }
eachTree(crudSchema.columns, (schemaItem: VxeCrudColumns) => { eachTree(crudSchema.columns, (schemaItem: VxeCrudColumns) => {
// 判断是否显示 // 判断是否显示
if (schemaItem?.isTable !== false && schemaItem?.table?.show !== false) { if (schemaItem?.isTable !== false && schemaItem?.table?.show !== false) {

View File

@ -1,5 +1,5 @@
import { computed, nextTick, reactive } from 'vue' import { computed, nextTick, reactive } from 'vue'
import { SizeType, VxeGridProps } from 'vxe-table' import { SizeType, VxeGridProps, VxeTablePropTypes } from 'vxe-table'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
import { VxeAllSchemas } from './useVxeCrudSchemas' import { VxeAllSchemas } from './useVxeCrudSchemas'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
@ -11,6 +11,7 @@ const message = useMessage() // 消息弹窗
interface UseVxeGridConfig<T = any> { interface UseVxeGridConfig<T = any> {
allSchemas: VxeAllSchemas allSchemas: VxeAllSchemas
treeConfig?: VxeTablePropTypes.TreeConfig
getListApi: (option: any) => Promise<T> getListApi: (option: any) => Promise<T>
deleteApi?: (option: any) => Promise<T> deleteApi?: (option: any) => Promise<T>
exportListApi?: (option: any) => Promise<T> exportListApi?: (option: any) => Promise<T>
@ -41,6 +42,7 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
/** /**
* grid options * grid options
*/ */
console.info(config?.allSchemas.tableSchema)
const gridOptions = reactive<VxeGridProps>({ const gridOptions = reactive<VxeGridProps>({
loading: true, loading: true,
size: currentSize as any, size: currentSize as any,
@ -62,25 +64,6 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
items: config?.allSchemas.searchSchema items: config?.allSchemas.searchSchema
}, },
columns: config?.allSchemas.tableSchema, columns: config?.allSchemas.tableSchema,
pagerConfig: {
border: false, // 带边框
background: true, // 带背景颜色
perfect: false, // 配套的样式
pageSize: 10, // 每页大小
pagerCount: 7, // 显示页码按钮的数量
autoHidden: true, // 当只有一页时自动隐藏
pageSizes: [5, 10, 20, 30, 50, 100], // 每页大小选项列表
layouts: [
'PrevJump',
'PrevPage',
'JumpNumber',
'NextPage',
'NextJump',
'Sizes',
'FullJump',
'Total'
]
},
proxyConfig: { proxyConfig: {
seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号) seq: true, // 启用动态序号代理(分页之后索引自动计算为当前页的起始序号)
form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为 form: true, // 启用表单代理,当点击表单提交按钮时会自动触发 reload 行为
@ -91,8 +74,10 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
if (config?.queryParams) { if (config?.queryParams) {
queryParams = Object.assign(queryParams, config.queryParams) queryParams = Object.assign(queryParams, config.queryParams)
} }
queryParams.pageSize = page.pageSize if (!config?.treeConfig) {
queryParams.pageNo = page.currentPage queryParams.pageSize = page.pageSize
queryParams.pageNo = page.currentPage
}
gridOptions.loading = false gridOptions.loading = false
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
resolve(await config?.getListApi(queryParams)) resolve(await config?.getListApi(queryParams))
@ -120,6 +105,30 @@ export const useVxeGrid = <T = any>(config?: UseVxeGridConfig<T>) => {
} }
}) })
if (config?.treeConfig) {
gridOptions.treeConfig = config.treeConfig
} else {
gridOptions.pagerConfig = {
border: false, // 带边框
background: true, // 带背景颜色
perfect: false, // 配套的样式
pageSize: 10, // 每页大小
pagerCount: 7, // 显示页码按钮的数量
autoHidden: false, // 当只有一页时自动隐藏
pageSizes: [5, 10, 20, 30, 50, 100], // 每页大小选项列表
layouts: [
'PrevJump',
'PrevPage',
'JumpNumber',
'NextPage',
'NextJump',
'Sizes',
'FullJump',
'Total'
]
}
}
/** /**
* *
* @param ref * @param ref

View File

@ -1,6 +1,9 @@
import { required } from '@/utils/formRules'
import { reactive } from 'vue' import { reactive } from 'vue'
import { FormSchema } from '@/types/form' import { useI18n } from '@/hooks/web/useI18n'
import { required } from '@/utils/formRules'
import { DICT_TYPE } from '@/utils/dict'
import { VxeCrudSchema, useVxeCrudSchemas } from '@/hooks/web/useVxeCrudSchemas'
const { t } = useI18n() // 国际化
// 表单校验 // 表单校验
export const rules = reactive({ export const rules = reactive({
@ -17,55 +20,49 @@ export const rules = reactive({
] ]
}) })
export const modelSchema = reactive<FormSchema[]>([ // CrudSchema
{ const crudSchemas = reactive<VxeCrudSchema>({
label: '上级部门', primaryKey: 'id',
field: 'parentId', primaryType: null,
component: 'Input' action: true,
}, columns: [
{ {
label: '部门名称', title: '上级部门',
field: 'name', field: 'parentId',
component: 'Input', isTable: false
formItemProps: { },
rules: [required] {
title: '部门名称',
field: 'name',
isSearch: true,
table: {
treeNode: true,
align: 'left'
}
},
{
title: '负责人',
field: 'leaderUserId'
},
{
title: '联系电话',
field: 'phone'
},
{
title: '邮箱',
field: 'email'
},
{
title: '显示排序',
field: 'sort'
},
{
title: t('common.status'),
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
dictData: 'number',
isSearch: true
} }
}, ]
{ })
label: '负责人', export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
field: 'leaderUserId',
component: 'Input'
},
{
label: '联系电话',
field: 'phone',
component: 'Input'
},
{
label: '邮箱',
field: 'email',
component: 'Input'
},
{
label: '显示排序',
field: 'sort',
component: 'Input'
},
{
label: '状态',
field: 'status',
component: 'RadioButton',
componentProps: {
options: [
{
label: '开启',
value: 0
},
{
label: '关闭',
value: 1
}
]
}
}
])

View File

@ -1,34 +1,8 @@
<template> <template>
<ContentWrap> <ContentWrap>
<!-- 搜索工作栏 --> <!-- 列表 -->
<el-form :model="queryParams" ref="queryForm" :inline="true"> <vxe-grid ref="xGrid" v-bind="gridOptions" show-overflow class="xtable-scrollbar">
<el-form-item label="部门名称" prop="name"> <template #toolbar_buttons>
<el-input v-model="queryParams.name" placeholder="请输入部门名称" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择部门状态">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<!-- 操作搜索 -->
<XButton
type="primary"
preIcon="ep:search"
:title="t('common.query')"
@click="handleQuery()"
/>
<!-- 操作重置 -->
<XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" />
</el-form-item>
</el-form>
<vxe-toolbar>
<template #buttons>
<!-- 操作新增 --> <!-- 操作新增 -->
<XButton <XButton
type="primary" type="primary"
@ -37,58 +11,32 @@
v-hasPermi="['system:dept:create']" v-hasPermi="['system:dept:create']"
@click="handleCreate()" @click="handleCreate()"
/> />
<XButton title="展开所有" @click="xTable?.setAllTreeExpand(true)" /> <XButton title="展开所有" @click="xGrid?.setAllTreeExpand(true)" />
<XButton title="关闭所有" @click="xTable?.clearTreeExpand()" /> <XButton title="关闭所有" @click="xGrid?.clearTreeExpand()" />
</template> </template>
</vxe-toolbar> <template #actionbtns_default="{ row }">
<!-- 列表 --> <!-- 操作修改 -->
<vxe-table <XTextButton
show-overflow preIcon="ep:edit"
keep-source :title="t('action.edit')"
ref="xTable" v-hasPermi="['system:dept:update']"
:loading="tableLoading" @click="handleUpdate(row.id)"
:row-config="{ keyField: 'id' }" />
:column-config="{ resizable: true }" <!-- 操作删除 -->
:tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }" <XTextButton
:print-config="{}" preIcon="ep:delete"
:export-config="{}" :title="t('action.del')"
:data="tableData" v-hasPermi="['system:dept:delete']"
class="xtable" @click="handleDelete(row.id)"
> />
<vxe-column title="部门名称" field="name" width="200" tree-node /> </template>
<vxe-column title="负责人" field="leaderUserId" :formatter="userNicknameFormat" /> </vxe-grid>
<vxe-column title="排序" field="sort" />
<vxe-column title="状态" field="status">
<template #default="{ row }">
<DictTag :type="DICT_TYPE.COMMON_STATUS" :value="row.status" />
</template>
</vxe-column>
<vxe-column title="创建时间" field="createTime" formatter="formatDate" />
<vxe-column title="操作" width="200">
<template #default="{ row }">
<!-- 操作修改 -->
<XTextButton
preIcon="ep:edit"
:title="t('action.edit')"
v-hasPermi="['system:dept:update']"
@click="handleUpdate(row.id)"
/>
<!-- 操作删除 -->
<XTextButton
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['system:dept:delete']"
@click="handleDelete(row.id)"
/>
</template>
</vxe-column>
</vxe-table>
</ContentWrap> </ContentWrap>
<!-- 添加或修改菜单对话框 --> <!-- 添加或修改菜单对话框 -->
<XModal id="deptModel" v-model="dialogVisible" :title="dialogTitle"> <XModal id="deptModel" v-model="dialogVisible" :title="dialogTitle">
<!-- 对话框(添加 / 修改) --> <!-- 对话框(添加 / 修改) -->
<!-- 操作工具栏 --> <!-- 操作工具栏 -->
<Form ref="formRef" :schema="modelSchema" :rules="rules"> <Form ref="formRef" :schema="allSchemas.formSchema" :rules="rules">
<template #parentId> <template #parentId>
<el-tree-select <el-tree-select
node-key="id" node-key="id"
@ -128,22 +76,32 @@
import { nextTick, onMounted, reactive, ref, unref } from 'vue' import { nextTick, onMounted, reactive, ref, unref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { useMessage } from '@/hooks/web/useMessage' import { useMessage } from '@/hooks/web/useMessage'
import { ElForm, ElFormItem, ElInput, ElSelect, ElTreeSelect, ElOption } from 'element-plus' import { VxeGridInstance } from 'vxe-table'
import { VxeTableInstance } from 'vxe-table' import { ElSelect, ElTreeSelect, ElOption } from 'element-plus'
import { modelSchema } from './dept.data' import { allSchemas } from './dept.data'
import * as DeptApi from '@/api/system/dept' import * as DeptApi from '@/api/system/dept'
import { getListSimpleUsersApi } from '@/api/system/user' import { getListSimpleUsersApi } from '@/api/system/user'
import { required } from '@/utils/formRules.js' import { required } from '@/utils/formRules.js'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { handleTree } from '@/utils/tree' import { handleTree } from '@/utils/tree'
import { FormExpose } from '@/components/Form' import { FormExpose } from '@/components/Form'
import { useVxeGrid } from '@/hooks/web/useVxeGrid'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
// //
const xTable = ref<VxeTableInstance>() const xGrid = ref<VxeGridInstance>() // Grid Ref
const tableLoading = ref(false) const treeConfig = {
const tableData = ref() transform: true,
rowField: 'id',
parentField: 'parentId',
expandAll: true
}
const { gridOptions, getList, deleteData } = useVxeGrid<DeptApi.DeptVO>({
allSchemas: allSchemas,
treeConfig: treeConfig,
getListApi: DeptApi.getDeptPageApi,
deleteApi: DeptApi.deleteDeptApi
})
// //
const dialogVisible = ref(false) // const dialogVisible = ref(false) //
const dialogTitle = ref('edit') // const dialogTitle = ref('edit') //
@ -181,30 +139,6 @@ const getUserList = async () => {
const res = await getListSimpleUsersApi() const res = await getListSimpleUsersApi()
userOption.value = res userOption.value = res
} }
// ========== ==========
const queryParams = reactive<DeptApi.DeptPageReqVO>({
name: undefined,
status: undefined
})
//
const getList = async () => {
tableLoading.value = true
const res = await DeptApi.getDeptPageApi(queryParams)
tableData.value = res
tableLoading.value = false
}
//
const handleQuery = async () => {
await getList()
}
//
const resetQuery = async () => {
queryParams.name = undefined
queryParams.status = undefined
await getList()
}
// ========== / ========== // ========== / ==========
@ -247,13 +181,15 @@ const submitForm = async () => {
data.leaderUserId = leaderUserId.value data.leaderUserId = leaderUserId.value
if (dialogTitle.value.startsWith('新增')) { if (dialogTitle.value.startsWith('新增')) {
await DeptApi.createDeptApi(data) await DeptApi.createDeptApi(data)
message.success(t('common.createSuccess'))
} else if (dialogTitle.value.startsWith('修改')) { } else if (dialogTitle.value.startsWith('修改')) {
await DeptApi.updateDeptApi(data) await DeptApi.updateDeptApi(data)
message.success(t('common.updateSuccess'))
} }
//
dialogVisible.value = false dialogVisible.value = false
} finally { } finally {
actionLoading.value = false actionLoading.value = false
await getList(xGrid)
} }
} }
}) })
@ -261,29 +197,24 @@ const submitForm = async () => {
// //
const handleDelete = async (rowId: number) => { const handleDelete = async (rowId: number) => {
message.delConfirm().then(async () => { await deleteData(xGrid, rowId)
await DeptApi.deleteDeptApi(rowId)
message.success(t('common.delSuccess'))
await getList()
})
}
const userNicknameFormat = (row) => {
if (!row && !row.row && !row.row.leaderUserId) {
return '未设置'
}
for (const user of userOption.value) {
if (row.row.leaderUserId === user.id) {
return user.nickname
}
}
return '未知【' + row.row.leaderUserId + '】'
} }
//const userNicknameFormat = (row) => {
// if (!row && !row.row && !row.row.leaderUserId) {
// return ''
// }
// for (const user of userOption.value) {
// if (row.row.leaderUserId === user.id) {
// return user.nickname
// }
// }
// return '' + row.row.leaderUserId + ''
//}
//
// ========== ========== // ========== ==========
onMounted(async () => { onMounted(async () => {
await getTree() await getTree()
await getUserList() await getUserList()
await getList()
}) })
</script> </script>