| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229 |
- <template>
- <el-dialog
- :model-value="visible"
- title="选择部门"
- :width="600"
- :close-on-click-modal="false"
- :close-on-press-escape="false"
- :destroy-on-close="true"
- :append-to-body="true"
- @open="onOpen"
- @close="onClose"
- >
- <div class="selector">
- <div class="selector-left">
- <el-scrollbar>
- <el-tree
- ref="selectorTreeRef"
- :props="treeProps"
- node-key="fullPathName"
- :show-checkbox="true"
- :check-strictly="true"
- :default-checked-keys="selectedKeys"
- :load="onLazyLoad"
- lazy
- @check-change="onCheckChange"
- class="cacp-pd-s"
- >
- <template #default="{ data }">
- <el-icon class="cacp-mr-s" :size="16">
- <Grid />
- </el-icon>
- <span>{{ data.oguName }}</span>
- </template>
- </el-tree>
- </el-scrollbar>
- </div>
- <div class="selector-right">
- <div class="selector-toolbar">
- <span class="count">已选 {{ count }} 个</span><el-link type="primary" @click.prevent="onClear">清空</el-link>
- </div>
- <div class="selector-list">
- <selector-list :dataList="selectedList" @on-delete="onRemove"></selector-list>
- </div>
- </div>
- </div>
- <template #footer>
- <el-button @click="onClose">取消</el-button>
- <el-button type="primary" @click="onSave">确定</el-button>
- </template>
- </el-dialog>
- </template>
- <script setup lang="ts">
- import { ref, computed } from 'vue'
- import type { ElTree } from 'element-plus'
- import type TreeNode from 'element-plus/es/components/tree/src/model/node'
- import SelectorList from './SelectorList.vue'
- import { cloneDeep } from 'lodash-es'
- import * as apis from '@/apis/auth'
- import type { CacpOrganization } from '@cacp/ui'
- import { SuccessResultCode } from '@cacp/ui'
- const selectorTreeRef = ref<InstanceType<typeof ElTree>>()
- const props = withDefaults(
- defineProps<{
- visible: boolean
- multiple?: boolean
- value?: Array<CacpOrganization>
- rootPath?: string
- }>(),
- {
- visible: false,
- multiple: false,
- value: () => [],
- rootPath: ''
- }
- )
- const emits = defineEmits<{
- (e: 'on-close'): void
- (e: 'on-save', value: Array<CacpOrganization>): void
- }>()
- const treeProps = {
- label: 'oguName'
- }
- const cachedList = ref<Array<CacpOrganization>>([])
- const selectedKeys = ref<Array<string>>([])
- const selectedList = computed<Array<CacpOrganization>>(() => {
- return cachedList.value.filter((o) => selectedKeys.value.includes(o.fullPathName))
- })
- const count = computed<number>(() => {
- return selectedKeys.value.length
- })
- function onOpen(): void {
- selectedKeys.value = props.value ? props.value.map((o) => o.fullPathName) : []
- cachedList.value = props.value ? cloneDeep(props.value) : []
- }
- function onClose(): void {
- emits('on-close')
- }
- function onSave(): void {
- emits('on-save', selectedList.value)
- }
- async function onLazyLoad(
- node: TreeNode,
- resolve: (data: Array<CacpOrganization>) => void,
- reject: () => void
- ): Promise<void> {
- if (node.level === 0) {
- let root: CacpOrganization
- if (props.rootPath) {
- const result = await apis.getOrganizationByPath(props.rootPath)
- if (result.code === SuccessResultCode && result.data) {
- root = result.data
- resolve([root])
- } else {
- if (reject) {
- reject()
- }
- }
- } else {
- const result = await apis.getRootOrganization()
- if (result.code === SuccessResultCode && result.data) {
- root = result.data
- resolve([root])
- } else {
- if (reject) {
- reject()
- }
- }
- }
- } else {
- const parent = node.data
- const result = await apis.getOrganizationListByParent(parent.fullPathName, true)
- if (result.code === SuccessResultCode) {
- const children = result.data ?? []
- resolve(children)
- } else {
- if (reject) {
- reject()
- }
- }
- }
- }
- function onCheckChange(data: CacpOrganization, checked: boolean) {
- if (checked) {
- handleCachedList([data])
- if (!props.multiple) {
- selectedKeys.value.splice(0, 1, data.fullPathName)
- } else {
- if (!selectedKeys.value.includes(data.fullPathName)) {
- selectedKeys.value.push(data.fullPathName)
- }
- }
- } else {
- const idx = selectedKeys.value.indexOf(data.fullPathName)
- if (idx > -1) {
- selectedKeys.value.splice(idx, 1)
- }
- }
- handleCheckStatus()
- }
- function onRemove(fullPathName: string): void {
- const idx = selectedKeys.value.indexOf(fullPathName)
- if (idx > -1) {
- selectedKeys.value.splice(idx, 1)
- }
- handleCheckStatus()
- }
- function onClear(): void {
- selectedKeys.value = []
- handleCheckStatus()
- }
- function handleCachedList(list: Array<CacpOrganization>): void {
- for (const org of list) {
- const idx = cachedList.value.findIndex((o) => o.fullPathName === org.fullPathName)
- if (idx > -1) {
- cachedList.value.splice(idx, 1)
- }
- cachedList.value.push(org)
- }
- }
- function handleCheckStatus(): void {
- if (selectorTreeRef.value) {
- selectorTreeRef.value.setCheckedKeys(selectedKeys.value, false)
- }
- }
- </script>
- <style lang="less" scoped>
- .selector {
- display: flex;
- align-items: flex-start;
- border: 1px solid var(--el-border-color);
- border-radius: var(--el-border-radius-base);
- min-height: 260px;
- max-height: 400px;
- align-items: stretch;
- &-left {
- flex: 1;
- border-right: 1px solid var(--el-border-color);
- }
- &-right {
- flex: 1;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- }
- &-list {
- flex: 1;
- overflow: hidden;
- }
- &-toolbar {
- line-height: 32px;
- height: 32px;
- padding: 0 var(--cacp-padding-space-s);
- font-size: var(--font-size-s);
- display: flex;
- align-items: center;
- border-bottom: 1px solid var(--el-border-color);
- justify-content: space-between;
- }
- }
- </style>
|