1
0
mirror of https://github.com/zclzone/vue-naive-admin.git synced 2026-01-23 08:00:22 +08:00
This commit is contained in:
zclzone
2023-12-07 21:55:23 +08:00
commit cfeb813b62
401 changed files with 11125 additions and 0 deletions

View 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>

View 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>

View 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>