feat: 新增部门管理

This commit is contained in:
2024-04-10 21:22:25 +08:00
parent 63da55ab03
commit 3533e020c6
7 changed files with 374 additions and 0 deletions

29
src/apis/system/dept.ts Normal file
View File

@@ -0,0 +1,29 @@
import http from '@/utils/http'
import type * as System from './type'
const BASE_URL = '/system/dept'
/** @desc 查询部门列表 */
export function listDept(query: System.DeptQuery) {
return http.get<System.DeptResp[]>(`${BASE_URL}/tree`, query)
}
/** @desc 查询部门详情 */
export function getDept(id: string) {
return http.get<System.DeptResp>(`${BASE_URL}/${id}`)
}
/** @desc 新增部门 */
export function addDept(data: any) {
return http.post<boolean>(`${BASE_URL}`, data)
}
/** @desc 修改部门 */
export function updateDept(data: any, id: string) {
return http.put(`${BASE_URL}/${id}`, data)
}
/** @desc 删除部门 */
export function deleteDept(id: string) {
return http.del(`${BASE_URL}/${id}`)
}

View File

@@ -1 +1,2 @@
export * from './dept'
export * from './log' export * from './log'

View File

@@ -1,3 +1,24 @@
/** 系统部门类型 */
export interface DeptResp {
id: string
name: string
sort: number
status: 1 | 2
isSystem: boolean
description: string
createUserString: string
createTime: string
updateUserString: string
updateTime: string
parentId: string
children: DeptResp[]
}
export interface DeptQuery {
description?: string
status?: number
sort?: Array<string>
}
/** 系统日志类型 */ /** 系统日志类型 */
export interface LogResp { export interface LogResp {
id: string id: string

View File

@@ -37,5 +37,6 @@ declare module 'vue' {
ParentView: typeof import('./../components/ParentView/index.vue')['default'] ParentView: typeof import('./../components/ParentView/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
TextCopy: typeof import('./../components/TextCopy/index.vue')['default']
} }
} }

View File

@@ -0,0 +1,151 @@
<template>
<a-modal
v-model:visible="visible"
:title="title"
:mask-closable="false"
:esc-to-close="false"
:modal-style="{ maxWidth: '520px' }"
width="90%"
@before-ok="save"
@close="reset"
>
<GiForm ref="formRef" v-model="form" :options="options" :columns="columns" />
</a-modal>
</template>
<script setup lang="ts">
import { getDept, addDept, updateDept } from '@/apis'
import type { DeptReq } from './type'
import { Message } from '@arco-design/web-vue'
import { GiForm, type Columns } from '@/components/GiForm'
import { useForm } from '@/hooks'
import { useDept } from '@/hooks/app'
const { deptList, getDeptList } = useDept()
const dataId = ref('')
const isUpdate = computed(() => !!dataId.value)
const title = computed(() => (isUpdate.value ? '修改部门' : '新增部门'))
const formRef = ref<InstanceType<typeof GiForm>>()
const options: Options = {
form: {},
col: { xs: 24, sm: 24, md: 24, lg: 24, xl: 24, xxl: 24 },
btns: { hide: true }
}
const columns: Columns = [
{
label: '上级部门',
field: 'parentId',
type: 'tree-select',
data: deptList,
props: {
allowClear: true,
allowSearch: true,
fallbackOption: false,
filterTreeNode(searchKey, nodeData) {
if (nodeData.title) {
return nodeData.title.toLowerCase().indexOf(searchKey.toLowerCase()) > -1
}
return false
}
}
},
{ label: '名称', field: 'name', type: 'input', rules: [{ required: true, message: '请输入名称' }] },
{
label: '排序',
field: 'sort',
type: 'input-number',
props: {
min: 1,
mode: 'button'
}
},
{
label: '描述',
field: 'description',
type: 'textarea',
props: {
maxLength: 200,
autoSize: { minRows: 3, maxRows: 5 }
}
},
{
label: '状态',
field: 'status',
type: 'switch',
props: {
type: 'round',
checkedValue: 1,
uncheckedValue: 2,
checkedText: '启用',
uncheckedText: '禁用'
}
}
]
const { form, resetForm } = useForm<DeptReq>({
name: '',
parentId: undefined,
sort: 999,
status: 1,
})
// 重置
const reset = () => {
formRef.value?.formRef?.resetFields()
resetForm()
}
const visible = ref(false)
// 新增
const onAdd = (id?: string) => {
if (!deptList.value.length) {
getDeptList()
}
reset()
form.parentId = id
dataId.value = ''
visible.value = true
}
// 修改
const onUpdate = async (id: string) => {
if (!deptList.value.length) {
await getDeptList()
}
reset()
dataId.value = id
const res = await getDept(id)
Object.assign(form, res.data)
visible.value = true
}
// 保存
const save = async () => {
try {
const isInvalid = await formRef.value?.formRef?.validate()
if (isInvalid) return false
if (isUpdate.value) {
await updateDept(form, dataId.value)
Message.success('修改成功')
} else {
await addDept(form)
Message.success('新增成功')
}
// 更新部门树
await getDeptList()
emit('save-success')
return true
} catch (error) {
return false
}
}
const emit = defineEmits<{
(e: 'save-success'): void
}>()
defineExpose({ onAdd, onUpdate })
</script>

View File

@@ -0,0 +1,164 @@
<template>
<div class="gi_page">
<a-card title="部门管理" class="general-card">
<GiTable
ref="tableRef"
row-key="id"
:data="dataList"
:columns="columns"
:loading="loading"
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
:pagination="false"
:disabledColumnKeys="['name']"
@refresh="search"
>
<template #expand-icon="{ expanded }">
<IconDown v-if="expanded" />
<IconRight v-else />
</template>
<template #custom-left>
<a-input v-model="queryForm.description" placeholder="请输入关键词" allow-clear @change="search">
<template #prefix><icon-search /></template>
</a-input>
<a-select
v-model="queryForm.status"
:options="DisEnableStatusList"
placeholder="请选择状态"
allow-clear
style="width: 150px"
@change="search"
/>
<a-button @click="reset">重置</a-button>
</template>
<template #custom-right>
<a-button type="primary" @click="onAdd">
<template #icon><icon-plus /></template>
<span>新增</span>
</a-button>
<a-tooltip content="导出">
<a-button>
<template #icon>
<icon-download />
</template>
</a-button>
</a-tooltip>
</template>
<template #status="{ record }">
<GiCellStatus :status="record.status" />
</template>
<template #isSystem="{ record }">
<a-tag v-if="record.isSystem" color="red" size="small"></a-tag>
<a-tag v-else color="arcoblue" size="small"></a-tag>
</template>
<template #action="{ record }">
<a-space>
<template #split>
<a-divider direction="vertical" :margin="0" />
</template>
<a-link @click="onUpdate(record)">修改</a-link>
<a-link @click="onAdd(record.id)">新增</a-link>
<a-popconfirm
type="warning"
content="是否确定删除该条数据?"
:ok-button-props="{ status: 'danger' }"
@ok="onDelete(record)"
>
<a-link
status="danger"
:title="record.isSystem ? '系统内置数据不能删除' : undefined"
:disabled="record.disabled"
>
删除
</a-link>
</a-popconfirm>
</a-space>
</template>
</GiTable>
</a-card>
<AddDeptModal ref="AddDeptModalRef" @save-success="search" />
</div>
</template>
<script setup lang="ts">
import { listDept, deleteDept, type DeptResp, type DeptQuery } from '@/apis'
import { Message, type TableInstance } from '@arco-design/web-vue'
import type GiTable from '@/components/GiTable/index.vue'
import AddDeptModal from './AddDeptModal.vue'
import { DisEnableStatusList } from '@/constant/common'
import { isMobile } from '@/utils'
defineOptions({ name: 'Dept' })
const columns: TableInstance['columns'] = [
{ title: '名称', dataIndex: 'name', width: 170, ellipsis: true, tooltip: true },
{ title: '状态', slotName: 'status', align: 'center' },
{ title: '排序', dataIndex: 'sort', align: 'center', show: false },
{ title: '系统内置', slotName: 'isSystem', align: 'center', show: false },
{ title: '描述', dataIndex: 'description', ellipsis: true, tooltip: true },
{ title: '创建人', dataIndex: 'createUserString', show: false, ellipsis: true, tooltip: true },
{ title: '创建时间', dataIndex: 'createTime', width: 180 },
{ title: '修改人', dataIndex: 'updateUserString', show: false, ellipsis: true, tooltip: true },
{ title: '修改时间', dataIndex: 'updateTime', width: 180, show: false },
{ title: '操作', slotName: 'action', width: 200, align: 'center', fixed: !isMobile() ? 'right' : undefined }
]
const queryForm = reactive({
description: undefined,
status: undefined,
sort: ['parentId,asc', 'sort,asc', 'createTime,desc']
})
const dataList = ref<DeptResp[]>([])
const loading = ref(false)
const tableRef = ref<InstanceType<typeof GiTable>>()
// 查询列表数据
const getDataList = async (query: DeptQuery = { ...queryForm }) => {
try {
loading.value = true
const res = await listDept(query)
dataList.value = res.data
await nextTick(() => {
tableRef.value?.tableRef?.expandAll(true)
})
} finally {
loading.value = false
}
}
// 查询
const search = () => {
getDataList()
}
// 重置
const reset = () => {
queryForm.description = undefined
queryForm.status = undefined
search()
}
// 删除
const onDelete = async (item: DeptResp) => {
await deleteDept(item.id)
Message.success('删除成功')
search()
}
const AddDeptModalRef = ref<InstanceType<typeof AddDeptModal>>()
// 新增
const onAdd = (id?: string) => {
AddDeptModalRef.value?.onAdd(id)
}
// 修改
const onUpdate = (item: DeptResp) => {
AddDeptModalRef.value?.onUpdate(item.id)
}
onMounted(() => {
search()
})
</script>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,7 @@
export interface DeptReq {
parentId: string
name: string
sort: number
description: string
status: 1 | 2
}