| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- <script lang="ts" setup>
- import { ref, watch, onMounted, nextTick } from 'vue'
- import { ElTree } from 'element-plus'
- import * as DeptApi from '@/api/system/dept'
- // 1. 引入需要的收缩/展开图标
- import { Search, CaretLeft, CaretRight } from '@element-plus/icons-vue'
- const props = defineProps({
- deptId: {
- type: Number,
- required: true
- },
- modelValue: {
- type: String,
- default: undefined
- },
- contractName: {
- type: String,
- default: undefined
- },
- title: {
- type: String,
- default: '井'
- }
- })
- const emits = defineEmits(['update:modelValue', 'node-click', 'update:contractName'])
- // --- 展开/收缩状态 ---
- const isCollapsed = ref(false)
- // --- 原有业务逻辑保持不变 ---
- const wellName = ref('')
- interface Tree {
- label: string
- value: string
- children?: Tree[]
- type: '1' | '2'
- rawData: any
- }
- const deptList = ref<Tree[]>([])
- const treeRef = ref<InstanceType<typeof ElTree>>()
- const expandedKeys = ref<string[]>([])
- const loadTree = async () => {
- try {
- const res = await DeptApi.getTaskWellNames(props.deptId, wellName.value)
- // --- 数据处理开始 ---
- const parentMap = new Map<number, Tree>()
- const treeData: Tree[] = []
- // 第一步:先找出所有的 Type 1 (父级),建立映射
- res.forEach((item: any) => {
- if (item.type === '1') {
- const node: Tree = {
- label: item.projectName,
- value: item.projectName,
- type: '1',
- children: [],
- rawData: item
- }
- parentMap.set(item.projectId, node)
- treeData.push(node)
- }
- })
- res.forEach((item: any) => {
- if (item.type === '2') {
- const parent = parentMap.get(item.projectId)
- const childNode: Tree = {
- label: item.wellName,
- value: item.wellName,
- type: '2',
- rawData: item
- }
- if (parent) {
- parent.children?.push(childNode)
- }
- }
- })
- deptList.value = treeData
- if (!props.modelValue && treeData.length > 0 && treeData[0].children?.length) {
- const firstChild = treeData[0].children[0]
- emits('update:modelValue', firstChild.value)
- nextTick(() => {
- if (treeRef.value) {
- treeRef.value.setCurrentKey(firstChild.value)
- expandedKeys.value = [treeData[0].value]
- }
- })
- } else if (props.modelValue) {
- nextTick(() => {
- if (treeRef.value) {
- treeRef.value.setCurrentKey(props.modelValue)
- expandedKeys.value = treeData.map((node) => node.value)
- }
- })
- }
- } catch (error) {
- console.error('加载井名失败:', error)
- }
- }
- const handleNodeClick = (data: Tree) => {
- if (data.type === '1') {
- emits('update:contractName', data.value)
- emits('update:modelValue', '')
- } else if (data.type === '2') {
- emits('update:modelValue', data.value)
- emits('update:contractName', '')
- }
- emits('node-click', data)
- }
- /** 筛选节点逻辑 */
- const filterNode = (value: string, data: Tree) => {
- if (!value) return true
- return data.label.includes(value)
- }
- /** 监听输入框进行过滤 */
- watch(wellName, (val) => {
- treeRef.value?.filter(val)
- })
- watch(
- () => props.deptId,
- (newVal, oldVal) => {
- if (newVal !== oldVal) {
- loadTree()
- }
- }
- )
- watch(
- () => props.modelValue,
- (newVal) => {
- if (newVal && treeRef.value) {
- treeRef.value.setCurrentKey(newVal)
- if (!expandedKeys.value.includes(newVal)) {
- expandedKeys.value.push(newVal)
- }
- }
- },
- { immediate: true }
- )
- onMounted(() => {
- loadTree()
- })
- </script>
- <template>
- <div
- class="dept-aside-container relative bg-white dark:bg-[#1d1e1f] shadow rounded-lg transition-all duration-300 ease-in-out overflow-visible"
- :class="[isCollapsed ? 'is-collapsed' : 'p-4']"
- >
- <div class="inner-content flex flex-col gap-4 h-full w-full">
- <h1 class="text-lg font-medium truncate shrink-0">{{ props.title }}</h1>
- <div class="shrink-0">
- <el-input
- v-model="wellName"
- size="default"
- placeholder="请输入井名"
- clearable
- :prefix-icon="Search"
- />
- </div>
- <div class="flex-1 relative overflow-hidden">
- <el-auto-resizer class="absolute">
- <template #default="{ height }">
- <el-scrollbar :style="{ height: `${height}px` }">
- <el-tree
- ref="treeRef"
- :data="deptList"
- :expand-on-click-node="false"
- :filter-node-method="filterNode"
- node-key="value"
- highlight-current
- :default-expanded-keys="expandedKeys"
- @node-click="handleNodeClick"
- />
- </el-scrollbar>
- </template>
- </el-auto-resizer>
- </div>
- </div>
- <div class="collapse-handle" @click="isCollapsed = !isCollapsed">
- <el-icon size="12">
- <CaretLeft v-if="!isCollapsed" />
- <CaretRight v-else />
- </el-icon>
- </div>
- </div>
- </template>
- <style scoped>
- /* 容器基础样式及动画 */
- .dept-aside-container {
- width: 14vw;
- height: calc(
- 100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
- );
- min-width: 200px;
- box-sizing: border-box;
- flex-shrink: 0;
- transition:
- width 0.3s ease-in-out,
- min-width 0.3s ease-in-out,
- padding 0.3s ease-in-out,
- margin 0.3s ease-in-out;
- }
- /* 内部内容区域防挤压及淡入淡出 */
- .inner-content {
- min-width: 168px;
- opacity: 1;
- transition: opacity 0.2s ease-in-out;
- }
- /* 折叠状态下的容器形态 */
- .dept-aside-container.is-collapsed {
- width: 0;
- min-width: 0;
- padding: 0;
- margin-right: -16px; /* 根据实际父级 grid gap 调整,若无间距可设为 0 */
- pointer-events: none;
- box-shadow: none;
- }
- /* 折叠状态下隐藏内部内容 */
- .dept-aside-container.is-collapsed .inner-content {
- opacity: 0;
- visibility: hidden;
- transition:
- opacity 0.1s ease-in-out,
- visibility 0s 0.1s;
- }
- /* 交互把手样式 */
- .collapse-handle {
- position: absolute;
- top: 50%;
- right: -14px;
- z-index: 200;
- display: flex;
- width: 14px;
- height: 60px;
- color: var(--el-text-color-secondary);
- pointer-events: auto;
- cursor: pointer;
- background-color: var(--el-bg-color);
- border: 1px solid var(--el-border-color-light);
- border-left: none;
- border-radius: 0 12px 12px 0;
- transform: translateY(-50%);
- box-shadow: 2px 0 6px rgb(0 0 0 / 5%);
- transition: right 0.3s ease-in-out;
- align-items: center;
- justify-content: center;
- }
- /* 折叠时把手向左位移贴边 */
- .is-collapsed .collapse-handle {
- right: -8px;
- border-left: 1px solid var(--el-border-color-light);
- }
- .collapse-handle:hover {
- color: var(--el-color-primary);
- background-color: var(--el-fill-color-light);
- }
- .truncate {
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- </style>
|