菜单管理模块增删改查

This commit is contained in:
余菲 2022-05-27 15:05:48 +08:00
parent d4c0ffffef
commit 25bade7644
10 changed files with 316 additions and 169 deletions

View File

@ -1,7 +1,7 @@
import { defAxios as request } from '@/utils/http' import { defAxios as request } from '@/utils/http'
/** /**
* 获取菜单分页数据接口 * 获取菜单管理数据接口
* @returns 菜单分页数据 * @returns 菜单数据
*/ */
export function getMenuList(params) { export function getMenuList(params) {
return request({ return request({

View File

@ -44,7 +44,7 @@ export function useDataSource(propsRef, { getPaginationInfo, setPagination, setL
} }
} }
// 处理数据结构 // 处理数据结构
const resultInfo = res[listField] ? res[listField] : [] const resultInfo = res[listField] ? res[listField] : res
dataSourceRef.value = dataType === 'tree' ? dealTree(resultInfo) : resultInfo dataSourceRef.value = dataType === 'tree' ? dealTree(resultInfo) : resultInfo
setPagination({ setPagination({
[pageField]: currentPage, [pageField]: currentPage,
@ -74,7 +74,7 @@ export function useDataSource(propsRef, { getPaginationInfo, setPagination, setL
* @returns 返回树形结构数据 * @returns 返回树形结构数据
*/ */
function dealTree(info) { function dealTree(info) {
const tree = toTreeData(info.data, 'id', 'pid', 'children') const tree = toTreeData(info, 'id', 'pid', 'children')
return tree return tree
} }
@ -106,7 +106,6 @@ export function useDataSource(propsRef, { getPaginationInfo, setPagination, setL
} }
async function reFetch(opt) { async function reFetch(opt) {
console.log('opt', opt)
const { paginationSetting } = unref(propsRef) const { paginationSetting } = unref(propsRef)
const pageField = paginationSetting.pageField const pageField = paginationSetting.pageField
const sizeField = paginationSetting.sizeField const sizeField = paginationSetting.sizeField

View File

@ -1,10 +1,10 @@
<template> <template>
<div> <div>
<n-card> <n-card>
<headSearch :info="info" @search="handleSearch" />
<data-table <data-table
:columns="data.columns" :columns="columns"
:data="data.data" :data="data"
:pagination="data.pagination"
:request="loadDataTable" :request="loadDataTable"
:row-key="(row) => row.id" :row-key="(row) => row.id"
data-type="tree" data-type="tree"
@ -17,17 +17,26 @@
</data-table> </data-table>
</n-card> </n-card>
</div> </div>
<!-- 新增编辑弹窗 -->
<dept-modal
v-if="modalShow"
v-model:visible="modalShow"
:row="rowData"
@done="handleSearch"
/>
</template> </template>
<script> <script>
import headSearch from '@/components/Search/index.vue'
import dataTable from '@/components/DataTable/index.vue' import dataTable from '@/components/DataTable/index.vue'
import TableAction from '@/components/DataTable/tools/Action.vue' import TableAction from '@/components/DataTable/tools/Action.vue'
import { getDeptList } from '@/api/system/dept/index.js' import { getDeptList, deleteDept } from '@/api/system/dept/index.js'
import { h, onMounted, unref } from 'vue' import { h, unref, toRefs, ref, reactive } from 'vue'
import { reactive } from 'vue' import DeptModal from './components/DeptModal.vue'
import info from './info.js'
export default { export default {
name: 'MenuPage', name: 'MenuPage',
components: { dataTable }, components: { dataTable, DeptModal, headSearch},
setup() { setup() {
const data = reactive({ const data = reactive({
columns: [ columns: [
@ -108,30 +117,36 @@ export default {
} }
], ],
data: [], data: [],
pagination: { info: ref(info),
pageSize: 10 modalShow: false,
} rowData: {}
}) })
//
function handleModal() {
data.modalShow = true
}
//
function play(row) { function play(row) {
console.log(row) data.rowData = row
data.modalShow = true
} }
/** /**
* @description: 获取部门数据并做树形结构处理 * @description: 获取部门数据并做树形结构处理
* @return {*} * @return {*}
*/ */
async function fetchList() { const params = ref({})
const params = {
page: 1, const tableRef = ref()
limit: 10
function handleSearch(data) {
params.value = {
...data
} }
const res = await getDeptList(params) tableRef.value.reFetch({ ...unref(params) })
data.data = res.data
} }
const params = reactive({
name: 'xiaoMa'
})
const loadDataTable = async(res) => { const loadDataTable = async(res) => {
const _params = { const _params = {
@ -141,11 +156,7 @@ export default {
return await getDeptList(_params) return await getDeptList(_params)
} }
onMounted(() => { return { ...toRefs(data), loadDataTable, handleSearch, handleModal }
fetchList()
})
return { data, loadDataTable }
} }
} }
</script> </script>

View File

@ -0,0 +1,30 @@
const data = [
{
label: '部门名称',
key: 'name',
props: {
placeholder: '请输入部门名称'
}
}
// {
// label: '角色类型',
// type: 'select',
// key: 'op',
// props: {
// options: [{
// label: 11, value: 1
// }]
// }
// },
// {
// label: '角色类型',
// type: 'date',
// key: 'date',
// props: {
// type: 'date'
// }
// }
]
export default data

View File

@ -6,12 +6,12 @@
:on-negative-click="handleClose" :on-negative-click="handleClose"
> >
<template #Context> <template #Context>
<n-form ref="formRef" :model="menuForm" :rules="menuRules" require-mark-placement="left" :label-width="80" label-placement="left"> <n-form ref="formRef" :model="form" :rules="rules" require-mark-placement="left" :label-width="80" label-placement="left">
<n-grid x-gap="12" :cols="2"> <n-grid x-gap="12" :cols="2">
<n-gi> <n-gi>
<n-form-item label="上级菜单" path="pid"> <n-form-item label="上级菜单" path="pid">
<n-select <n-select
v-model:value="menuForm.pid" v-model:value="form.pid"
placeholder="请选择上级菜单" placeholder="请选择上级菜单"
:options="getMenuList" :options="getMenuList"
/> />
@ -19,8 +19,8 @@
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="菜单类型" path="type"> <n-form-item label="菜单类型" path="type">
<n-radio-group v-model:value="menuForm.type" name="type"> <n-radio-group v-model:value="form.type" name="type">
<n-radio v-for="item in typeOptions" :key="`type_${item.key}`" :value="item.key"> <n-radio v-for="item in typeOptions" :key="item.key" :value="item.key">
{{ item.label }} {{ item.label }}
</n-radio> </n-radio>
</n-radio-group> </n-radio-group>
@ -28,13 +28,13 @@
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="菜单名称" path="title"> <n-form-item label="菜单名称" path="title">
<n-input v-model:value="menuForm.title" placeholder="请输入菜单名称" /> <n-input v-model:value="form.title" placeholder="请输入菜单名称" />
</n-form-item> </n-form-item>
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="打开方式" path="target"> <n-form-item label="打开方式" path="target">
<n-radio-group v-model:value="menuForm.target" name="target"> <n-radio-group v-model:value="form.target" name="target">
<n-radio v-for="item in openOptions" :key="`target_${item.key}`" :value="item.key"> <n-radio v-for="item in openOptions" :key="item.key" :value="item.key">
{{ item.label }} {{ item.label }}
</n-radio> </n-radio>
</n-radio-group> </n-radio-group>
@ -42,33 +42,33 @@
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="菜单图标" path="icon"> <n-form-item label="菜单图标" path="icon">
<n-input v-model:value="menuForm.icon" placeholder="请选择菜单图标" /> <n-input v-model:value="form.icon" :disabled="form.type === 1" placeholder="请选择菜单图标" />
</n-form-item> </n-form-item>
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="权限标识" path="permission"> <n-form-item label="权限标识" path="permission">
<n-input v-model:value="menuForm.permission" placeholder="请输入权限标识" /> <n-input v-model:value="form.permission" :disabled="form.type === 0" placeholder="请输入权限标识" />
</n-form-item> </n-form-item>
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="路由地址" path="path"> <n-form-item label="路由地址" path="path">
<n-input v-model:value="menuForm.path" placeholder="请输入路由地址" /> <n-input v-model:value="form.path" :disabled="form.type === 1" placeholder="请输入路由地址" />
</n-form-item> </n-form-item>
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="排序号" path="sort"> <n-form-item label="排序号" path="sort">
<n-input v-model:value="menuForm.sort" placeholder="请输入排序号" /> <n-input-number v-model:value="form.sort" placeholder="请输入排序号" />
</n-form-item> </n-form-item>
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="组件路径" path="component"> <n-form-item label="组件路径" path="component">
<n-input v-model:value="menuForm.component" placeholder="请输入组件路径" /> <n-input v-model:value="form.component" :disabled="form.type === 1" placeholder="请输入组件路径" />
</n-form-item> </n-form-item>
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="是否可见" path="hide"> <n-form-item label="是否可见" path="hide">
<n-radio-group v-model:value="menuForm.hide" name="hide"> <n-radio-group v-model:value="form.hide" name="hide">
<n-radio v-for="item in visibleOptions" :key="`hide_${item.key}`" :value="item.key"> <n-radio v-for="item in visibleOptions" :key="item.key" :value="item.key">
{{ item.label }} {{ item.label }}
</n-radio> </n-radio>
</n-radio-group> </n-radio-group>
@ -76,8 +76,8 @@
</n-gi> </n-gi>
<n-gi> <n-gi>
<n-form-item label="菜单状态" path="status"> <n-form-item label="菜单状态" path="status">
<n-radio-group v-model:value="menuForm.status" name="status"> <n-radio-group v-model:value="form.status" name="status">
<n-radio v-for="item in statusOptions" :key="`status_${item.key}`" :value="item.key"> <n-radio v-for="item in statusOptions" :key="item.key" :value="item.key">
{{ item.label }} {{ item.label }}
</n-radio> </n-radio>
</n-radio-group> </n-radio-group>
@ -91,9 +91,9 @@
</template> </template>
<script> <script>
import { defineComponent, computed, reactive, toRefs, ref } from 'vue' import { defineComponent, computed, reactive, toRefs } from 'vue'
import Modal from '@/components/Modal/index.vue' import Modal from '@/components/Modal/index.vue'
import { addMenu } from '@/api/system/menu/index.js' import { addMenu, editMenu } from '@/api/system/menu/index.js'
export default defineComponent({ export default defineComponent({
name: 'MenuModal', name: 'MenuModal',
components: { Modal }, components: { Modal },
@ -114,59 +114,48 @@ export default defineComponent({
} }
}, },
emits: { emits: {
'update:visible': null 'update:visible': null,
'done': null
}, },
setup(props, { emit }) { setup(props, { emit }) {
const data = reactive({ const data = reactive({
menuForm: { form: {},
pid: null, rules: {
type: '0', title: [{
title: '',
target: '0',
icon: '',
permission: '',
path: '',
sort: '',
component: '',
hide: '0',
status: '1'
},
menuRules: {
title: {
required: true, required: true,
message: '请输入菜单名称', message: '请输入菜单名称',
trigger: ['blur'] trigger: 'blur'
}, }],
sort: { sort: [{
required: true, required: true,
message: '请输入排序号', message: '请输入排序号',
trigger: ['blur'] trigger: 'blur'
} }]
}, },
typeOptions: [ typeOptions: [
{ key: '0', label: '菜单' }, { key: 0, label: '菜单' },
{ key: '1', label: '按钮' } { key: 1, label: '按钮' }
], ],
openOptions: [ openOptions: [
{ key: '0', label: '组件' }, { key: '1', label: '内部' },
{ key: '1', label: '内链' }, { key: '2', label: '外部' }
{ key: '2', label: '外链' }
], ],
visibleOptions: [ visibleOptions: [
{ key: '0', label: '可见' }, { key: 0, label: '可见' },
{ key: '1', label: '不可见' } { key: 1, label: '不可见' }
], ],
statusOptions: [ statusOptions: [
{ key: '1', label: '在用' }, { key: 1, label: '在用' },
{ key: '2', label: '停用' } { key: 2, label: '停用' }
] ],
menuList: []
}) })
const getMenuList = computed(() => { const getMenuList = computed(() => {
const list = props.menuList.map((item) => { const list = props.menuList.map((item) => {
const menu = { const menu = {
label: item.title, label: item.title,
value: item.pid value: item.id
} }
return menu return menu
}) })
@ -174,9 +163,14 @@ export default defineComponent({
}) })
const getModalOptions = computed(() => { const getModalOptions = computed(() => {
const row = props.data
if (props.data.pid === 0) {
row.pid = null
}
return { return {
show: props.visible, show: props.visible,
title: props.data ? '修改菜单' : '新建菜单', title: props.data.title ? '修改菜单' : '添加菜单',
form: Object.assign(data.form, row),
width: 700, width: 700,
negativeText: '取消', negativeText: '取消',
positiveText: '确认' positiveText: '确认'
@ -187,31 +181,47 @@ export default defineComponent({
const handleClose = () => { const handleClose = () => {
emit('update:visible', false) emit('update:visible', false)
} }
const formRef = ref()
const handleConfirm = () => {
formRef.value?.validate((errors) => {
if (!errors) {
const params = {
...data.menuForm
}
addMenu(params)
.then(res => {
handleClose()
})
} else {
$message.error('清先完成校验')
}
})
return false
}
return { return {
...toRefs(data), ...toRefs(data),
formRef,
getMenuList, getMenuList,
getModalOptions, getModalOptions,
handleClose, handleClose
handleConfirm }
},
methods: {
//
handleConfirm() {
const type = this.data.title ? 'add' : 'edit'
this.$refs.formRef.validate((errors) => {
if (!errors) {
if (type === 'add') {
addMenu(this.form).then(res => {
if (res.code === 0) {
this.handleClose()
this.$emit('done')
$message.success(res.msg)
} else {
$message.error(res.msg)
}
}).catch(e => {
console.log(e)
})
} else if (type === 'edit') {
editMenu(this.form).then(res => {
if (res.code === 0) {
this.handleClose()
this.$emit('done')
$message.success(res.msg)
} else {
$message.error(res.msg)
}
})
}
} else {
$message.error('请完善必填信息')
}
})
} }
} }
}) })

View File

@ -1,29 +1,41 @@
<template> <template>
<div> <div>
<n-card> <n-card>
<data-table :columns="data.columns" :pagination="false" :data="data.data" size="large" @fetch-success="getFechData"> <headSearch :info="info" @search="handleSearch" />
<data-table
ref="tableRef"
:columns="columns"
:pagination="false"
data-type="tree"
:request="loadDataTable"
>
<template #tableTitle> <template #tableTitle>
<n-button type="primary" @click="handlleRoleAdd"> <n-button type="primary" @click="handlleRoleAdd"> 添加菜单 </n-button>
添加菜单
</n-button>
</template> </template>
</data-table> </data-table>
</n-card> </n-card>
</div> </div>
<RoleModal v-model:visible="data.modalShow" :data="data.selectRow" :menu-list="data.data" /> <MenuModal
v-model:visible="modalShow"
:data="rowData"
:menu-list="menuList"
@done="handleSearch"
/>
</template> </template>
<script> <script>
import headSearch from '@/components/Search/index.vue'
import dataTable from '@/components/DataTable/index.vue' import dataTable from '@/components/DataTable/index.vue'
import RoleModal from './components/MenuModal.vue' import MenuModal from './components/MenuModal.vue'
import TableTags from '@/components/DataTable/tools/Tags.vue'
import Action from '@/components/DataTable/tools/Action.vue' import Action from '@/components/DataTable/tools/Action.vue'
import { getMenuList } from '@/api/system/index.js' import { getMenu, getMenuList, deleteMenu } from '@/api/system/menu/index.js'
import { h, onMounted } from 'vue' import { h, ref, unref, reactive, toRefs, onMounted } from 'vue'
import { reactive } from 'vue' import info from './info.js'
export default { export default {
name: 'MenuPage', name: 'MenuPage',
components: { dataTable, RoleModal }, components: { dataTable, MenuModal, headSearch },
setup() { setup() {
const data = reactive({ const data = reactive({
columns: [ columns: [
@ -37,13 +49,22 @@ export default {
title: '菜单类型', title: '菜单类型',
key: 'type', key: 'type',
align: 'center', align: 'center',
width: 100 width: 100,
render(row) {
return h(TableTags, {
data: row.type,
filters: [
{
key: 0,
label: '菜单'
}, },
{ {
title: '请求方式', key: 1,
key: 'method', label: '节点'
align: 'center', }
width: 100 ]
})
}
}, },
{ {
title: '路由地址', title: '路由地址',
@ -57,17 +78,26 @@ export default {
align: 'center', align: 'center',
width: 200 width: 200
}, },
{
title: '权限标识',
key: 'permission',
align: 'center',
width: 200
},
{ {
title: '状态', title: '状态',
key: 'status', key: 'status',
align: 'center', align: 'center',
width: 100 width: 100,
render(row) {
return h(TableTags, {
data: row.status,
filters: [
{
key: 1,
label: '在用'
},
{
key: 2,
label: '停用'
}
]
})
}
}, },
{ {
title: '排序', title: '排序',
@ -76,10 +106,25 @@ export default {
width: 100 width: 100
}, },
{ {
title: '可见', title: '是否可见',
key: 'hide', key: 'hide',
align: 'center', align: 'center',
width: 100 width: 100,
render(row) {
return h(TableTags, {
data: row.hide,
filters: [
{
key: 1,
label: '可见'
},
{
key: 2,
label: '不可见'
}
]
})
}
}, },
{ {
title: '创建时间', title: '创建时间',
@ -100,18 +145,29 @@ export default {
type: 'button', type: 'button',
props: { props: {
type: 'primary', type: 'primary',
onClick: play.bind(null, row) onClick: add.bind(null, row)
}, },
auth: 'basic_list' auth: 'basic_list'
}, },
{ {
label: '修改', label: '修改',
type: 'button',
props: {
type: 'primary',
onClick: play.bind(null, row)
},
auth: 'basic_list' auth: 'basic_list'
}, },
{ {
label: '删除', label: '删除',
type: 'popconfirm', type: 'popconfirm',
auth: 'basic_list' auth: 'basic_list',
tip: '确定删除这条数据吗?',
props: {
negativeText: '取消',
positiveText: '确认',
onPositiveClick: deleteSingle.bind(null, row.id)
}
} }
], ],
align: 'center' align: 'center'
@ -119,48 +175,94 @@ export default {
} }
} }
], ],
data: [], info: ref(info),
modalShow: false, modalShow: false,
selectRow: null menuList: [],
rowData: {
status: 1,
type: 0,
hide: 0
}
}) })
function play(row) { //
console.log(row) async function getMenuAll() {
const list = await getMenu()
data.menuList = list.data
} }
onMounted(() => {
getMenuAll()
})
/** /**
* @description: 获取菜单数据 * @description: 获取菜单数据
* @return {*} * @return {*}
*/ */
async function fetchList() { const params = ref({})
const res = await getMenuList() const tableRef = ref()
data.data = res.data
function handleSearch(data) {
params.value = {
...data
} }
tableRef.value.reFetch({ ...unref(params) })
}
const loadDataTable = async(res) => {
const _params = {
...unref(params),
...res
}
return await getMenuList(_params)
}
//
function handlleRoleAdd() { function handlleRoleAdd() {
data.modalShow = true data.modalShow = true
data.rowData = {}
}
//
function add(row) {
data.modalShow = true
data.rowData = {}
data.rowData.pid = row.id
}
//
function play(row) {
data.rowData = row
data.modalShow = true
}
//
function deleteSingle(id) {
deleteData(id)
} }
/** //
* @description: 获取表单数据 function deleteData(id) {
* @param {*} data deleteMenu(id)
* @return {*} .then((res) => {
*/ if (res.code === 0) {
function getFechData(data) { handleSearch({})
$message.success(res.msg)
} else {
$message.error(res.msg)
} }
onMounted(() => {
fetchList()
}) })
.catch((e) => {
console.log(e)
})
}
return { return {
data, ...toRefs(data),
tableRef,
loadDataTable,
handleSearch,
handlleRoleAdd, handlleRoleAdd,
getFechData deleteSingle
} }
} }
} }
</script> </script>
<style scoped lang='scss'> <style scoped lang='scss'>
</style> </style>

View File

@ -0,0 +1,12 @@
const data = [
{
label: '菜单名称',
key: 'title',
props: {
placeholder: '请输入菜单名称'
}
}
]
export default data

View File

@ -6,24 +6,6 @@ const data = [
placeholder: '请输入角色名称' placeholder: '请输入角色名称'
} }
} }
// {
// label: '角色类型',
// type: 'select',
// key: 'op',
// props: {
// options: [{
// label: 11, value: 1
// }]
// }
// },
// {
// label: '角色类型',
// type: 'date',
// key: 'date',
// props: {
// type: 'date'
// }
// }
] ]
export default data export default data

View File

@ -239,6 +239,8 @@ export default defineComponent({
} }
}) })
} }
} else {
$message.error('请完善必填信息')
} }
}) })
} }

View File

@ -268,7 +268,6 @@ export default {
} }
return { return {
data,
loadDataTable, loadDataTable,
handleModal, handleModal,
...toRefs(data), ...toRefs(data),