-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
317 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<template> | ||
<a-menu class="right-menu"> | ||
<a-menu-item @click="onClick('add')"> | ||
<template #icon><icon-plus-circle :size="16" :stroke-width="3" /></template> | ||
<span>新增</span> | ||
</a-menu-item> | ||
|
||
<a-menu-item v-permission="['system:dept:update']" @click="onClick('update')"> | ||
<template #icon><icon-edit :size="16" :stroke-width="3" /></template> | ||
<span>修改</span> | ||
</a-menu-item> | ||
|
||
<a-menu-item v-permission="['system:dept:delete']" :title="data.isSystem ? '系统内置数据不能删除' : undefined" :disabled="data.isSystem" @click="onClick('delete')"> | ||
<template #icon><icon-delete :size="16" :stroke-width="3" /></template> | ||
<span>删除</span> | ||
</a-menu-item> | ||
</a-menu> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import type { DeptResp } from '@/apis' | ||
interface Props { | ||
data: DeptResp | ||
} | ||
const props = withDefaults(defineProps<Props>(), {}) | ||
const emit = defineEmits<{ | ||
(e: 'on-menu-item-click', mode: string, data: DeptResp): void | ||
}>() | ||
// 点击菜单项 | ||
const onClick = (mode: string) => { | ||
emit('on-menu-item-click', mode, props.data) | ||
} | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
:deep(.arco-menu-inner) { | ||
padding: 4px; | ||
.arco-menu-item { | ||
height: 34px; | ||
&:not(.arco-menu-selected) { | ||
color: $color-text-1; | ||
} | ||
&:last-child { | ||
margin-bottom: 0; | ||
} | ||
} | ||
} | ||
.right-menu { | ||
width: 120px; | ||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); | ||
border-radius: 4px; | ||
border: 1px solid var(--color-border-2); | ||
box-sizing: border-box; | ||
.arrow-icon { | ||
margin-right: 0; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,225 @@ | ||
<template> | ||
<div class="left-tree"> | ||
<div class="left-tree__search"> | ||
<a-input v-model="inputValue" :placeholder="props.placeholder" allow-clear> | ||
<template #prefix><icon-search /></template> | ||
</a-input> | ||
</div> | ||
<div class="left-tree__container"> | ||
<div class="left-tree__tree"> | ||
<a-tree | ||
ref="treeRef" | ||
:data="(treeData as unknown as TreeNodeData[])" | ||
:field-names="{ key: 'id' }" | ||
show-line | ||
block-node | ||
default-expand-all | ||
:selected-keys="selectedKeys" | ||
@select="select" | ||
> | ||
<template #title="node"> | ||
<a-trigger | ||
v-model:popup-visible="node.popupVisible" | ||
trigger="contextMenu" | ||
align-point | ||
animation-name="slide-dynamic-origin" | ||
auto-fit-transform-origin | ||
position="bl" | ||
scroll-to-close | ||
> | ||
<a-tooltip v-if="node.description" :content="node.description" background-color="rgb(var(--primary-6))" position="right"> | ||
<div @contextmenu="onContextmenu(node)">{{ node.name }}</div> | ||
</a-tooltip> | ||
<div v-else @contextmenu="onContextmenu(node)">{{ node.name }}</div> | ||
<template #content> | ||
<RightMenu | ||
v-if="has.hasPermOr(['system:dept:update', 'system:dept:delete'])" | ||
:data="node" | ||
@on-menu-item-click="onMenuItemClick" | ||
/> | ||
</template> | ||
</a-trigger> | ||
</template> | ||
</a-tree> | ||
</div> | ||
</div> | ||
</div> | ||
<DeptAddModal ref="DeptAddModalRef" @save-success="getTreeData" /> | ||
</template> | ||
|
||
<script setup lang="tsx"> | ||
import type { Message, Modal, TreeInstance, TreeNodeData } from '@arco-design/web-vue' | ||
import { mapTree } from 'xe-utils' | ||
import DeptAddModal from '../../dept/DeptAddModal.vue' | ||
import RightMenu from './RightMenu.vue' | ||
import { type DeptQuery, type DeptResp, deleteDept, listDept } from '@/apis' | ||
import has from '@/utils/has' | ||
interface Props { | ||
placeholder?: string | ||
} | ||
const props = withDefaults(defineProps<Props>(), { | ||
placeholder: '请输入关键词' | ||
}) | ||
const emit = defineEmits<{ | ||
(e: 'node-click', keys: Array<any>): void | ||
}>() | ||
// 选中节点 | ||
const selectedKeys = ref() | ||
const select = (keys: Array<any>) => { | ||
selectedKeys.value = keys | ||
emit('node-click', keys) | ||
} | ||
const queryForm = reactive<DeptQuery>({ | ||
sort: ['parentId,asc', 'sort,asc', 'createTime,desc'] | ||
}) | ||
interface TreeItem extends DeptResp { | ||
popupVisible: boolean | ||
} | ||
const treeRef = ref<TreeInstance>() | ||
const treeData = ref<TreeItem[]>([]) | ||
const loading = ref(false) | ||
// 查询树列表 | ||
const getTreeData = async (query: DeptQuery = { ...queryForm }) => { | ||
try { | ||
loading.value = true | ||
const { data } = await listDept(query) | ||
treeData.value = mapTree(data, (i) => ({ | ||
...i, | ||
popupVisible: false, | ||
switcherIcon: (node: any) => { | ||
if (!node.isLeaf) { | ||
if (node.expanded) { | ||
return <icon-down /> | ||
} else { | ||
return <icon-down /> | ||
} | ||
} | ||
return <icon-idcard /> | ||
} | ||
})) | ||
await nextTick(() => { | ||
treeRef.value?.expandAll(true) | ||
select([data[0].id]) | ||
}) | ||
} finally { | ||
loading.value = false | ||
} | ||
} | ||
// 树查询 | ||
const inputValue = ref('') | ||
watch(inputValue, (val) => { | ||
queryForm.description = val | ||
getTreeData() | ||
}) | ||
// 保存当前右键的节点 | ||
const contextmenuNode = ref<TreeItem | null>(null) | ||
const onContextmenu = (node: TreeItem) => { | ||
contextmenuNode.value = node | ||
} | ||
// 关闭右键菜单弹框 | ||
const closeRightMenuPopup = () => { | ||
if (contextmenuNode.value?.popupVisible) { | ||
contextmenuNode.value.popupVisible = false | ||
} | ||
} | ||
const DeptAddModalRef = ref<InstanceType<typeof DeptAddModal>>() | ||
// 右键菜单项点击 | ||
const onMenuItemClick = (mode: string, node: DeptResp) => { | ||
closeRightMenuPopup() | ||
if (mode === 'add') { | ||
DeptAddModalRef.value?.onAdd(node.id) | ||
} else if (mode === 'update') { | ||
DeptAddModalRef.value?.onUpdate(node.id) | ||
} else if (mode === 'delete') { | ||
Modal.warning({ | ||
title: '提示', | ||
content: `是否确定删除 [${node.name}]?`, | ||
hideCancel: false, | ||
okButtonProps: { status: 'danger' }, | ||
onBeforeOk: async () => { | ||
try { | ||
const res = await deleteDept(node.id) | ||
if (res.success) { | ||
Message.success('删除成功') | ||
getTreeData() | ||
} | ||
return res.success | ||
} catch (error) { | ||
return false | ||
} | ||
} | ||
}) | ||
} | ||
} | ||
onMounted(() => { | ||
getTreeData() | ||
}) | ||
</script> | ||
|
||
<style lang="scss" scoped> | ||
:deep(.arco-tree-node-title-text) { | ||
width: 100%; | ||
white-space: nowrap; | ||
} | ||
:deep(.arco-tree-node) { | ||
line-height: normal; | ||
.arco-tree-node-title { | ||
border-radius: var(--border-radius-medium); | ||
&:hover { | ||
background-color: var(--color-secondary-hover); | ||
} | ||
} | ||
} | ||
:deep(.arco-tree-node-selected) { | ||
.arco-tree-node-title { | ||
background-color: rgba(var(--primary-6), 0.1); | ||
&:hover { | ||
background-color: rgba(var(--primary-6), 0.1); | ||
} | ||
} | ||
} | ||
.left-tree { | ||
flex: 1; | ||
overflow: hidden; | ||
position: relative; | ||
display: flex; | ||
flex-direction: column; | ||
box-sizing: border-box; | ||
height: 100%; | ||
&__search { | ||
margin-bottom: 10px; | ||
} | ||
&__container { | ||
flex: 1; | ||
overflow: hidden; | ||
background-color: var(--color-bg-1); | ||
position: relative; | ||
height: 100%; | ||
margin-bottom:10px; | ||
} | ||
&__tree { | ||
position: absolute; | ||
top: 0; | ||
bottom: 0; | ||
left: 0; | ||
right: 0; | ||
overflow: auto | ||
} | ||
} | ||
</style> |
Oops, something went wrong.