division.vue 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. <template>
  2. <div class="hc-page-layout-box">
  3. <div class="hc-layout-left-box" :style="'width:' + leftWidth + 'px;'">
  4. <div class="hc-project-box">
  5. <div class="hc-project-icon-box">
  6. <HcIcon name="stack"/>
  7. </div>
  8. <div class="ml-2 project-name-box">
  9. <span class="text-xl text-cut project-alias">{{projectInfo['projectAlias']}}</span>
  10. <div class="text-xs text-cut project-name">{{projectInfo['name']}}</div>
  11. </div>
  12. </div>
  13. <div class="hc-tree-box">
  14. <el-scrollbar>
  15. <WbsTree ui="page-division-tree" :menus="ElTreeMenu" :autoExpandKeys="treeAutoExpandKeys" :projectId="projectId" :contractId="contractId" isColor @nodeTap="wbsElTreeClick" @menuTap="ElTreeMenuClick"/>
  16. </el-scrollbar>
  17. </div>
  18. <!--左右拖动-->
  19. <div class="horizontal-drag-line" @mousedown="onmousedown"/>
  20. </div>
  21. <div class="hc-page-content-box hc-division-page">
  22. <div class="basic-info">
  23. <HcCard title="当前节点基础信息">
  24. <HcTable :column="tableBasicColumn" :datas="tableBasicData" :isIndex="false" border>
  25. <template #type="{row}">{{getRowType(row['type'])}}</template>
  26. <template #majorDataType="{row}">{{getRowMajorType(row['majorDataType'])}}</template>
  27. </HcTable>
  28. </HcCard>
  29. </div>
  30. <div class="project-info">
  31. <HcCard title="当前节点工程用表信息">
  32. <HcTable :column="tableProjectColumn" :datas="tableProjectData" :isIndex="false" border>
  33. <template #tableType="{row}">{{getRowTableType(row['tableType'])}}</template>
  34. </HcTable>
  35. </HcCard>
  36. </div>
  37. <div class="footer-box">
  38. <el-button hc-btn @click="downloadXlsx">
  39. <HcIcon name="download-2"/>
  40. <span>下载导入划分模板</span>
  41. </el-button>
  42. <el-button type="primary" hc-btn @click="toImportTempClick">
  43. <HcIcon name="folder-upload"/>
  44. <span>导入划分模板</span>
  45. </el-button>
  46. <el-button hc-btn @click="toBackClick">
  47. <HcIcon name="arrow-go-back"/>
  48. <span>返回</span>
  49. </el-button>
  50. </div>
  51. </div>
  52. <!--编辑节点-->
  53. <HcDialog :show="editNodeModal" title="编辑节点" widths="600px" :loading="editNodeLoading" @close="editNodeModal = false" @save="editNodeClick">
  54. <el-form ref="formEditNodeRef" :model="formEditNodeModel" :rules="formEditNodeRules" label-width="auto" size="large">
  55. <el-form-item label="节点名称" prop="title">
  56. <el-input v-model="formEditNodeModel.title" placeholder="请输入节点名称"/>
  57. </el-form-item>
  58. <el-form-item label="上级节点">
  59. <el-input v-model="formEditNodeModel.parent.title" disabled/>
  60. </el-form-item>
  61. <el-form-item label="节点类型">
  62. <el-select v-model="formEditNodeModel.type" block disabled>
  63. <el-option v-for="item in nodeTypeData" :label="item['label']" :value="item['value']"/>
  64. </el-select>
  65. </el-form-item>
  66. <el-form-item label="划分编号">
  67. <el-input v-model="formEditNodeModel.partitionCode" placeholder="请输入划分编号"/>
  68. </el-form-item>
  69. </el-form>
  70. </HcDialog>
  71. <!--复制节点-->
  72. <HcDialog :show="copyNodeModal" title="复制节点" :widths="copyNodeTabKey === '1'?'600px':'1200px'" @close="copyNodeModal = false">
  73. <el-form ref="formCopyNodeModelRef" :model="formCopyNodeModel" :rules="formCopyNodeModelRules" label-width="auto" size="large" v-if="copyNodeTabKey !== '3'">
  74. <el-form-item label="节点名称" prop="title" style="margin-bottom: 0;">
  75. <el-input v-model="formCopyNodeModel.title" placeholder="请输入节点名称"/>
  76. </el-form-item>
  77. </el-form>
  78. <div class="copy-node-many-box" v-if="copyNodeTabKey !== '1'">
  79. <div class="copy-node-many-tree">
  80. <el-scrollbar>
  81. <WbsTree :autoExpandKeys="treeAutoExpandKeys" :projectId="projectId" :contractId="contractId" idPrefix="tree-node-copy-" :isAutoClick="false" :isAutoKeys="false" @nodeTap="copyNodeElTreeClick"/>
  82. </el-scrollbar>
  83. </div>
  84. <div class="copy-node-many-table">
  85. <el-scrollbar>
  86. <el-table :data="copyNodeTable" border stripe>
  87. <el-table-column prop="title" label="复制到的位置"/>
  88. <el-table-column prop="nodeName" label="节点名称" v-if="copyNodeTabKey === '2'">
  89. <template #default="{row}">
  90. <el-form ref="copyNodeTableRef" :model="row" :rules="copyNodeTableRules" label-width="0" size="large">
  91. <el-form-item prop="nodeName" style="margin-bottom: 0;">
  92. <el-input v-model="row.nodeName" placeholder="请输入节点名称"/>
  93. </el-form-item>
  94. </el-form>
  95. </template>
  96. </el-table-column>
  97. <el-table-column prop="action" label="操作" width="120" align="center">
  98. <template #default="{_,$index}">
  99. <el-button type="danger" plain @click="copyNodeTableDel($index)">删除</el-button>
  100. </template>
  101. </el-table-column>
  102. </el-table>
  103. </el-scrollbar>
  104. </div>
  105. </div>
  106. <template #footer>
  107. <div class="lr-dialog-footer">
  108. <div class="left">
  109. <template v-for="item in copyNodeTab">
  110. <el-button size="large" type="primary" plain v-if="item?.key === copyNodeTabKey" @click="copyNodeTabChange(item?.key)">{{item.name}}</el-button>
  111. <el-button size="large" text bg @click="copyNodeTabChange(item?.key)" v-else>{{item.name}}</el-button>
  112. </template>
  113. </div>
  114. <div class="right">
  115. <el-button size="large" @click="copyNodeModal = false">取消</el-button>
  116. <el-button type="primary" hc-btn :loading="copyNodeLoading" @click="copyNodeClick">提交</el-button>
  117. </div>
  118. </div>
  119. </template>
  120. </HcDialog>
  121. <!--新增子节点-->
  122. <HcDialog :show="addNodeModal" title="新增子节点" widths="720px" @close="addNodeModal = false">
  123. <el-alert title="双击节点,可编辑节点名称,编辑完成后,请按回车或输入框消失后,再点提交" type="warning" :closable="false"/>
  124. <HcTreeNode :projectId="projectId" :nodeId="addTreeNodeId" :oldId="addTreeNodeOldId" @check-change="addTreeNodeCheckChange" v-if="addTreeNodeType === '1'"/>
  125. <HcTreeNode :projectId="projectId" :nodeId="addTreeNodeId" :oldId="addTreeNodeOldId" strictly @check-change="addTreeNodeCheckChange" v-if="addTreeNodeType === '2'"/>
  126. <template #footer>
  127. <div class="lr-dialog-footer">
  128. <div class="left flex items-center">
  129. <div class="mr-4">选中方式:</div>
  130. <el-radio-group v-model="addTreeNodeType">
  131. <el-radio label="1">当前及子节点</el-radio>
  132. <el-radio label="2" class="ml-4">仅当前节点</el-radio>
  133. </el-radio-group>
  134. </div>
  135. <div class="right">
  136. <el-button size="large" @click="addNodeModal = false">取消</el-button>
  137. <el-button type="primary" hc-btn :loading="addNodeLoading" @click="addNodeClick">提交</el-button>
  138. </div>
  139. </div>
  140. </template>
  141. </HcDialog>
  142. <!--调整排序-->
  143. <HcDialog :show="sortNodeModal" title="调整排序" widths="700px" :loading="sortNodeLoading" @close="sortNodeModal = false" @save="sortNodeClick">
  144. <el-alert title="可拖动排序,也可在后面点击图标,切换排序" type="warning" :closable="false"/>
  145. <div class="sort-node-body-box list-group header">
  146. <div class="list-group-item">
  147. <div class="index-box">序号</div>
  148. <div class="title-box">节点名称</div>
  149. <div class="icon-box">排序</div>
  150. </div>
  151. </div>
  152. <Draggable class="sort-node-body-box list-group" ghost-class="ghost" :list="sortNodeData" item-key="id" @start="sortNodeDrag = true" @end="sortNodeDrag = false">
  153. <template #item="{element, index}">
  154. <div class="list-group-item">
  155. <div class="index-box">{{index + 1}}</div>
  156. <div class="title-box">{{element.title}}</div>
  157. <div class="icon-box">
  158. <span class="icon" @click="downSortClick(index)">
  159. <HcIcon name="arrow-down" ui="text-lg"/>
  160. </span>
  161. <span class="icon" @click="upSortClick(index)">
  162. <HcIcon name="arrow-up" ui="text-lg"/>
  163. </span>
  164. </div>
  165. </div>
  166. </template>
  167. </Draggable>
  168. </HcDialog>
  169. <!--导入划分模板-->
  170. <HcDialog :show="importTempModal" title="导入划分模板" widths="84%" ui="hc-modal-table" @close="importTempModal = false">
  171. <div class="hc-import-temp-box">
  172. <div class="hc-choose-type-box">
  173. <div class="text-title text-orange">请选择需要导入的工程类别:</div>
  174. <div class="hc-type-box">
  175. <el-radio-group v-model="importRadio" size="large">
  176. <el-radio v-for="item in importRadioData" :label="item.key">{{item.name}}</el-radio>
  177. </el-radio-group>
  178. </div>
  179. </div>
  180. <div class="hc-import-node-tree-box" v-loading="uploadLoading" element-loading-text="Loading...">
  181. <div class="import-node-tree-box">
  182. <div class="hc-tree-title-box">导入并识别成功</div>
  183. <div class="hc-tree-bar-box">
  184. <el-scrollbar>
  185. <HcTreeData :datas="matchedData"/>
  186. </el-scrollbar>
  187. </div>
  188. </div>
  189. <div class="import-node-tree-box">
  190. <div class="hc-tree-title-box">未被系统识别:手动关联</div>
  191. <div class="hc-tree-bar-box">
  192. <el-scrollbar>
  193. <HcTreeData1 :datas="unmatchedData" @relationTap="unmatchedTreeTap"/>
  194. </el-scrollbar>
  195. </div>
  196. </div>
  197. </div>
  198. </div>
  199. <template #footer>
  200. <div class="lr-dialog-footer">
  201. <div class="left">
  202. <HcUpload ref="uploadRef" :contractId="contractId" :type="importRadio" :isSplicingNumber="isSplicingNumber" @change="uploadChange" @progress="uploadprogress" @finished="uploadFinished"/>
  203. </div>
  204. <div class="right flex">
  205. <div class="mr-6">
  206. <span class="text-grey">是否拼接划分编号:</span>
  207. <el-switch v-model="isSplicingNumber" inline-prompt :active-value="1" active-text="是" :inactive-value="0" inactive-text="否" size="large"/>
  208. </div>
  209. <el-button size="large" @click="importTempModalClose">
  210. <HcIcon name="close"/>
  211. <span>取消</span>
  212. </el-button>
  213. <el-button type="primary" hc-btn :disabled="uploadLoading" :loading="uploadLoading" @click="importTempFolder">
  214. <HcIcon name="folder-upload"/>
  215. <span>导入模板</span>
  216. </el-button>
  217. </div>
  218. </div>
  219. </template>
  220. </HcDialog>
  221. <!--替换并关联节点-->
  222. <HcDialog :show="relationModal" title="替换并关联节点" widths="47rem" ui="hc-modal-table" saveText="确认关联" :loading="relationLoading" @close="relationModal = false" @save="relationSaveClick">
  223. <el-scrollbar>
  224. <DivisionTree :datas="unmatchedTreeData" @nodeTap="divisionTreeClick"/>
  225. </el-scrollbar>
  226. </HcDialog>
  227. </div>
  228. </template>
  229. <script setup>
  230. import {ref,watch,onMounted} from "vue";
  231. import {useAppStore} from "~src/store";
  232. import {useRouter, useRoute} from 'vue-router'
  233. import WbsTree from "./components/WbsTree.vue"
  234. import HcUpload from "./components/division/HcUpload.vue"
  235. import HcTreeData from "./components/division/HcTreeData.vue"
  236. import HcTreeData1 from "./components/division/HcTreeData1.vue"
  237. import DivisionTree from "./components/division/DivisionTree.vue"
  238. import HcTreeNode from "./components/HcTreeNode.vue"
  239. import {isType, getIndex, deepClone, formValidate} from "vue-utils-plus"
  240. import {getStoreData, setStoreData} from '~src/utils/storage'
  241. import {HcIsButton} from "~src/plugins/IsButtons";
  242. import {getDictionary} from "~api/other"
  243. import wbsApi from "~api/data-fill/wbs";
  244. import divisionApi from "~api/data-fill/division";
  245. import Draggable from "vuedraggable";
  246. //初始变量
  247. const router = useRouter()
  248. const useRoutes = useRoute()
  249. const useAppState = useAppStore()
  250. const {getObjValue, getArrValue} = isType()
  251. //全局变量
  252. const projectId = ref(useAppState.getProjectId);
  253. const contractId = ref(useAppState.getContractId);
  254. const projectInfo = ref(useAppState.getProjectInfo);
  255. const isCollapse = ref(useAppState.getCollapse)
  256. //监听
  257. watch(() => [
  258. useAppState.getCollapse
  259. ], ([Collapse]) => {
  260. isCollapse.value = Collapse
  261. })
  262. //自动展开缓存
  263. const treeAutoExpandKeys = ref(getStoreData('wbsTreeExpandKeys') || [])
  264. //渲染完成
  265. onMounted(() => {
  266. setElTreeMenu()
  267. getWbsNodeTypeApi()
  268. getMajorDataTypeApi()
  269. getWbsNodeTableTypeApi()
  270. })
  271. //获取节点类型
  272. const nodeTypeData = ref([])
  273. const getWbsNodeTypeApi = async () => {
  274. const { data } = await getDictionary({
  275. code: 'wbs_node_type'
  276. })
  277. //处理数据
  278. let newArr = []
  279. const newData = getArrValue(data)
  280. for (let i = 0; i < newData.length; i++) {
  281. newArr.push({
  282. label: newData[i]['dictValue'],
  283. value: Number(newData[i]['dictKey']),
  284. })
  285. }
  286. nodeTypeData.value = newArr
  287. }
  288. //获取类型名称
  289. const getRowType = (type) => {
  290. const nodeData = nodeTypeData.value
  291. const index = getIndex(nodeData, 'value', type)
  292. return nodeData[index]?.label ?? type
  293. }
  294. //获取资料类型
  295. const majorDataTypeData = ref([])
  296. const getMajorDataTypeApi = async () => {
  297. const { data } = await getDictionary({
  298. code: 'major_data_type'
  299. })
  300. //处理数据
  301. majorDataTypeData.value = getArrValue(data)
  302. }
  303. //获取资料类型名称
  304. const getRowMajorType = (type) => {
  305. if (type) {
  306. const nodeData = majorDataTypeData.value
  307. const index = getIndex(nodeData, 'dictKey', type)
  308. return nodeData[index]?.dictValue ?? type
  309. }
  310. }
  311. //获取表类型数据
  312. const nodeTableTypeData = ref([])
  313. const getWbsNodeTableTypeApi = async () => {
  314. const { data } = await getDictionary({
  315. code: 'table_type'
  316. })
  317. nodeTableTypeData.value = getArrValue(data)
  318. }
  319. //获取表类型
  320. const getRowTableType = (type) => {
  321. if (type > 0) {
  322. const nodeData = nodeTableTypeData.value
  323. const index = getIndex(nodeData, 'dictKey', type)
  324. return nodeData[index]?.dictValue ?? type
  325. } else {
  326. return ''
  327. }
  328. }
  329. //树被点击
  330. const treeItemInfo = ref({})
  331. const treeNodeInfo = ref({})
  332. const wbsElTreeClick = ({node, data, keys}) => {
  333. treeNodeInfo.value = node
  334. treeItemInfo.value = data
  335. tableBasicData.value = [data]
  336. setStoreData('wbsTreeExpandKeys',keys)
  337. treeAutoExpandKeys.value = keys || []
  338. if (node.level > 1) {
  339. searchNodeAllTableApi(data['primaryKeyId'])
  340. }
  341. }
  342. //当前节点基础信息
  343. const tableBasicColumn = ref([
  344. {key:'title', name: '节点名称'},
  345. {key:'partitionCode', name: '划分编号'},
  346. {key:'type', name: '节点类型'},
  347. {key:'majorDataType', name: '资料类型'}
  348. ])
  349. const tableBasicData = ref([])
  350. //当前节点工程用表信息
  351. const tableProjectColumn = ref([
  352. {key:'nodeName', name: '工程用表名称'},
  353. {key:'tableType', name: '用表类型'},
  354. {key:'taskStatusStr', name: '用表单位'},
  355. {key:'fillRate', name: '填报完整率'}
  356. ])
  357. const tableProjectData = ref([])
  358. //获取数据
  359. const searchNodeAllTableApi = async (pid) => {
  360. const {error, code, data} = await wbsApi.searchNodeAllTable({
  361. projectId: projectId.value,
  362. contractId: contractId.value,
  363. primaryKeyId: pid
  364. })
  365. if (!error && code === 200) {
  366. tableProjectData.value = getArrValue(data)
  367. } else {
  368. tableProjectData.value = []
  369. }
  370. }
  371. //树菜单配置
  372. const ElTreeMenu = ref([])
  373. const setElTreeMenu = () => {
  374. let newArr = [];
  375. if (HcIsButton('wbs_tree_add')) {
  376. newArr.push({icon: 'add-circle', label: '新增节点', key: "add"})
  377. }
  378. if (HcIsButton('wbs_tree_copy')) {
  379. newArr.push({icon: 'file-copy-2', label: '复制节点', key: "copy"})
  380. }
  381. if (HcIsButton('wbs_tree_edit')) {
  382. newArr.push({icon: 'draft', label: '修改节点', key: "edit"})
  383. }
  384. if (HcIsButton('wbs_tree_sort')) {
  385. newArr.push({icon: 'sort-asc', label: '调整排序', key: "sort"})
  386. }
  387. if (HcIsButton('wbs_tree_del')) {
  388. newArr.push({icon: 'delete-bin', label: '删除节点', key: "del"})
  389. }
  390. ElTreeMenu.value = newArr
  391. }
  392. //树菜单被点击
  393. const ElTreeMenuClick = async ({key,node,data}) => {
  394. treeNodeInfo.value = node
  395. treeItemInfo.value = data
  396. if (key === 'add') {
  397. addTreeNodeId.value = data?.id
  398. addTreeNodeOldId.value = data?.oldId
  399. addNodeLoading.value = false
  400. addNodeModal.value = true
  401. } else if (key === 'copy') {
  402. const parent = deepClone(node?.parent?.data || {})
  403. formCopyNodeModel.value = {...deepClone(data), parent: parent}
  404. copyNodeTabKey.value = '1'
  405. copyNodeTable.value = []
  406. copyNodeLoading.value = false
  407. copyNodeModal.value = true
  408. } else if (key === 'edit') {
  409. const parent = deepClone(node?.parent?.data || {})
  410. formEditNodeModel.value = {...deepClone(data), parent: parent}
  411. editNodeModal.value = true
  412. } else if (key === 'sort') {
  413. let nodes = [], childNodes = []
  414. childNodes = node?.parent?.childNodes || []
  415. for (let i = 0; i < childNodes.length; i++) {
  416. const res = childNodes[i]?.data
  417. nodes.push({
  418. id: res?.primaryKeyId,
  419. title: res?.title
  420. })
  421. }
  422. sortNodeData.value = nodes
  423. sortNodeModal.value = true
  424. } else if (key === 'del') {
  425. delModalClick()
  426. }
  427. }
  428. //编辑节点
  429. const editNodeModal = ref(false)
  430. const formEditNodeRef = ref(null)
  431. const formEditNodeModel = ref({})
  432. const formEditNodeRules = {
  433. title: {
  434. required: true,
  435. trigger: 'blur',
  436. message: "请输入节点名称"
  437. },
  438. }
  439. const editNodeLoading = ref(false)
  440. //保存编辑节点数据
  441. const editNodeClick = async () => {
  442. const validate = await formValidate(formEditNodeRef.value)
  443. if (validate) {
  444. //发起请求
  445. editNodeLoading.value = true
  446. const { primaryKeyId, title, partitionCode } = formEditNodeModel.value
  447. const { error, code } = await wbsApi.wbsTreeUpdateNode({
  448. nodeName: title || '',
  449. pKeyId: primaryKeyId || '',
  450. partitionCode: partitionCode || ''
  451. })
  452. //处理数据
  453. editNodeLoading.value = false
  454. if (!error && code === 200) {
  455. window?.$message?.success('修改成功')
  456. treeItemInfo.value['title'] = title || ''
  457. treeItemInfo.value['partitionCode'] = partitionCode || ''
  458. editNodeModal.value = false
  459. window?.location?.reload() //刷新页面
  460. }
  461. }
  462. }
  463. //复制节点
  464. const copyNodeModal = ref(false)
  465. //复制节点类型tab数据和相关处理
  466. const copyNodeTabKey = ref('1')
  467. const copyNodeTab = ref([
  468. {key:'1', name: '单份复制'},
  469. {key:'2', name: '多份复制'},
  470. {key:'3', name: '复制数据'}
  471. ]);
  472. const copyNodeTabChange = (key) => {
  473. if (key !== copyNodeTabKey.value) {
  474. copyNodeTabKey.value = key;
  475. copyNodeTable.value = []
  476. copyNodeLoading.value = false
  477. }
  478. }
  479. //复制节点变量
  480. const copyNodeLoading = ref(false)
  481. const formCopyNodeModel = ref({})
  482. const copyNodeTable = ref([])
  483. //复制树被点击
  484. const copyNodeElTreeClick = ({data}) => {
  485. const TabKey = copyNodeTabKey.value;
  486. const {title, type} = formCopyNodeModel.value;
  487. if (TabKey === '2') {
  488. //1 单位工程,2 分部工程,3 子分部工程,4 分项工程, 5 子分项工程,6 工序
  489. if (type === 6 && (data['type'] === 4 || data['type'] === 5 || data['type'] === 6)) {
  490. setCopyNodeTable(data, title)
  491. }
  492. if (type === 5 && data['type'] === 4) {
  493. setCopyNodeTable(data, title)
  494. }
  495. if (type === 4 && (data['type'] === 2 || data['type'] === 3)) {
  496. setCopyNodeTable(data, title)
  497. }
  498. if (type === 3 && data['type'] === 2) {
  499. setCopyNodeTable(data, title)
  500. }
  501. if (type === 2 && data['type'] === 1) {
  502. setCopyNodeTable(data, title)
  503. }
  504. if (type === 1 && data['type'] === 1) {
  505. setCopyNodeTable(data, title)
  506. }
  507. } else if (TabKey === '3') {
  508. if (data['notExsitChild']) {
  509. setCopyNodeTable(data, data?.title)
  510. }
  511. }
  512. }
  513. const setCopyNodeTable = (data, title) => {
  514. copyNodeTable.value.push({
  515. title: data?.title || '',
  516. nodeName: title || '',
  517. primaryKeyId: data?.primaryKeyId || ''
  518. })
  519. }
  520. //节点表单
  521. const formCopyNodeModelRef = ref(null)
  522. const formCopyNodeModelRules = {
  523. title: {
  524. required: true,
  525. trigger: "blur",
  526. message: "请输入节点名称"
  527. }
  528. }
  529. //表格节点表单
  530. const copyNodeTableRef = ref(null)
  531. const copyNodeTableRules = {
  532. nodeName: {
  533. required: true,
  534. trigger: "blur",
  535. message: "请输入节点名称"
  536. }
  537. }
  538. //删除选中的节点
  539. const copyNodeTableDel = (index) => {
  540. copyNodeTable.value.splice(index,1)
  541. }
  542. //复制节点
  543. const copyNodeClick = async () => {
  544. const type = copyNodeTabKey.value
  545. const form = formCopyNodeModel.value
  546. const table = copyNodeTable.value
  547. //效验数据
  548. if (type === '1') {
  549. const validate = await formValidate(formCopyNodeModelRef.value)
  550. if (validate) await copyContractTreeNode(type, form,[])
  551. } else if (type === '2') {
  552. if (table.length > 0) {
  553. const validate = await formValidate(copyNodeTableRef.value)
  554. if (validate) await copyContractTreeNode(type, form,table)
  555. } else {
  556. window?.$message?.warning('请先在左侧选择要复制到的节点')
  557. }
  558. } else if (type === '3') {
  559. if (table.length > 0) {
  560. await copyContractNodeSubmitBusinessData(form,table)
  561. } else {
  562. window?.$message?.warning('请先在左侧选择要复制的节点')
  563. }
  564. }
  565. }
  566. //单个复制、多份复制请求
  567. const copyContractTreeNode = async (type, form,table) => {
  568. copyNodeLoading.value = true
  569. const {error, code} = await wbsApi.copyContractTreeNode({
  570. copyType: type,
  571. needCopyNodeName: form?.title || '',
  572. needCopyPrimaryKeyId: form?.primaryKeyId || '',
  573. parentPrimaryKeyId: form?.parent?.primaryKeyId || '',
  574. copyBatchToPaths: table
  575. })
  576. //判断状态
  577. if (!error && code === 200) {
  578. window?.$message?.success('复制成功')
  579. copyNodeLoading.value = false
  580. copyNodeModal.value = false
  581. window?.location?.reload() //刷新页面
  582. }
  583. }
  584. //复制数据
  585. const copyContractNodeSubmitBusinessData = async (form,table) => {
  586. copyNodeLoading.value = true
  587. const {error, code} = await wbsApi.copyContractNodeSubmitBusinessData({
  588. needCopyPrimaryKeyId: form?.primaryKeyId || '',
  589. copyBatchToPaths: table
  590. })
  591. //判断状态
  592. if (!error && code === 200) {
  593. window?.$message?.success('复制成功')
  594. copyNodeLoading.value = false
  595. copyNodeModal.value = false
  596. window?.location?.reload() //刷新页面
  597. }
  598. }
  599. //新增节点
  600. const addNodeModal = ref(false)
  601. const addTreeNodeId = ref('')
  602. const addTreeNodeOldId = ref('')
  603. const addTreeNodeType = ref('1')
  604. //选中的节点
  605. const allSelectedList = ref([])
  606. const halfSelectedList = ref([])
  607. const addTreeNodeCheckChange = (nodes) => {
  608. let NodesArr = [], halfArr = []
  609. //全选数据
  610. const keys = nodes.checkedNodes || []
  611. for (let i = 0; i < keys.length; i++) {
  612. NodesArr.push({
  613. nodeName: keys[i].title,
  614. primaryKeyId: keys[i].primaryKeyId
  615. })
  616. }
  617. allSelectedList.value = NodesArr
  618. //半选数据
  619. const halfNodes = nodes.halfCheckedNodes || []
  620. for (let i = 0; i < halfNodes.length; i++) {
  621. halfArr.push({
  622. nodeName: halfNodes[i].title,
  623. primaryKeyId: halfNodes[i].primaryKeyId
  624. })
  625. }
  626. halfSelectedList.value = halfArr
  627. }
  628. //新增节点
  629. const addNodeLoading = ref(false)
  630. const addNodeClick = async () => {
  631. const keys = allSelectedList.value || []
  632. if (keys.length <= 0) {
  633. window?.$message?.warning('请先选择节点')
  634. } else {
  635. //发起请求
  636. addNodeLoading.value = true
  637. const primaryKeyId = treeItemInfo.value?.primaryKeyId || ''
  638. const {error, code} = await wbsApi.saveContractTreeNode({
  639. projectId: projectId.value,
  640. contractId: contractId.value,
  641. saveType: addTreeNodeType.value,
  642. allSelectedList: allSelectedList.value,
  643. halfSelectedList: halfSelectedList.value,
  644. currentNodePrimaryKeyId: primaryKeyId
  645. })
  646. //判断状态
  647. addNodeLoading.value = false
  648. if (!error && code === 200) {
  649. window?.$message?.success('新增成功')
  650. addNodeModal.value = false
  651. window?.location?.reload() //刷新页面
  652. }
  653. }
  654. }
  655. //删除节点
  656. const delModalClick = () => {
  657. window?.$messageBox?.alert('请谨慎考虑后,确认是否需要删除?', '删除节点', {
  658. showCancelButton: true,
  659. confirmButtonText: '确认删除',
  660. cancelButtonText: '取消',
  661. callback: (action) => {
  662. if (action === 'confirm') {
  663. removeContractTreeNode()
  664. }
  665. }
  666. })
  667. }
  668. const removeContractTreeNode = async () => {
  669. const {error, code} = await wbsApi.removeContractTreeNode({
  670. ids: treeItemInfo.value?.primaryKeyId || ''
  671. })
  672. if (!error && code === 200) {
  673. window?.$message?.success('删除成功')
  674. window?.location?.reload() //刷新页面
  675. }
  676. }
  677. //调整排序
  678. const sortNodeModal = ref(false)
  679. const sortNodeLoading = ref(false)
  680. const sortNodeData = ref([])
  681. const sortNodeDrag = ref(false)
  682. //向下
  683. const downSortClick = (index) => {
  684. const indexs = index + 1
  685. const data = sortNodeData.value || []
  686. if(indexs !== data.length) {
  687. const tmp = data.splice(indexs,1);
  688. sortNodeData.value.splice(index,0,tmp[0]);
  689. } else {
  690. window?.$message?.warning('已经处于置底,无法下移')
  691. }
  692. }
  693. //向上
  694. const upSortClick = (index) => {
  695. const data = sortNodeData.value || []
  696. if(index !== 0) {
  697. const tmp = data.splice(index - 1,1);
  698. sortNodeData.value.splice(index,0,tmp[0]);
  699. } else {
  700. window?.$message?.warning('已经处于置顶,无法上移')
  701. }
  702. }
  703. //确认排序
  704. const sortNodeClick = async () => {
  705. const sortList = [];
  706. const nodes = sortNodeData.value || []
  707. nodes.forEach(item => {
  708. sortList.push(item?.id)
  709. })
  710. //发起请求
  711. sortNodeLoading.value = true
  712. const { error, code } = await wbsApi.diySortTreeNode({sortList})
  713. sortNodeLoading.value = false
  714. //判断状态
  715. if (!error && code === 200) {
  716. window?.$message?.success('保存成功')
  717. sortNodeModal.value = false
  718. window?.location?.reload() //刷新页面
  719. }
  720. }
  721. //导入模板
  722. const uploadRef = ref(null)
  723. const importTempModal = ref(false)
  724. const toImportTempClick = () => {
  725. importTempModal.value = true
  726. getContractInfoTreeApi()
  727. }
  728. //导入类型
  729. const importRadio = ref(1)
  730. const importRadioData = ref([
  731. {key: 1, name: '路基工程'},
  732. {key: 2, name: '路面工程'},
  733. {key: 3, name: '桥梁工程'},
  734. {key: 4, name: '机电工程'},
  735. {key: 5, name: '绿化工程'},
  736. {key: 6, name: '隧道工程'},
  737. {key: 7, name: '声屏障工程'},
  738. {key: 8, name: '交通与安全工程'},
  739. {key: 9, name: '特大斜拉桥、特大悬索桥'},
  740. ])
  741. const isSplicingNumber = ref(0)
  742. //上传文件被改变
  743. const uploadFile = ref(null)
  744. const uploadChange = (file) => {
  745. uploadFile.value = file
  746. }
  747. //上传中
  748. const uploadLoading = ref(false)
  749. const uploadprogress = (state) => {
  750. console.log(state)
  751. uploadLoading.value = state
  752. }
  753. //上传完成
  754. const matchedData = ref([])
  755. const unmatchedData = ref([])
  756. const uploadFinished = ({type, data}) => {
  757. uploadRef.value?.clearFiles()
  758. if (type === 'success') {
  759. uploadFile.value = null
  760. window?.$message?.success('导入成功');
  761. matchedData.value = getArrValue(data['matchedData'])
  762. unmatchedData.value = getArrValue(data['unmatchedData'])
  763. } else {
  764. uploadFile.value = null
  765. matchedData.value = []
  766. unmatchedData.value = []
  767. }
  768. }
  769. //关闭导入弹窗
  770. const importTempModalClose = () => {
  771. uploadFile.value = null
  772. importTempModal.value = false
  773. }
  774. //确认导入
  775. const importTempFolder = () => {
  776. if (uploadFile.value) {
  777. uploadRef.value?.submit()
  778. } else {
  779. window?.$message?.warning('请先选择文件');
  780. }
  781. }
  782. //关联被点击
  783. const relationModal = ref(false)
  784. const relationItemInfo = ref({})
  785. const unmatchedTreeTap = ({data}) => {
  786. relationItemInfo.value = data
  787. relationModal.value = true
  788. }
  789. //获取导入树
  790. const unmatchedTreeData = ref([])
  791. const getContractInfoTreeApi = async () => {
  792. const {error, code, data} = await divisionApi.getContractInfoTree({
  793. projectId: projectId.value,
  794. contractId: contractId.value,
  795. wbsId: ''
  796. })
  797. //判断状态
  798. if (!error && code === 200) {
  799. unmatchedTreeData.value = getArrValue(data)
  800. } else {
  801. unmatchedTreeData.value = []
  802. }
  803. }
  804. //关联树
  805. const divisionTreeItemInfo = ref({})
  806. const divisionTreeClick = ({data}) => {
  807. divisionTreeItemInfo.value = data
  808. }
  809. //确认关联
  810. const relationLoading = ref(false)
  811. const relationSaveClick = () => {
  812. const item = relationItemInfo.value
  813. const info = divisionTreeItemInfo.value
  814. const primaryKeyId = info?.primaryKeyId ?? ''
  815. if (!primaryKeyId) {
  816. window?.$message?.warning('请先选择一个树节点')
  817. } else {
  818. setImportRelationApi({
  819. pKeyIdOld: info?.primaryKeyId,
  820. wbsTreeContractVO: {
  821. ...item,
  822. id: ''
  823. }
  824. })
  825. }
  826. }
  827. const setImportRelationApi = async (form) => {
  828. relationLoading.value = true
  829. const { error, code } = await divisionApi.setImportRelation(form)
  830. relationLoading.value = false
  831. if (!error && code === 200) {
  832. window.$message?.success('关联成功')
  833. relationModal.value = false
  834. }
  835. }
  836. //下载模板
  837. const downloadXlsx = () => {
  838. window.open('https://bladex-test-info.oss-cn-chengdu.aliyuncs.com//upload/20220928/46c85506c6719b70cfbd192f59fe95b6.xlsx','_blank')
  839. }
  840. //返回上页
  841. const toBackClick = () => {
  842. router.push({path: '/data-fill/wbs'})
  843. }
  844. //左右拖动,改变树形结构宽度
  845. const leftWidth = ref(382);
  846. const onmousedown = () => {
  847. const leftNum = isCollapse.value ? 142 : 272
  848. document.onmousemove = (ve) => {
  849. let diffVal = ve.clientX - leftNum;
  850. if(diffVal >= 310 && diffVal <= 900) {
  851. leftWidth.value = diffVal;
  852. }
  853. }
  854. document.onmouseup = () => {
  855. document.onmousemove = null;
  856. document.onmouseup = null;
  857. }
  858. }
  859. </script>
  860. <style lang="scss" scoped>
  861. @import "../../styles/data-fill/division.scss";
  862. </style>