فهرست منبع

跨节点移动

duy 1 ماه پیش
والد
کامیت
e3613074d6
3فایلهای تغییر یافته به همراه428 افزوده شده و 4 حذف شده
  1. 18 3
      src/api/modules/data-fill/query.js
  2. 402 0
      src/views/data-fill/components/JumpTreeDialog.vue
  3. 8 1
      src/views/data-fill/wbs.vue

+ 18 - 3
src/api/modules/data-fill/query.js

@@ -195,7 +195,7 @@ export default {
             data: form,
         })
     },
-            //导出内业台账
+        //导出内业台账
     async exportNeiye(form) {
         return HcApi({
             url: '/api/blade-business/neiYeController/export',
@@ -204,7 +204,23 @@ export default {
             responseType: 'blob',
         })
     },
-     async updateCheckPdfInfo(form) {
+    //获取同级节点
+    async getSiblingWbsContract(form) {
+        return HcApi({
+            url: '/api/blade-manager/wbsTreeContract/getSiblingWbsContract',
+            method: 'get',
+            params: form,
+        })
+    },
+    //跨节点移动
+    async moveNode(form) {
+        return HcApi({
+            url: '/api/blade-manager/wbsTreeContract/moveNode',
+            method: 'post',
+            data: form,
+        })
+    },
+    async updateCheckPdfInfo(form) {
         return HcApi({
             url: '/api/blade-business/informationWriteQuery/updateCheckPdfInfo',
             method: 'get',
@@ -220,4 +236,3 @@ export default {
         })
     },
 }
-

+ 402 - 0
src/views/data-fill/components/JumpTreeDialog.vue

@@ -0,0 +1,402 @@
+<template>
+    <hc-new-dialog v-model="moveModal" is-table title="跨节点移动" widths="72rem" @close="closeModal">
+        <hc-page-split class="m-4" :options="{ sizes: [50, 50] }">
+            <!-- 左侧内容保持不变 -->
+            <template #left>
+                <hc-card scrollbar>
+                    <div v-loading="cityLoading" class="checkbox-container">
+                        <el-checkbox
+                            v-model="checkAll"
+                            :indeterminate="isIndeterminate"
+                            @change="handleCheckAllChange"
+                        >
+                            <span class="font-800">全选</span>
+                        </el-checkbox>
+                        <el-checkbox-group
+                            v-model="checkedCities"
+                            class="checkbox-group"
+                            @change="handleCheckedCitiesChange"
+                        >
+                            <el-checkbox 
+                                v-for="city in cities" 
+                                :key="city.pkeyId" 
+                                :label="city.fullName " 
+                                :value="city.pkeyId "
+                                class="checkbox-item"
+                            >
+                                {{ city.fullName }}
+                            </el-checkbox>
+                        </el-checkbox-group>
+                    </div>
+                </hc-card>
+            </template>
+            
+            <hc-card class="tree-card">
+                <template #search>
+                    <div class="flex-1">
+                        <el-input v-model="searchInput" placeholder="请输入" clearable />
+                    </div>
+                    <div class="ml-2">
+                        <el-button       
+                            hc-btn
+                            type="primary"       
+                            @click="searchClick"
+                        >
+                            搜索
+                        </el-button>
+                    </div>
+                </template>
+                
+                <!-- 修改树的渲染方式 -->
+                <el-scrollbar class="tree-scrollbar mt-3" style="height: 100%;">
+                    <!-- 普通树 -->
+                    <el-tree
+                        v-if="!isShowSearch"
+                        ref="treeRef"
+                        :key="treeKey"
+                        node-key="id" 
+                        :props="treeProps"
+                        :load="treeLoadNode"
+                        lazy
+                        :check-strictly="true"
+                        highlight-current
+                        @node-click="handleNodeClick"
+                    >
+                        <template #default="{ node, data }">
+                            <span class="custom-tree-node">
+                                <!-- 使用单选框替代复选框 -->
+                                <el-radio 
+                                    v-model="selectedNodeId" 
+                                    :value="data.id"
+                                    :disabled="data.nodeType === 6"
+                                    class="mr-2"
+                                />
+                                <span>{{ node.label }}</span>
+                            </span>
+                        </template>
+                    </el-tree>
+                    
+                    <!-- 搜索结果树 -->
+                    <el-tree
+                        v-else
+                        v-loading="treeLoading"
+                        node-key="id"
+                        default-expand-all
+                        :props="treeProps"
+                        :data="treeData"
+                        :check-strictly="true"
+                        highlight-current
+                        @node-click="handleNodeClick"
+                    >
+                        <template #default="{ node, data }">
+                            <span class="custom-tree-node">
+                                <!-- 使用单选框替代复选框 -->
+                                <el-radio 
+                                    v-model="selectedNodeId" 
+                                    :value="data.id"
+                                    :disabled="data.nodeType === 6"
+                                    class="mr-2"
+                                />
+                                <span>{{ node.label }}</span>
+                            </span>
+                        </template>
+                    </el-tree>
+                </el-scrollbar>
+            </hc-card>
+        </hc-page-split>
+        <template #footer>
+            <el-button :loading="moveLoading" @click="submitMove(1)">保存并退出</el-button>
+            <el-button type="primary" :loading="moveLoading" @click="submitMove(2)">保存并继续</el-button>
+        </template>
+    </hc-new-dialog>
+</template>
+
+<script setup> 
+import { nextTick, ref, watch } from 'vue'
+import { getArrValue, getObjValue } from 'js-fast-way'
+import queryApi from '~api/data-fill/query'
+
+// 接收父组件传入的属性
+const props = defineProps({
+  contractId: {
+    type: String,
+    default: '',
+  },
+  classType: {
+    type: String,
+    default: '',
+  },
+  authBtnTabKey: {
+    type: String,
+    default: '',
+  },
+  primaryKeyId: {
+    type: String,
+    default: '',
+  },
+})
+
+// 事件
+const emit = defineEmits(['close', 'save'])
+const contractId = ref(props.contractId)
+const classType = ref(props.classType) 
+const authBtnTabKey = ref(props.authBtnTabKey)
+const primaryKeyId = ref(props.primaryKeyId)
+
+// 监听
+watch(() => [
+    props.contractId,
+    props.classType,
+    props.authBtnTabKey,
+    props.primaryKeyId,
+], ([cid, clas, tab, pkid]) => {
+    contractId.value = cid
+    classType.value = clas
+    authBtnTabKey.value = tab
+    primaryKeyId.value = pkid
+  
+})
+
+const moveModal = defineModel('modelValue', {
+    default: false,
+})
+watch(() => moveModal.value, (val) => {
+    if (val && primaryKeyId.value) {
+        getSameLevelsTreeData()
+    }
+})
+const closeModal = ()=>{
+    moveModal.value = false
+    checkAll.value = false
+    checkedCities.value = []
+    cities.value = []
+    selectedNodeId.value = null // 重置选中状态
+    emit('close')
+}
+
+// 左侧复选框相关
+const checkAll = ref(false)
+const isIndeterminate = ref(false)
+const checkedCities = ref([])
+const cities = ref([])
+
+const handleCheckAllChange = (val) => {
+  checkedCities.value = val ? cities.value.map(city => city.pkeyId) : []
+  isIndeterminate.value = false
+}
+
+const handleCheckedCitiesChange = (value) => {
+  const checkedCount = value.length
+  checkAll.value = checkedCount === cities.value.length
+  isIndeterminate.value = checkedCount > 0 && checkedCount < cities.value.length
+}
+
+// 搜索相关
+const searchInput = ref('')
+
+// 树相关 - 修改为单选
+const treeRef = ref(null)
+const treeData = ref([])
+const isShowSearch = ref(false)
+const selectedNodeId = ref(null) // 存储当前选中的节点ID
+const currentNode = ref(null) // 存储当前选中的节点数据
+
+// 树配置 - 移除disabled配置,在单选框中处理
+const treeProps = {
+  label: 'title',
+  children: 'children',
+  isLeaf: 'notExsitChild',
+}
+const treeKey = ref(0) // 用于强制刷新树组件
+// 加载节点数据
+const treeLoadNode = async (node, resolve) => {
+    const { level, data: item } = node
+    
+    let contractIdRelation = '',
+        parentId = '',
+        primaryKeyId = ''
+    if (level !== 0) {
+        const nodeData = getObjValue(item)
+        contractIdRelation = nodeData?.contractIdRelation || ''
+        parentId = contractIdRelation ? nodeData?.primaryKeyId : nodeData?.id
+        primaryKeyId = nodeData?.id || ''
+    }
+    
+    // 获取数据
+    const { data } = await queryApi.queryWbsTreeData({
+        contractId: contractId.value || '',
+        contractIdRelation,
+        primaryKeyId,
+        parentId,
+        classifyType: classType.value,
+        tableOwner: authBtnTabKey.value,
+        dataTime: new Date(),
+    })
+
+    resolve(getArrValue(data))
+}
+
+// 处理节点点击和单选逻辑
+const handleNodeClick = (data) => {
+    // 如果节点被禁用则不处理
+    if (data.nodeType === 6) return
+    
+    // 设置选中状态
+    selectedNodeId.value = data.id
+    currentNode.value = data
+}
+
+// 搜索功能
+
+// 搜索功能
+const searchClick = () => {
+  if (!searchInput.value) {
+     isShowSearch.value = false
+     // 重新加载原始树
+     if (treeRef.value) {
+       refreshTree()
+     }
+  } else {
+    isShowSearch.value = true
+    getSearchTreeData()
+  }
+}
+// 刷新树的方法 - 替代reload
+const refreshTree = () => {
+  // 通过修改key值强制树组件重新渲染
+  treeKey.value += 1
+  // 重置选中状态
+  selectedNodeId.value = null
+  currentNode.value = null
+}
+
+
+const treeLoading = ref(false)
+const getSearchTreeData = async () => {
+    treeLoading.value = true
+    const { error, code, data } = await queryApi.getTreeNodeByQueryValueAndContractId({
+        contractId: contractId.value,
+        queryValue: searchInput.value,
+        tableOwner: authBtnTabKey.value,
+    })
+    
+    if (!error && code === 200) {
+        treeData.value = getArrValue(data)
+    } else {
+        treeData.value = []
+    }
+    treeLoading.value = false
+}
+
+// 获取左侧数据
+const cityLoading = ref(false)
+const getSameLevelsTreeData = async () => { 
+    cityLoading.value = true
+    const { error, code, data } = await queryApi.getSiblingWbsContract({
+        pKeyId: primaryKeyId.value,
+    })
+    
+    if (!error && code === 200) {
+        cities.value = getArrValue(data)
+    } else {
+        cities.value = []
+    }
+    cityLoading.value = false
+}
+
+// 提交移动
+const moveLoading = ref(false)
+const submitMove = async (type)=>{
+    // 验证是否选择了目标节点
+    if (!selectedNodeId.value) {
+        window.$message?.warning('请选择目标节点')
+        return
+    }
+    
+    moveLoading.value = true
+    const { error, code, data, msg } = await queryApi.moveNode({
+        leftPkeyIds: checkedCities.value,
+        rightPkeyId: currentNode.value.pKeyId,
+    })
+    moveLoading.value = false
+    
+    if (!error && code === 200) {
+        window.$message?.success(msg ?? '操作成功')
+        
+        if (type === 1) {
+            emit('save')
+            closeModal()
+        } else {
+            // 重置表单但保持弹窗打开
+            checkAll.value = false
+            checkedCities.value = []
+            selectedNodeId.value = null
+            currentNode.value = null
+            getSameLevelsTreeData()
+             // 刷新树数据
+            if (isShowSearch.value && searchInput.value) {
+                getSearchTreeData() // 搜索状态下重新搜索
+            } else {
+                refreshTree() // 普通状态下刷新树
+            }
+    
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.checkbox-container {
+    display: flex;
+    flex-direction: column;
+    gap: 10px;
+
+    .checkbox-group {
+        display: flex;
+        flex-direction: column;
+        gap: 10px;
+    }
+
+    .checkbox-item {
+        height: 32px;
+        display: flex;
+        align-items: center;
+    }
+}
+
+// 树卡片样式
+.tree-card {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+
+  :deep(.hc-card-body) {
+    flex: 1;
+    height: 0;
+    display: flex;
+    flex-direction: column;
+    padding: 0;
+  }
+}
+
+// 滚动容器样式
+.tree-scrollbar {
+  flex: 1;
+  height: 0;
+  margin-top: 12px;
+  
+  :deep(.el-scrollbar__view) {
+    height: 100%;
+  }
+}
+
+// 调整单选框与文字的对齐方式
+:deep(.el-radio) {
+  vertical-align: middle;
+}
+
+.custom-tree-node {
+  display: flex;
+  align-items: center;
+}
+</style>

+ 8 - 1
src/views/data-fill/wbs.vue

@@ -1436,7 +1436,7 @@ import { HcDelMsg, NewDelMsg } from 'hc-vue3-ui'
 import HcUpload from './components/HcUpload.vue'
 import { toPdfPage } from '~uti/btn-auth'
 import website from '~src/config'
-
+import JumpTreeDialog from './components/JumpTreeDialog.vue'
 //初始变量
 const router = useRouter()
 const useRoutes = useRoute()
@@ -1707,6 +1707,13 @@ const setElTreeMenu = (contractType) => {
                 key: 'nodetree',
             })
         }
+        if (HcIsButton('wbs_tree_jump')) {
+            newArr.push({
+                icon: 'drag-move-2',
+                label: '跨节点移动',
+                key: 'jumpTree',
+            })
+        } 
     } else if (contractType === 2) {
         if (HcIsButton('wbs_tree_add')) {
             newArr.push({ icon: 'add-circle', label: '新增节点', key: 'add' })