mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2026-01-23 08:00:22 +08:00
init
This commit is contained in:
116
src/views/pms/resource/components/MenuTree.vue
Normal file
116
src/views/pms/resource/components/MenuTree.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:28:59
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<n-space vertical :size="12">
|
||||
<h3>菜单</h3>
|
||||
<div class="flex">
|
||||
<n-input v-model:value="pattern" placeholder="搜索" clearable />
|
||||
<n-button class="ml-12" type="primary" @click="handleAdd()">
|
||||
<i class="i-material-symbols:add mr-4 text-14" />
|
||||
新增
|
||||
</n-button>
|
||||
</div>
|
||||
|
||||
<n-tree
|
||||
:show-irrelevant-nodes="false"
|
||||
:pattern="pattern"
|
||||
:data="treeData"
|
||||
:render-prefix="renderPrefix"
|
||||
:render-suffix="renderSuffix"
|
||||
:on-update:selected-keys="onSelect"
|
||||
key-field="code"
|
||||
label-field="name"
|
||||
block-line
|
||||
default-expand-all
|
||||
/>
|
||||
</n-space>
|
||||
|
||||
<ResAddOrEdit ref="modalRef" :menus="treeData" @refresh="(data) => emit('refresh', data)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { withModifiers } from 'vue'
|
||||
import ResAddOrEdit from './ResAddOrEdit.vue'
|
||||
import { NButton } from 'naive-ui'
|
||||
import api from '../api'
|
||||
|
||||
defineProps({
|
||||
treeData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
currentMenu: {
|
||||
type: Object,
|
||||
default: () => null,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['refresh', 'update:currentMenu'])
|
||||
|
||||
const pattern = ref('')
|
||||
|
||||
const modalRef = ref(null)
|
||||
async function handleAdd(data = {}) {
|
||||
modalRef.value?.handleOpen({
|
||||
action: 'add',
|
||||
title: '新增菜单',
|
||||
row: { type: 'MENU', ...data },
|
||||
okText: '保存',
|
||||
})
|
||||
}
|
||||
|
||||
function onSelect(keys, option, { action, node }) {
|
||||
emit('update:currentMenu', action === 'select' ? node : null)
|
||||
}
|
||||
|
||||
function renderPrefix({ option }) {
|
||||
return h('i', { class: `${option.icon}?mask text-16` })
|
||||
}
|
||||
|
||||
function renderSuffix({ option }) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'primary',
|
||||
title: '新增下级菜单',
|
||||
size: 'tiny',
|
||||
onClick: withModifiers(() => handleAdd({ parentId: option.id }), ['stop']),
|
||||
},
|
||||
{ default: () => '新增' }
|
||||
),
|
||||
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'error',
|
||||
size: 'tiny',
|
||||
style: 'margin-left: 12px;',
|
||||
onClick: withModifiers(() => handleDelete(option), ['stop']),
|
||||
},
|
||||
{ default: () => '删除' }
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
function handleDelete(item) {
|
||||
$dialog.confirm({
|
||||
content: `确认删除【${item.name}】?`,
|
||||
async confirm() {
|
||||
$message.loading('正在删除', { key: 'deleteMenu' })
|
||||
await api.deletePermission(item.id)
|
||||
$message.success('删除成功', { key: 'deleteMenu' })
|
||||
emit('refresh')
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
32
src/views/pms/resource/components/QuestionLabel.vue
Normal file
32
src/views/pms/resource/components/QuestionLabel.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:29:05
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<span class="flex items-center">
|
||||
<n-popover v-if="content" trigger="hover">
|
||||
<template #trigger>
|
||||
<i class="i-material-symbols:help mr-4" />
|
||||
</template>
|
||||
<span>{{ content }}</span>
|
||||
</n-popover>
|
||||
{{ label }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
</script>
|
||||
201
src/views/pms/resource/components/ResAddOrEdit.vue
Normal file
201
src/views/pms/resource/components/ResAddOrEdit.vue
Normal file
@@ -0,0 +1,201 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:29:12
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<MeModal ref="modalRef">
|
||||
<n-form
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
require-mark-placement="left"
|
||||
:label-width="100"
|
||||
:model="modalForm"
|
||||
>
|
||||
<n-grid :cols="24" :x-gap="24">
|
||||
<n-form-item-gi :span="12" label="所属菜单" path="parentId">
|
||||
<n-tree-select
|
||||
v-model:value="modalForm.parentId"
|
||||
:options="menuOptions"
|
||||
label-field="name"
|
||||
key-field="id"
|
||||
placeholder="根菜单"
|
||||
clearable
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" path="name" :rule="required">
|
||||
<template #label>
|
||||
<QuestionLabel label="名称" content="标题" />
|
||||
</template>
|
||||
<n-input v-model:value="modalForm.name" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" path="code" :rule="required">
|
||||
<template #label>
|
||||
<QuestionLabel label="编码" content="如果是菜单则对应前端路由的name,使用大驼峰" />
|
||||
</template>
|
||||
<n-input v-model:value="modalForm.code" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" path="path">
|
||||
<template #label>
|
||||
<QuestionLabel label="路由地址" content="父级菜单可不填" />
|
||||
</template>
|
||||
<n-input v-model:value="modalForm.path" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" path="icon">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="菜单图标"
|
||||
content="如material-symbols:help,图标库地址: https://icones.js.org/collection/all"
|
||||
/>
|
||||
</template>
|
||||
<n-select v-model:value="modalForm.icon" :options="iconOptions" clearable filterable />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" path="layout">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="layout"
|
||||
content="对应layouts文件夹下的目录名, 为空则默认为 default"
|
||||
/>
|
||||
</template>
|
||||
<n-select v-model:value="modalForm.layout" :options="layoutOptions" clearable />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="24" path="component">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="组件路径"
|
||||
content="前端组件的路径,以 /src 开头,父级菜单可不填"
|
||||
/>
|
||||
</template>
|
||||
<n-select
|
||||
v-model:value="modalForm.component"
|
||||
:options="componentOptions"
|
||||
clearable
|
||||
filterable
|
||||
tag
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi :span="12" path="show">
|
||||
<template #label>
|
||||
<QuestionLabel label="显示状态" content="控制是否在菜单栏显示,不影响路由注册" />
|
||||
</template>
|
||||
<n-switch v-model:value="modalForm.show">
|
||||
<template #checked>显示</template>
|
||||
<template #unchecked>隐藏</template>
|
||||
</n-switch>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" path="enable">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="状态"
|
||||
content="如果是菜单,禁用后将不添加到路由表,无法进入此页面"
|
||||
/>
|
||||
</template>
|
||||
<n-switch v-model:value="modalForm.enable">
|
||||
<template #checked>启用</template>
|
||||
<template #unchecked>禁用</template>
|
||||
</n-switch>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12" path="enable">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="KeepAlive"
|
||||
content="设置keepAlive需将组件的name设置成当前菜单的code"
|
||||
/>
|
||||
</template>
|
||||
<n-switch v-model:value="modalForm.keepAlive">
|
||||
<template #checked>是</template>
|
||||
<template #unchecked>否</template>
|
||||
</n-switch>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi
|
||||
:span="12"
|
||||
label="排序"
|
||||
path="order"
|
||||
:rule="{
|
||||
type: 'number',
|
||||
required: true,
|
||||
message: '此为必填项',
|
||||
trigger: ['blur', 'change'],
|
||||
}"
|
||||
>
|
||||
<n-input-number v-model:value="modalForm.order" />
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</MeModal>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { MeModal } from '@/components'
|
||||
import QuestionLabel from './QuestionLabel.vue'
|
||||
import { useForm, useModal } from '@/composables'
|
||||
import api from '../api'
|
||||
import pagePathes from 'isme:page-pathes'
|
||||
import icons from 'isme:icons'
|
||||
|
||||
const props = defineProps({
|
||||
menus: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['refresh'])
|
||||
|
||||
const menuOptions = computed(() => {
|
||||
return [{ name: '根菜单', id: '', children: props.menus || [] }]
|
||||
})
|
||||
const componentOptions = pagePathes.map((path) => ({ label: path, value: path }))
|
||||
const iconOptions = icons.map((item) => ({
|
||||
label: () =>
|
||||
h('span', { class: 'flex items-center' }, [h('i', { class: item + ' text-18 mr-8' }), item]),
|
||||
value: item,
|
||||
}))
|
||||
const layoutOptions = [
|
||||
{ label: '默认-default', value: 'default' },
|
||||
{ label: '空白-empty', value: 'empty' },
|
||||
]
|
||||
const required = {
|
||||
required: true,
|
||||
message: '此为必填项',
|
||||
trigger: ['blur', 'change'],
|
||||
}
|
||||
|
||||
const defaultForm = { enable: true, show: true }
|
||||
const [modalFormRef, modalForm, validation] = useForm(defaultForm)
|
||||
const [modalRef, okLoading] = useModal()
|
||||
|
||||
const modalAction = ref('')
|
||||
function handleOpen(options = {}) {
|
||||
const { action, row = {}, ...rest } = options
|
||||
modalAction.value = action
|
||||
modalForm.value = { ...row }
|
||||
modalRef.value.open({ ...rest, onOk: onSave })
|
||||
}
|
||||
|
||||
async function onSave() {
|
||||
if (!(await validation())) return
|
||||
okLoading.value = true
|
||||
try {
|
||||
if (!modalForm.value.parentId) modalForm.value.parentId = null
|
||||
if (modalAction.value === 'add') {
|
||||
await api.addPermission(modalForm.value)
|
||||
} else if (modalAction.value === 'edit') {
|
||||
await api.savePermission(modalForm.value.id, modalForm.value)
|
||||
}
|
||||
okLoading.value = false
|
||||
$message.success('保存成功')
|
||||
emit('refresh', modalForm.value)
|
||||
} catch (error) {
|
||||
okLoading.value = false
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
handleOpen,
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user