123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- <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>
|