123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344 |
- <template>
- <div class="hc-contract-info-wbs relative h-full">
- <div class="wbs-template-body">
- <div v-loading="leftLoading" class="left">
- <hc-card-item scrollbar :title="`${leftNum}项`">
- <el-tree
- ref="leftTreeRef" :data="leftTreeData" show-checkbox node-key="id" :props="treeProps"
- highlight-current :expand-on-click-node="false" @node-expand="nodeLeftTreeExpand"
- @check-change="checkLeftTreeChange"
- />
- </hc-card-item>
- </div>
- <div class="btn-action">
- <div class="relative">
- <div class="relative">
- <el-button hc-btn :type="leftNum <= 0 ? '' : 'primary'" :disabled="leftNum <= 0" @click="addTreeClick">
- <i class="i-ri-arrow-right-line" />
- </el-button>
- </div>
- <div class="relative mt-14px">
- <el-button hc-btn :type="rightNum <= 0 ? '' : 'primary'" :disabled="rightNum <= 0" @click="delTreeClick">
- <i class="i-ri-arrow-left-line" />
- </el-button>
- </div>
- </div>
- </div>
- <div v-loading="rightLoading" class="right">
- <hc-card-item scrollbar :title="`${rightNum}项`">
- <el-tree
- ref="rightTreeRef" :data="rightTreeData" show-checkbox node-key="id" :props="treeProps"
- highlight-current :expand-on-click-node="false" :default-expanded-keys="rightExpands"
- @check-change="checkRightTreeChange"
- />
- </hc-card-item>
- </div>
- </div>
- <div class="action">
- <el-button hc-btn class="mr-4" :loading="submitLoading" @click="saveAndExit">保存并退出</el-button>
- <el-button hc-btn type="success" :loading="submitLoading" @click="saveAndBackStep">保存并返回上一步</el-button>
- <el-button hc-btn type="primary" :loading="submitLoading" @click="saveAndNextStep">保存并进入下一步</el-button>
- </div>
- </div>
- </template>
- <script setup>
- import { nextTick, onMounted, ref, watch } from 'vue'
- import { deepClone, getArrValue, getObjValue, isNullES } from 'js-fast-way'
- import projectApi from '~api/project/project'
- import contractApi from '~api/project/contract'
- const props = defineProps({
- data: {
- type: Object,
- default: () => ({}),
- },
- })
- //事件
- const emit = defineEmits(['close', 'next', 'back'])
- //监听数据
- const dataInfo = ref(props.data)
- watch(() => props.data, (data) => {
- dataInfo.value = data
- getDataApi()
- }, { deep: true })
- //渲染完成
- onMounted(() => {
- getDataApi()
- })
- //树配置
- const leftTreeRef = ref(null)
- const rightTreeRef = ref(null)
- const treeProps = {
- children: 'children',
- label: 'title',
- }
- //获取数据
- const getDataApi = async () => {
- await getProjectDeatil()
- if (isNullES(wbsId.value)) {
- return
- }
- await getLeftTreeApi()
- await getRightTreeApi()
- }
- //获取项目详情
- const wbsId = ref('')
- const projectInfo = ref({})
- const getProjectDeatil = async () => {
- wbsId.value = ''
- projectInfo.value = {}
- const { pid } = getObjValue(dataInfo.value)
- if (isNullES(pid)) return
- const { data } = await projectApi.detail(pid)
- const res = getObjValue(data)
- projectInfo.value = res
- wbsId.value = res.referenceWbsTemplateId
- }
- //获取WBS树列表
- const leftLoading = ref(false)
- const leftTreeData = ref([])
- const getLeftTreeApi = async () => {
- leftLoading.value = true
- const { pid } = getObjValue(dataInfo.value)
- const { data } = await projectApi.findProjectTree({
- projectId: pid,
- wbsId: wbsId.value,
- })
- leftTreeData.value = getArrValue(data)
- leftLoading.value = false
- }
- //获取右边数据
- const rightLoading = ref(false)
- const rightTreeData = ref([])
- const getRightTreeApi = async () => {
- rightLoading.value = true
- const { pid, cid } = getObjValue(dataInfo.value)
- const { data } = await contractApi.getContractInfoTree({
- wbsId: wbsId.value,
- projectId: pid,
- contractId: cid,
- })
- rightTreeData.value = getArrValue(data)
- rightLoading.value = false
- setRightTree()
- }
- //左边树被展开
- const rightExpands = ref([])
- const nodeLeftTreeExpand = (data) => {
- rightExpands.value = rightExpands.value.concat([data.id])
- }
- //左边树复选框被点击
- const leftNum = ref(0)
- const checkLeftTreeChange = () => {
- const e = leftTreeRef.value
- let checkNum = e.getCheckedKeys().length
- let halfNum = e.getHalfCheckedKeys().length
- leftNum.value = checkNum + halfNum
- }
- //右边树复选框被点击
- const rightNum = ref(0)
- const checkRightTreeChange = () => {
- const e = rightTreeRef.value
- let checkNum = e.getCheckedKeys().length
- let halfNum = e.getHalfCheckedKeys().length
- rightNum.value = checkNum + halfNum
- }
- //左边树新增到右边
- const addTreeClick = () => {
- const left = leftTreeRef.value, right = rightTreeRef.value
- if (rightTreeData.value.length < 1) {
- //直接把左边勾选的树复制到右侧
- let allTree = deepClone(leftTreeData.value)
- const halfKeys = left.getHalfCheckedKeys()
- const keys = left.getCheckedKeys().concat(halfKeys)
- getRightTree(allTree, keys)
- rightTreeData.value = allTree
- } else {
- //只增加右侧树没有的节点,不会覆盖右侧树多余的节点
- let checkNodes = left.getCheckedNodes(false, true)
- let rIdMap = new Map()
- checkNodes.forEach((data) => {
- rIdMap.set(data.id, data)
- })
- let lIdSet = new Set()
- getRightTreeData(lIdSet, rightTreeData.value)
- //在右侧id减去左侧有的id,剩下的就是新增的id
- lIdSet.forEach((id) => {
- rIdMap.delete(id)
- })
- let addMap = new Map()
- rIdMap.forEach((data) => {
- if (data.parentId !== '0' && data.parentId !== 0) {
- //在左侧树能找到父节点的,新增
- let lNode = right.getNode(data.parentId)
- if (lNode) addMap.set(data, lNode.data.id)
- }
- })
- //把半选和选中的数组key合并
- const halfKeys = left.getHalfCheckedKeys()
- const keys = left.getCheckedKeys().concat(halfKeys)
- const myArray = Array.from(addMap.keys())
- let myright = deepClone(myArray)
- getRightTree(myright, keys)
- myright.forEach((data) => {
- right.append(data, data.parentId)
- })
- }
- }
- //获取右边树
- const getRightTree = (arr, keys) => {
- //对比所有的node和选中的key
- for (let i = arr.length - 1; i >= 0; i--) {
- let isIn = false
- for (let j = keys.length - 1; j >= 0; j--) {
- if (keys[j] === arr[i].id) {
- isIn = true
- //已经匹配到的key移除,节省性能
- keys.splice(j, 1)
- break
- }
- }
- if (isIn) {
- //包含在选中的节点,如果有childer继续递归判断
- if (arr[i].children && arr[i].children.length) {
- getRightTree(arr[i].children, keys)
- }
- } else {
- //不包含在选中key的node删除
- arr.splice(i, 1)
- }
- }
- }
- const getRightTreeData = (set, arr) => {
- arr.forEach((data) => {
- set.add(data.id)
- if (data.children && data.children.length) {
- getRightTreeData(set, data.children)
- }
- })
- }
- //右边树移除
- const delTreeClick = () => {
- const left = leftTreeRef.value, right = rightTreeRef.value
- let delNodes = right.getCheckedNodes()
- //只把选中的节点移除
- delNodes.forEach((node) => {
- right.remove(node)
- left.setChecked(node.id, false)
- })
- checkLeftTreeChange()
- checkRightTreeChange()
- }
- const setRightTree = () => {
- let ids = []
- const data = rightTreeData.value
- for (let i = 0; i < data.length; i++) {
- getLeafIds(ids, data[i])
- }
- //在左边把右边的节点勾选上
- const e = leftTreeRef.value
- nextTick(() => {
- e.setCheckedKeys(ids, true)
- })
- }
- const getLeafIds = (ids, data) => {
- if (data.children && data.children.length) {
- for (let i = 0; i < data.children.length; i++) {
- getLeafIds(ids, data.children[i])
- }
- } else {
- ids.push(data.id)
- }
- }
- const getTreeAllId = async (name) => {
- let tree
- if (name === 'left') {
- tree = leftTreeRef.value
- } else if (name === 'right') {
- tree = rightTreeRef.value
- }
- let ids = []
- for (let i = 0; i < tree.data.length; i++) {
- await getIds(ids, tree.data[i])
- }
- return ids.join(',')
- }
- const getIds = async (ids, data) => {
- ids.push(data.id)
- if (data.children && data.children.length) {
- for (let i = 0; i < data.children.length; i++) {
- await getIds(ids, data.children[i])
- }
- }
- }
- //保存并退出
- const saveAndExit = async () => {
- const isRes = await saveDataApi()
- if (!isRes) return
- emit('close', dataInfo.value)
- }
- //保存并返回上一步
- const saveAndBackStep = async () => {
- const isRes = await saveDataApi()
- if (!isRes) return
- emit('back', dataInfo.value)
- }
- //保存并进入下一步
- const saveAndNextStep = async () => {
- const isRes = await saveDataApi()
- if (!isRes) return
- emit('next', dataInfo.value)
- }
- //保存
- const submitLoading = ref(false)
- const saveDataApi = async () => {
- submitLoading.value = true
- const ids = await getTreeAllId('right')
- const { pid, cid } = getObjValue(dataInfo.value)
- const { code } = await contractApi.submitWbsTreeInContract({
- wbsId: wbsId.value,
- projectId: pid,
- contractId: cid,
- wbsTreeIds: ids,
- })
- submitLoading.value = false
-
- if (code === 200) {
- window?.$message?.success('保存成功')
- }
- return code === 200
- }
- </script>
- <style lang="scss">
- @import './style/wbs';
- </style>
|