zhouzhou
21 小时以前 fc9103d3c93617bf4b739a05d01bca6b22f8ecc2
wms-admin/src/views/system/role/modules/role-scope-drawer.vue
@@ -5,10 +5,11 @@
    size="min(100vw, 960px)"
    direction="rtl"
    append-to-body
    class="role-scope-drawer"
    :close-on-click-modal="false"
  >
    <div class="flex h-full flex-col gap-4">
      <div class="flex flex-wrap items-center justify-between gap-3">
    <div class="role-scope-drawer__content">
      <div class="role-scope-drawer__toolbar">
        <div class="flex flex-wrap gap-2">
          <ElButton @click="toggleSelectAll" v-ripple>
            {{
@@ -19,9 +20,7 @@
          </ElButton>
          <ElButton @click="toggleExpandAll" v-ripple>
            {{
              expandedKeys.length === 0
                ? $t('roleScope.actions.expandAll')
                : $t('roleScope.actions.collapseAll')
              isExpanded ? $t('roleScope.actions.collapseAll') : $t('roleScope.actions.expandAll')
            }}
          </ElButton>
        </div>
@@ -40,28 +39,28 @@
        </div>
      </div>
      <ElCard
        class="flex min-h-0 flex-1 flex-col overflow-hidden [&_.el-card__body]:flex-1 [&_.el-card__body]:min-h-0 [&_.el-card__body]:overflow-auto"
        shadow="never"
      >
      <ElCard class="role-scope-drawer__tree-card" shadow="never">
        <ElSkeleton v-if="loading" :rows="10" animated />
        <ElEmpty
          v-else-if="treeData.length === 0"
          :description="$t('roleScope.messages.emptyData')"
        />
        <ElTree
          v-else
          ref="treeRef"
          node-key="id"
          show-checkbox
          check-strictly
          check-on-click-node
          :data="treeData"
          :props="treeProps"
          :default-expanded-keys="expandedKeys"
          @check-change="handleCheckChange"
          @check="handleCheck"
        />
        <div v-else class="scope-tree-scroll">
          <ElTree
            ref="treeRef"
            node-key="id"
            show-checkbox
            check-strictly
            check-on-click-node
            :data="treeData"
            :props="treeProps"
            :default-expanded-keys="expandedKeys"
            @check-change="handleCheckChange"
            @check="handleCheck"
            @node-expand="syncExpandedState"
            @node-collapse="syncExpandedState"
          />
        </div>
      </ElCard>
    </div>
  </ElDrawer>
@@ -108,6 +107,7 @@
  const selectedKeys = ref<Array<string>>([])
  const halfCheckedKeys = ref<Array<string>>([])
  const expandedKeys = ref<Array<string>>([])
  const isExpanded = ref(false)
  const treeProps = {
    label: 'label',
@@ -177,8 +177,50 @@
    return keys
  }
  const getNodeFromMap = (nodesMap: unknown, key: string) => {
    if (nodesMap instanceof Map) return nodesMap.get(key)
    return (nodesMap as Record<string, { childNodes?: unknown[]; expanded?: boolean }> | undefined)?.[
      key
    ]
  }
  const applyNodeExpansion = (expand: boolean, keys = collectNodeKeys(treeData.value).parentKeys) => {
    const nodesMap = treeRef.value?.store?.nodesMap
    if (!nodesMap) return
    keys.forEach((key) => {
      const node = getNodeFromMap(nodesMap, key)
      if (node) node.expanded = expand
    })
  }
  const syncExpandedState = () => {
    const { parentKeys } = collectNodeKeys(treeData.value)
    const nodesMap = treeRef.value?.store?.nodesMap
    if (!nodesMap || parentKeys.length === 0) {
      expandedKeys.value = []
      isExpanded.value = false
      return
    }
    expandedKeys.value = parentKeys.filter((key) => Boolean(getNodeFromMap(nodesMap, key)?.expanded))
    isExpanded.value = expandedKeys.value.length === parentKeys.length
  }
  const setTreeExpansion = async (
    expand: boolean,
    keys = collectNodeKeys(treeData.value).parentKeys
  ) => {
    expandedKeys.value = expand ? keys : []
    isExpanded.value = expand && keys.length > 0
    await nextTick()
    applyNodeExpansion(expand, keys)
  }
  const loadTree = async () => {
    loading.value = true
    isExpanded.value = false
    try {
      const [scopeIds, scopeTree] = await Promise.all([
        props.role?.id ? fetchGetRoleScopeList(props.kind, props.role.id) : Promise.resolve([]),
@@ -191,11 +233,14 @@
      const { parentKeys } = collectNodeKeys(treeData.value)
      expandedKeys.value = parentKeys
      isExpanded.value = parentKeys.length > 0
    } finally {
      loading.value = false
    }
    await nextTick()
    applyNodeExpansion(isExpanded.value, expandedKeys.value)
    syncExpandedState()
    programmaticChecking.value = true
    treeRef.value?.setCheckedKeys(selectedKeys.value)
    halfCheckedKeys.value = treeRef.value?.getHalfCheckedKeys?.() || []
@@ -248,12 +293,8 @@
    programmaticChecking.value = false
  }
  const toggleExpandAll = () => {
    if (expandedKeys.value.length === 0) {
      expandedKeys.value = collectNodeKeys(treeData.value).parentKeys
    } else {
      expandedKeys.value = []
    }
  const toggleExpandAll = async () => {
    await setTreeExpansion(!isExpanded.value)
  }
  const handleSave = async () => {
@@ -285,3 +326,47 @@
    { immediate: true }
  )
</script>
<style scoped>
  :global(.role-scope-drawer .el-drawer__body) {
    min-height: 0;
    overflow: hidden;
  }
  .role-scope-drawer__content {
    display: grid;
    grid-template-rows: auto minmax(0, 1fr);
    gap: 16px;
    width: 100%;
    height: 100%;
    min-height: 0;
  }
  .role-scope-drawer__toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-width: 0;
  }
  .role-scope-drawer__tree-card {
    min-height: 0;
    overflow: hidden;
  }
  .role-scope-drawer__tree-card :deep(.el-card__body) {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;
    overflow: hidden;
  }
  .scope-tree-scroll {
    flex: 1;
    min-height: 0;
    overflow: auto;
  }
</style>