|
@@ -1,402 +0,0 @@
|
|
|
-<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>
|