feat: 菜单管理

This commit is contained in:
kailong321200875
2023-08-05 17:43:24 +08:00
parent 28d0785be8
commit c72b3a33aa
16 changed files with 681 additions and 33 deletions

View File

@@ -11,8 +11,8 @@ import {
saveDepartmentApi,
deleteDepartmentApi
} from '@/api/department'
import type { DepartmentItem } from '@/api/department/types'
import { useTable } from '@/hooks/web/useTable'
import { TableData } from '@/api/table/types'
import { ref, unref, reactive } from 'vue'
import Write from './components/Write.vue'
import Detail from './components/Detail.vue'
@@ -238,7 +238,7 @@ const { allSchemas } = useCrudSchemas(crudSchemas)
const dialogVisible = ref(false)
const dialogTitle = ref('')
const currentRow = ref<TableData | null>(null)
const currentRow = ref<DepartmentItem | null>(null)
const actionType = ref('')
const AddAction = () => {
@@ -250,16 +250,18 @@ const AddAction = () => {
const delLoading = ref(false)
const delData = async (row: TableData | null) => {
const delData = async (row: DepartmentItem | null) => {
const elTableExpose = await getElTableExpose()
ids.value = row ? [row.id] : elTableExpose?.getSelectionRows().map((v: TableData) => v.id) || []
ids.value = row
? [row.id]
: elTableExpose?.getSelectionRows().map((v: DepartmentItem) => v.id) || []
delLoading.value = true
await delList(unref(ids).length).finally(() => {
delLoading.value = false
})
}
const action = (row: TableData, type: string) => {
const action = (row: DepartmentItem, type: string) => {
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
actionType.value = type
currentRow.value = row

View File

@@ -1,11 +1,11 @@
<script setup lang="ts">
import { PropType } from 'vue'
import type { TableData } from '@/api/table/types'
import { DepartmentItem } from '@/api/department/types'
import { Descriptions, DescriptionsSchema } from '@/components/Descriptions'
defineProps({
currentRow: {
type: Object as PropType<Nullable<TableData>>,
type: Object as PropType<Nullable<DepartmentItem>>,
default: () => null
},
detailSchema: {

View File

@@ -2,14 +2,14 @@
import { Form, FormSchema } from '@/components/Form'
import { useForm } from '@/hooks/web/useForm'
import { PropType, reactive, watch } from 'vue'
import { TableData } from '@/api/table/types'
import { useValidator } from '@/hooks/web/useValidator'
import { DepartmentItem } from '@/api/department/types'
const { required } = useValidator()
const props = defineProps({
currentRow: {
type: Object as PropType<Nullable<TableData>>,
type: Object as PropType<Nullable<DepartmentItem>>,
default: () => null
},
formSchema: {

View File

@@ -0,0 +1,193 @@
<script setup lang="tsx">
import { reactive, ref, unref } from 'vue'
import { getMenuListApi } from '@/api/menu'
import { useTable } from '@/hooks/web/useTable'
import { useI18n } from '@/hooks/web/useI18n'
import { Table, TableColumn } from '@/components/Table'
import { ElButton, ElTag } from 'element-plus'
import { Icon } from '@/components/Icon'
import { Search } from '@/components/Search'
import { FormSchema } from '@/components/Form'
import { ContentWrap } from '@/components/ContentWrap'
import Write from './components/Write.vue'
import { Dialog } from '@/components/Dialog'
const { t } = useI18n()
const { tableRegister, tableState, tableMethods } = useTable({
fetchDataApi: async () => {
const res = await getMenuListApi()
return {
list: res.data.list || []
}
}
})
const { dataList, loading } = tableState
const { getList } = tableMethods
const tableColumns = reactive<TableColumn[]>([
{
field: 'index',
label: t('userDemo.index'),
type: 'index'
},
{
field: 'meta.title',
label: t('menu.menuName')
},
{
field: 'meta.icon',
label: t('menu.icon'),
slots: {
default: (data: any) => {
const icon = data[0].row.meta.icon
if (icon) {
return (
<>
<Icon icon={icon} />
</>
)
} else {
return null
}
}
}
},
{
field: 'meta.permission',
label: t('menu.permission'),
slots: {
default: (data: any) => {
const permission = data[0].row.meta.permission
return permission ? <>{permission.join(', ')}</> : null
}
}
},
{
field: 'component',
label: t('menu.component'),
slots: {
default: (data: any) => {
const component = data[0].row.component
return <>{component === '#' ? '顶级目录' : component === '##' ? '子目录' : component}</>
}
}
},
{
field: 'path',
label: t('menu.path')
},
{
field: 'status',
label: t('menu.status'),
slots: {
default: (data: any) => {
return (
<>
<ElTag type={data[0].row.status === 0 ? 'danger' : 'success'}>
{data[0].row.status === 1 ? t('userDemo.enable') : t('userDemo.disable')}
</ElTag>
</>
)
}
}
},
{
field: 'action',
label: t('userDemo.action'),
width: 240,
slots: {
default: (data: any) => {
const row = data[0].row
return (
<>
<ElButton type="primary" onClick={() => action(row, 'edit')}>
{t('exampleDemo.edit')}
</ElButton>
<ElButton type="danger">{t('exampleDemo.del')}</ElButton>
</>
)
}
}
}
])
const searchSchema = reactive<FormSchema[]>([
{
field: 'meta.title',
label: t('menu.menuName'),
component: 'Input'
}
])
const searchParams = ref({})
const setSearchParams = (data: any) => {
searchParams.value = data
getList()
}
const dialogVisible = ref(false)
const dialogTitle = ref('')
const currentRow = ref()
const actionType = ref('')
const writeRef = ref<ComponentRef<typeof Write>>()
const saveLoading = ref(false)
const action = (row: any, type: string) => {
dialogTitle.value = t(type === 'edit' ? 'exampleDemo.edit' : 'exampleDemo.detail')
actionType.value = type
currentRow.value = row
dialogVisible.value = true
}
const AddAction = () => {
dialogTitle.value = t('exampleDemo.add')
currentRow.value = undefined
dialogVisible.value = true
actionType.value = ''
}
const save = async () => {
const write = unref(writeRef)
const formData = await write?.submit()
if (formData) {
saveLoading.value = true
setTimeout(() => {
saveLoading.value = false
dialogVisible.value = false
}, 1000)
}
}
</script>
<template>
<ContentWrap>
<Search :schema="searchSchema" @reset="setSearchParams" @search="setSearchParams" />
<div class="mb-10px">
<ElButton type="primary" @click="AddAction">{{ t('exampleDemo.add') }}</ElButton>
</div>
<Table
:columns="tableColumns"
default-expand-all
node-key="id"
:data="dataList"
:loading="loading"
@register="tableRegister"
/>
</ContentWrap>
<Dialog v-model="dialogVisible" :title="dialogTitle">
<Write v-if="actionType !== 'detail'" ref="writeRef" :current-row="currentRow" />
<template #footer>
<ElButton v-if="actionType !== 'detail'" type="primary" :loading="saveLoading" @click="save">
{{ t('exampleDemo.save') }}
</ElButton>
<ElButton @click="dialogVisible = false">{{ t('dialogDemo.close') }}</ElButton>
</template>
</Dialog>
</template>

View File

@@ -0,0 +1,164 @@
<script setup lang="ts">
import { Form, FormSchema } from '@/components/Form'
import { useForm } from '@/hooks/web/useForm'
import { PropType, reactive, watch } from 'vue'
import { useValidator } from '@/hooks/web/useValidator'
import { useI18n } from '@/hooks/web/useI18n'
const { t } = useI18n()
const { required } = useValidator()
const props = defineProps({
currentRow: {
type: Object as PropType<any>,
default: () => null
}
})
const formSchema = reactive<FormSchema[]>([
{
field: 'meta.title',
label: t('menu.menuName'),
component: 'Input'
},
{
field: 'component',
label: t('menu.component'),
component: 'Input'
},
{
field: 'name',
label: t('menu.name'),
component: 'Input'
},
{
field: 'meta.icon',
label: t('menu.icon'),
component: 'Input'
},
{
field: 'path',
label: t('menu.path'),
component: 'Input'
},
{
field: 'status',
label: t('menu.status'),
component: 'Select',
componentProps: {
options: [
{
label: t('userDemo.disable'),
value: 0
},
{
label: t('userDemo.enable'),
value: 1
}
]
}
},
{
field: 'meta.activeMenu',
label: t('menu.activeMenu'),
component: 'Input'
},
{
field: 'meta.permission',
label: t('menu.permission'),
component: 'CheckboxGroup',
componentProps: {
options: [
{
label: 'add',
value: 'add'
},
{
label: 'edit',
value: 'edit'
},
{
label: 'delete',
value: 'delete'
}
]
}
},
{
field: 'meta.hidden',
label: t('menu.hidden'),
component: 'Switch'
},
{
field: 'meta.alwaysShow',
label: t('menu.alwaysShow'),
component: 'Switch'
},
{
field: 'meta.noCache',
label: t('menu.noCache'),
component: 'Switch'
},
{
field: 'meta.breadcrumb',
label: t('menu.breadcrumb'),
component: 'Switch'
},
{
field: 'meta.affix',
label: t('menu.affix'),
component: 'Switch'
},
{
field: 'meta.noTagsView',
label: t('menu.noTagsView'),
component: 'Switch'
},
{
field: 'canTo',
label: t('menu.canTo'),
component: 'Switch'
}
])
const rules = reactive({
component: [required()],
path: [required()],
'meta.title': [required()]
})
const { formRegister, formMethods } = useForm()
const { setValues, getFormData, getElFormExpose } = formMethods
const submit = async () => {
const elForm = await getElFormExpose()
const valid = await elForm?.validate().catch((err) => {
console.log(err)
})
if (valid) {
const formData = getFormData()
return formData
}
}
watch(
() => props.currentRow,
(currentRow) => {
if (!currentRow) return
setValues(currentRow)
},
{
deep: true,
immediate: true
}
)
defineExpose({
submit
})
</script>
<template>
<Form :rules="rules" @register="formRegister" :schema="formSchema" />
</template>

View File

@@ -21,6 +21,7 @@ const props = defineProps({
const rules = reactive({
username: [required()],
account: [required()],
'department.id': [required()],
role: [required()],
email: [required()],
createTime: [required()]