mirror of
https://github.com/zclzone/vue-naive-admin.git
synced 2025-05-01 22:59:01 +08:00
Compare commits
No commits in common. "0141c0287e3b7dc4c1150f71b0221329ae9ef8cc" and "961ad6af7b906b27b7d3d8e9ec6c044248c0a7da" have entirely different histories.
0141c0287e
...
961ad6af7b
@ -1,7 +1,7 @@
|
||||
/**********************************
|
||||
* @Author: Ronnie Zhang
|
||||
* @LastEditor: Ronnie Zhang
|
||||
* @LastEditTime: 2024/04/01 15:52:04
|
||||
* @LastEditTime: 2023/12/05 21:28:47
|
||||
* @Email: zclzone@outlook.com
|
||||
* Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
**********************************/
|
||||
@ -11,7 +11,7 @@ import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getMenuTree: () => request.get('/permission/menu/tree'),
|
||||
getButtons: ({ parentId }) => request.get(`/permission/button/${parentId}`),
|
||||
getButtonAndApi: (parentId) => request.get(`/permission/button-and-api/${parentId}`),
|
||||
getComponents: () => axios.get(`${import.meta.env.VITE_PUBLIC_PATH}components.json`),
|
||||
addPermission: (data) => request.post('/permission', data),
|
||||
savePermission: (id, data) => request.patch(`/permission/${id}`, data),
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2024/04/01 15:51:34
|
||||
- @LastEditTime: 2023/12/05 21:28:59
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2024/04/01 15:52:31
|
||||
- @LastEditTime: 2023/12/12 09:03:43
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
@ -20,7 +20,6 @@
|
||||
<n-tree-select
|
||||
v-model:value="modalForm.parentId"
|
||||
:options="menuOptions"
|
||||
:disabled="parentIdDisabled"
|
||||
label-field="name"
|
||||
key-field="id"
|
||||
placeholder="根菜单"
|
||||
@ -40,7 +39,6 @@
|
||||
<n-input v-model:value="modalForm.code" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi
|
||||
v-if="modalForm.type === 'MENU'"
|
||||
:span="12"
|
||||
path="path"
|
||||
:rule="{
|
||||
@ -60,7 +58,7 @@
|
||||
</template>
|
||||
<n-input v-model:value="modalForm.path" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="modalForm.type === 'MENU'" :span="12" path="icon">
|
||||
<n-form-item-gi :span="12" path="icon">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="菜单图标"
|
||||
@ -69,7 +67,7 @@
|
||||
</template>
|
||||
<n-select v-model:value="modalForm.icon" :options="iconOptions" clearable filterable />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="modalForm.type === 'MENU'" :span="12" path="layout">
|
||||
<n-form-item-gi :span="12" path="layout">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="layout"
|
||||
@ -78,7 +76,7 @@
|
||||
</template>
|
||||
<n-select v-model:value="modalForm.layout" :options="layoutOptions" clearable />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="modalForm.type === 'MENU'" :span="24" path="component">
|
||||
<n-form-item-gi :span="24" path="component">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="组件路径"
|
||||
@ -94,7 +92,7 @@
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
|
||||
<n-form-item-gi v-if="modalForm.type === 'MENU'" :span="12" path="show">
|
||||
<n-form-item-gi :span="12" path="show">
|
||||
<template #label>
|
||||
<QuestionLabel label="显示状态" content="控制是否在菜单栏显示,不影响路由注册" />
|
||||
</template>
|
||||
@ -115,7 +113,7 @@
|
||||
<template #unchecked>禁用</template>
|
||||
</n-switch>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="modalForm.type === 'MENU'" :span="12" path="enable">
|
||||
<n-form-item-gi :span="12" path="enable">
|
||||
<template #label>
|
||||
<QuestionLabel
|
||||
label="KeepAlive"
|
||||
@ -128,7 +126,6 @@
|
||||
</n-switch>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi
|
||||
v-if="modalForm.type === 'MENU'"
|
||||
:span="12"
|
||||
label="排序"
|
||||
path="order"
|
||||
@ -189,12 +186,10 @@ const [modalFormRef, modalForm, validation] = useForm(defaultForm)
|
||||
const [modalRef, okLoading] = useModal()
|
||||
|
||||
const modalAction = ref('')
|
||||
const parentIdDisabled = ref(false)
|
||||
function handleOpen(options = {}) {
|
||||
const { action, row = {}, ...rest } = options
|
||||
modalAction.value = action
|
||||
modalForm.value = { ...row }
|
||||
parentIdDisabled.value = !!row.parentId
|
||||
modalRef.value.open({ ...rest, onOk: onSave })
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,12 @@
|
||||
<template v-if="currentMenu">
|
||||
<div class="flex justify-between">
|
||||
<h3 class="mb-12">{{ currentMenu.name }}</h3>
|
||||
<n-button size="small" type="primary" @click="handleEdit(currentMenu)">
|
||||
<n-button
|
||||
:disabled="!currentMenu"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="handleEdit(currentMenu)"
|
||||
>
|
||||
<i class="i-material-symbols:edit-outline mr-4 text-14" />
|
||||
编辑
|
||||
</n-button>
|
||||
@ -59,22 +64,6 @@
|
||||
{{ currentMenu.order ?? '--' }}
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
|
||||
<div class="mt-32 flex justify-between">
|
||||
<h3 class="mb-12">按钮</h3>
|
||||
<n-button size="small" type="primary" @click="handleAddBtn">
|
||||
<i class="i-fe:plus mr-4 text-14" />
|
||||
新增
|
||||
</n-button>
|
||||
</div>
|
||||
|
||||
<MeCrud
|
||||
ref="$table"
|
||||
:columns="btnsColumns"
|
||||
:scroll-x="-1"
|
||||
:get-data="api.getButtons"
|
||||
:query-items="{ parentId: currentMenu.id }"
|
||||
></MeCrud>
|
||||
</template>
|
||||
<n-empty v-else class="h-450 f-c-c" size="large" description="请选择菜单查看详情" />
|
||||
</div>
|
||||
@ -84,19 +73,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NButton, NSwitch } from 'naive-ui'
|
||||
import MenuTree from './components/MenuTree.vue'
|
||||
import ResAddOrEdit from './components/ResAddOrEdit.vue'
|
||||
import { MeCrud } from '@/components'
|
||||
import api from './api'
|
||||
|
||||
const treeData = ref([])
|
||||
const treeLoading = ref(false)
|
||||
async function initData(data) {
|
||||
if (data?.type === 'BUTTON') {
|
||||
$table.value.handleSearch()
|
||||
return
|
||||
}
|
||||
treeLoading.value = true
|
||||
const res = await api.getMenuTree()
|
||||
treeData.value = res?.data || []
|
||||
@ -117,127 +100,4 @@ function handleEdit(item = {}) {
|
||||
okText: '保存',
|
||||
})
|
||||
}
|
||||
|
||||
const btnsColumns = [
|
||||
{ title: '名称', key: 'name' },
|
||||
{ title: '编码', key: 'code' },
|
||||
{
|
||||
title: '状态',
|
||||
key: 'enable',
|
||||
render: (row) =>
|
||||
h(
|
||||
NSwitch,
|
||||
{
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row.enable,
|
||||
loading: !!row.enableLoading,
|
||||
onUpdateValue: () => handleEnable(row),
|
||||
},
|
||||
{
|
||||
checked: () => '启用',
|
||||
unchecked: () => '停用',
|
||||
}
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 320,
|
||||
align: 'right',
|
||||
fixed: 'right',
|
||||
render(row) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
style: 'margin-left: 12px;',
|
||||
onClick: () => handleEditBtn(row),
|
||||
},
|
||||
{
|
||||
default: () => '编辑',
|
||||
icon: () => h('i', { class: 'i-material-symbols:edit-outline text-14' }),
|
||||
}
|
||||
),
|
||||
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 12px;',
|
||||
onClick: () => handleDeleteBtn(row.id),
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: () => h('i', { class: 'i-material-symbols:delete-outline text-14' }),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
const $table = ref(null)
|
||||
|
||||
watch(
|
||||
() => currentMenu.value,
|
||||
async (v) => {
|
||||
await nextTick()
|
||||
if (v) $table.value.handleSearch()
|
||||
}
|
||||
)
|
||||
|
||||
function handleAddBtn() {
|
||||
modalRef.value?.handleOpen({
|
||||
action: 'add',
|
||||
title: '新增按钮',
|
||||
row: { type: 'BUTTON', parentId: currentMenu.value.id },
|
||||
okText: '保存',
|
||||
})
|
||||
}
|
||||
|
||||
function handleEditBtn(row) {
|
||||
modalRef.value?.handleOpen({
|
||||
action: 'edit',
|
||||
title: '编辑按钮 - ' + row.name,
|
||||
row,
|
||||
okText: '保存',
|
||||
})
|
||||
}
|
||||
|
||||
function handleDeleteBtn(id) {
|
||||
const d = $dialog.warning({
|
||||
content: '确定删除?',
|
||||
title: '提示',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
async onPositiveClick() {
|
||||
try {
|
||||
d.loading = true
|
||||
await api.deletePermission(id)
|
||||
$message.success('删除成功')
|
||||
$table.value.handleSearch()
|
||||
d.loading = false
|
||||
} catch (error) {
|
||||
d.loading = false
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function handleEnable(item) {
|
||||
try {
|
||||
item.enableLoading = true
|
||||
await api.savePermission(item.id, {
|
||||
enable: !item.enable,
|
||||
})
|
||||
$message.success('操作成功')
|
||||
$table.value?.handleSearch()
|
||||
item.enableLoading = false
|
||||
} catch (error) {
|
||||
item.enableLoading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
84
src/views/pms/role/components/CascadeTree.vue
Normal file
84
src/views/pms/role/components/CascadeTree.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2023/12/05 21:29:32
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
|
||||
<template>
|
||||
<n-tree
|
||||
:key-field="keyField"
|
||||
:label-field="labelField"
|
||||
:selectable="false"
|
||||
default-expand-all
|
||||
checkable
|
||||
check-on-click
|
||||
cascade
|
||||
:data="treeData"
|
||||
:checked-keys="checkedKeys"
|
||||
:on-update:checked-keys="(keys) => (checkedKeys = keys)"
|
||||
:on-update:indeterminate-keys="(keys) => (halfCheckedKeys = keys)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
treeData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
labelField: {
|
||||
type: String,
|
||||
default: 'label',
|
||||
},
|
||||
keyField: {
|
||||
type: String,
|
||||
default: 'value',
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(['update:value'])
|
||||
|
||||
const halfCheckedKeys = ref([])
|
||||
const checkedKeys = ref([])
|
||||
watch([halfCheckedKeys, checkedKeys], ([v1, v2]) => {
|
||||
emit('update:value', Array.from(new Set([...v1, ...v2])))
|
||||
})
|
||||
onMounted(() => {
|
||||
halfCheckedKeys.value = getHalfCheckedValues(props.value, props.treeData)
|
||||
checkedKeys.value = props.value.filter((item) => !halfCheckedKeys.value.includes(item))
|
||||
})
|
||||
|
||||
// 获取半选状态的值
|
||||
function getHalfCheckedValues(selectedValues, treeData, halfCheckedValues = []) {
|
||||
function isHalfChecked(node) {
|
||||
// 如果存在子节点没有选中或者子节点是半选状态
|
||||
return node.children.some(
|
||||
(item) =>
|
||||
!selectedValues.includes(item[props.keyField]) ||
|
||||
halfCheckedValues.includes(item[props.keyField])
|
||||
)
|
||||
}
|
||||
|
||||
function hasGrandson(node) {
|
||||
return node.children.some((item) => !!item.children?.length)
|
||||
}
|
||||
|
||||
for (const item of treeData) {
|
||||
if (!item.children?.length) continue
|
||||
if (hasGrandson(item)) {
|
||||
// 根据孙节点判断子节点是否是半选
|
||||
getHalfCheckedValues(selectedValues, item.children, halfCheckedValues)
|
||||
isHalfChecked(item) && halfCheckedValues.push(item[props.keyField])
|
||||
} else {
|
||||
isHalfChecked(item) && halfCheckedValues.push(item[props.keyField])
|
||||
}
|
||||
}
|
||||
|
||||
return halfCheckedValues
|
||||
}
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
<!--------------------------------
|
||||
- @Author: Ronnie Zhang
|
||||
- @LastEditor: Ronnie Zhang
|
||||
- @LastEditTime: 2024/04/01 15:52:40
|
||||
- @LastEditTime: 2023/12/05 21:29:38
|
||||
- @Email: zclzone@outlook.com
|
||||
- Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
|
||||
--------------------------------->
|
||||
@ -67,16 +67,11 @@
|
||||
<n-input v-model:value="modalForm.code" :disabled="modalAction !== 'add'" />
|
||||
</n-form-item>
|
||||
<n-form-item label="权限" path="permissionIds">
|
||||
<n-tree
|
||||
key-field="id"
|
||||
<CascadeTree
|
||||
v-model:value="modalForm.permissionIds"
|
||||
:tree-data="permissionTree"
|
||||
label-field="name"
|
||||
:selectable="false"
|
||||
:data="permissionTree"
|
||||
:checked-keys="modalForm.permissionIds"
|
||||
:on-update:checked-keys="(keys) => (modalForm.permissionIds = keys)"
|
||||
default-expand-all
|
||||
checkable
|
||||
check-on-click
|
||||
key-field="id"
|
||||
class="cus-scroll max-h-200 w-full"
|
||||
/>
|
||||
</n-form-item>
|
||||
@ -96,6 +91,7 @@ import { NButton, NSwitch } from 'naive-ui'
|
||||
import { MeCrud, MeQueryItem, MeModal } from '@/components'
|
||||
import { useCrud } from '@/composables'
|
||||
import api from './api'
|
||||
import CascadeTree from './components/CascadeTree.vue'
|
||||
|
||||
defineOptions({ name: 'RoleMgt' })
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user