feat: dept vxe table

This commit is contained in:
xingyu 2022-11-17 20:20:09 +08:00
parent 83954241ae
commit 7cabf0ead5
6 changed files with 271 additions and 202 deletions

View File

@ -43,7 +43,7 @@
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"pinia": "^2.0.23",
"pinia": "^2.0.24",
"qrcode": "^1.5.1",
"qs": "^6.11.0",
"url": "^0.11.0",

View File

@ -42,7 +42,7 @@ specifiers:
lodash-es: ^4.17.21
mitt: ^3.0.0
nprogress: ^0.2.0
pinia: ^2.0.23
pinia: ^2.0.24
plop: ^3.1.1
postcss: ^8.4.19
postcss-html: ^1.5.0
@ -99,7 +99,7 @@ dependencies:
lodash-es: 4.17.21
mitt: 3.0.0
nprogress: 0.2.0
pinia: 2.0.23_zwu2zepfy3m6u2gunxlolp35gi
pinia: 2.0.24_zwu2zepfy3m6u2gunxlolp35gi
qrcode: 1.5.1
qs: 6.11.0
url: 0.11.0
@ -1565,10 +1565,6 @@ packages:
/@vue/devtools-api/6.4.3:
resolution: {integrity: sha512-9WCRwdROJvWcHAdyrR7SZMM/qUvllDZnpndHXokThkUsjnJ2xe4/pvsH9FZrxFe22L+JmDKczL79HjLJ7DK9rg==}
/@vue/devtools-api/6.4.4:
resolution: {integrity: sha512-Ku31WzpOV/8cruFaXaEZKF81WkNnvCSlBY4eOGtz5WMSdJvX1v1WWlSMGZeqUwPtQ27ZZz7B62erEMq8JDjcXw==}
dev: false
/@vue/devtools-api/6.4.5:
resolution: {integrity: sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==}
dev: false
@ -5564,8 +5560,8 @@ packages:
dev: true
optional: true
/pinia/2.0.23_zwu2zepfy3m6u2gunxlolp35gi:
resolution: {integrity: sha512-N15hFf4o5STrxpNrib1IEb1GOArvPYf1zPvQVRGOO1G1d74Ak0J0lVyalX/SmrzdT4Q0nlEFjbURsmBmIGUR5Q==}
/pinia/2.0.24_zwu2zepfy3m6u2gunxlolp35gi:
resolution: {integrity: sha512-DDLd4Iphyc+6PYYYbx7jkb6WP9gecgu9bz9huyB5rb7CdJI3DhzYiZI+/Ih8MLewRrP9DSpslF/BgSNrJtZU7A==}
peerDependencies:
'@vue/composition-api': ^1.4.0
typescript: '>=4.4.4'
@ -5576,7 +5572,7 @@ packages:
typescript:
optional: true
dependencies:
'@vue/devtools-api': 6.4.4
'@vue/devtools-api': 6.4.5
typescript: 4.8.4
vue: 3.2.45
vue-demi: 0.13.11_vue@3.2.45

View File

@ -1,32 +1,46 @@
import request from '@/config/axios'
import type { DeptVO, DeptListReqVO } from './types'
export type DeptVO = {
id: number
name: string
parentId: number
status: number
sort: number
leaderUserId: number
phone: string
email: string
}
export interface DeptPageReqVO {
name?: string
status?: number
}
// 查询部门(精简)列表
export const listSimpleDeptApi = () => {
return request.get({ url: '/system/dept/list-all-simple' })
export const listSimpleDeptApi = async () => {
return await request.get({ url: '/system/dept/list-all-simple' })
}
// 查询部门列表
export const getDeptPageApi = (params: DeptListReqVO) => {
return request.get({ url: '/system/dept/list', params })
export const getDeptPageApi = async (params: DeptPageReqVO) => {
return await request.get({ url: '/system/dept/list', params })
}
// 查询部门详情
export const getDeptApi = (id: number) => {
return request.get({ url: '/system/dept/get?id=' + id })
export const getDeptApi = async (id: number) => {
return await request.get({ url: '/system/dept/get?id=' + id })
}
// 新增部门
export const createDeptApi = (data: DeptVO) => {
return request.post({ url: '/system/dept/create', data: data })
export const createDeptApi = async (data: DeptVO) => {
return await request.post({ url: '/system/dept/create', data: data })
}
// 修改部门
export const updateDeptApi = (params: DeptVO) => {
return request.put({ url: '/system/dept/update', data: params })
export const updateDeptApi = async (params: DeptVO) => {
return await request.put({ url: '/system/dept/update', data: params })
}
// 删除部门
export const deleteDeptApi = (id: number) => {
return request.delete({ url: '/system/dept/delete?id=' + id })
export const deleteDeptApi = async (id: number) => {
return await request.delete({ url: '/system/dept/delete?id=' + id })
}

View File

@ -49,7 +49,7 @@ export const modelSchema = reactive<FormSchema[]>([
{
label: '显示排序',
field: 'sort',
component: 'InputNumber'
component: 'Input'
},
{
label: '状态',

View File

@ -1,70 +1,92 @@
<template>
<div class="flex">
<el-card class="w-1/3 dept" :gutter="12" shadow="always">
<template #header>
<div class="card-header">
<span>部门列表</span>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" :inline="true">
<el-form-item label="部门名称" prop="name">
<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
type="primary"
preIcon="ep:zoom-in"
title="新增根节点"
v-hasPermi="['system:dept:create']"
@click="handleCreate"
/>
</div>
</template>
<div class="custom-tree-container">
<!-- <p>部门列表</p> -->
<!-- 操作工具栏 -->
<el-input v-model="filterText" placeholder="搜索部门" />
<el-tree
ref="treeRef"
node-key="id"
:data="deptOptions"
:props="defaultProps"
:highlight-current="true"
default-expand-all
:filter-node-method="filterNode"
:expand-on-click-node="false"
>
<template #default="{ node, data }">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<XTextButton
preIcon="ep:zoom-in"
:title="t('action.add')"
v-hasPermi="['system:dept:create']"
@click="handleCreate(data)"
@click="handleCreate()"
/>
<XButton title="展开所有" @click="xTable?.setAllTreeExpand(true)" />
<XButton title="关闭所有" @click="xTable?.clearTreeExpand()" />
</template>
</vxe-toolbar>
<!-- 列表 -->
<vxe-table
show-overflow
keep-source
ref="xTable"
:loading="tableLoading"
:row-config="{ keyField: 'id' }"
:column-config="{ resizable: true }"
:tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
:print-config="{}"
:export-config="{}"
:data="tableData"
class="xtable"
>
<vxe-column title="部门名称" field="name" width="200" tree-node />
<vxe-column title="负责人" field="leaderUserId" :formatter="userNicknameFormat" />
<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(data)"
@click="handleUpdate(row.id)"
/>
<!-- 操作删除 -->
<XTextButton
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['system:dept:delete']"
@click="handleDelete(data)"
@click="handleDelete(row.id)"
/>
</span>
</span>
</template>
</el-tree>
</div>
</el-card>
<el-card class="w-2/3 dept" style="margin-left: 10px" :gutter="12" shadow="hover">
<template #header>
<div class="card-header">
<span>{{ formTitle }}</span>
</div>
</template>
<div v-if="!showForm">
<span><p>请从左侧选择部门</p></span>
</div>
<div v-if="showForm">
</vxe-column>
</vxe-table>
</ContentWrap>
<!-- 添加或修改菜单对话框 -->
<XModal id="deptModel" v-model="dialogVisible" :title="dialogTitle">
<!-- 对话框(添加 / 修改) -->
<!-- 操作工具栏 -->
<Form ref="formRef" :schema="modelSchema" :rules="rules">
<template #parentId>
@ -73,6 +95,7 @@
v-model="deptParentId"
:props="defaultProps"
:data="deptOptions"
:default-expanded-keys="[100]"
check-strictly
/>
</template>
@ -87,143 +110,179 @@
</el-select>
</template>
</Form>
<template #footer>
<!-- 按钮保存 -->
<XButton
v-if="['create', 'update'].includes(actionType)"
type="primary"
:title="t('action.save')"
v-hasPermi="['system:dept:update']"
:loading="loading"
:loading="actionLoading"
@click="submitForm()"
:title="t('action.save')"
/>
<!-- 按钮关闭 -->
<XButton :loading="loading" :title="t('dialog.close')" @click="showForm = false" />
</div>
</el-card>
</div>
<XButton :loading="actionLoading" @click="dialogVisible = false" :title="t('dialog.close')" />
</template>
</XModal>
</template>
<script setup lang="ts">
import { nextTick, onMounted, reactive, ref, unref } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { ElInput, ElCard, ElTree, ElTreeSelect, ElSelect, ElOption } from 'element-plus'
import { handleTree } from '@/utils/tree'
import { onMounted, ref, unref, watch } from 'vue'
import * as DeptApi from '@/api/system/dept'
import { Form, FormExpose } from '@/components/Form'
import { modelSchema, rules } from './dept.data'
import { DeptVO } from '@/api/system/dept/types'
import { useMessage } from '@/hooks/web/useMessage'
import { ElForm, ElFormItem, ElInput, ElSelect, ElTreeSelect, ElOption } from 'element-plus'
import { VxeColumn, VxeTable, VxeTableInstance, VxeToolbar } from 'vxe-table'
import { modelSchema } from './dept.data'
import * as DeptApi from '@/api/system/dept'
import { getListSimpleUsersApi } from '@/api/system/user'
const message = useMessage()
import { required } from '@/utils/formRules.js'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { handleTree } from '@/utils/tree'
import { FormExpose } from '@/components/Form'
const { t } = useI18n() //
const message = useMessage() //
//
const xTable = ref<VxeTableInstance>()
const tableLoading = ref(false)
const tableData = ref()
//
const dialogVisible = ref(false) //
const dialogTitle = ref('edit') //
const actionType = ref('') //
const actionLoading = ref(false) //
const deptParentId = ref(0) // ID
const leaderUserId = ref()
const formRef = ref<FormExpose>() // Ref
const deptOptions = ref() //
const userOption = ref()
//
const rules = reactive({
name: [required],
sort: [required],
path: [required],
status: [required]
})
// []
const defaultProps = {
checkStrictly: true,
children: 'children',
label: 'name',
value: 'id'
}
const { t } = useI18n() //
const loading = ref(false) //
const dialogVisible = ref(false) //
const showForm = ref(false) // form
const formTitle = ref('部门信息') // form
const deptParentId = ref(0) // ID
// form
const formRef = ref<FormExpose>()
// ========== ==========
const filterText = ref('')
const deptOptions = ref() //
const treeRef = ref<InstanceType<typeof ElTree>>()
// []
const getTree = async () => {
const res = await DeptApi.listSimpleDeptApi()
deptOptions.value = handleTree(res)
console.info(deptOptions.value)
}
const filterNode = (value: string, data: Tree) => {
if (!value) return true
return data.name.includes(value)
}
watch(filterText, (val) => {
treeRef.value!.filter(val)
})
//
const userOption = ref()
const leaderUserId = ref()
const getUserList = async () => {
const res = await getListSimpleUsersApi()
userOption.value = res
}
//
const handleCreate = (data: { id: number }) => {
//
deptParentId.value = data.id
formTitle.value = '新增部门'
showForm.value = true
}
//
const handleUpdate = async (data: { id: number }) => {
const res = await DeptApi.getDeptApi(data.id)
formTitle.value = '修改- ' + res?.name
deptParentId.value = res.parentId
leaderUserId.value = res.leaderUserId
unref(formRef)?.setValues(res)
showForm.value = true
}
//
const handleDelete = async (data: { id: number }) => {
message
.confirm(t('common.delDataMessage'), t('common.confirmTitle'))
.then(async () => {
await DeptApi.deleteDeptApi(data.id)
message.success(t('common.delSuccess'))
// ========== ==========
const queryParams = reactive<DeptApi.DeptPageReqVO>({
name: undefined,
status: undefined
})
.catch(() => {})
await getTree()
//
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()
}
// ========== / ==========
//
const setDialogTile = (type: string) => {
dialogTitle.value = t('action.' + type)
actionType.value = type
dialogVisible.value = true
}
//
const handleCreate = async () => {
deptParentId.value = 0
leaderUserId.value = null
setDialogTile('create')
}
//
const handleUpdate = async (rowId: number) => {
setDialogTile('update')
//
const res = await DeptApi.getDeptApi(rowId)
console.info(res)
deptParentId.value = res.deptParentId
leaderUserId.value = res.leaderUserId
await nextTick()
unref(formRef)?.setValues(res)
}
// /
const submitForm = async () => {
const elForm = unref(formRef)?.getElFormRef()
if (!elForm) return
elForm.validate(async (valid) => {
if (valid) {
loading.value = true
actionLoading.value = true
//
try {
const data = unref(formRef)?.formModel as DeptVO
const data = unref(formRef)?.formModel as DeptApi.DeptVO
data.parentId = deptParentId.value
data.leaderUserId = leaderUserId.value
if (formTitle.value.startsWith('新增')) {
if (dialogTitle.value.startsWith('新增')) {
await DeptApi.createDeptApi(data)
} else if (formTitle.value.startsWith('修改')) {
} else if (dialogTitle.value.startsWith('修改')) {
await DeptApi.updateDeptApi(data)
}
//
dialogVisible.value = false
} finally {
loading.value = false
actionLoading.value = false
}
}
})
}
//
const handleDelete = async (rowId: number) => {
message.delConfirm().then(async () => {
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 + '】'
}
// ========== ==========
onMounted(async () => {
await getTree()
await getUserList()
await getList()
})
</script>
<style scoped>
.dept {
height: 600px;
max-height: 1800px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
</style>

View File

@ -13,7 +13,7 @@ import { createHtmlPlugin } from 'vite-plugin-html'
import viteCompression from 'vite-plugin-compression'
import VueMarcos from 'unplugin-vue-macros/vite'
// 当前执行node命令时文件夹的地址(工作目录)
// 当前执行node命令时文件夹的地址(工作目录)
const root = process.cwd()
// 路径查找