unit.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. <template>
  2. <div class="relative h-full flex">
  3. <div :id="`hc_tree_card_${uuid}`">
  4. <hc-new-card scrollbar>
  5. <template #header>
  6. <el-button hc-btn type="primary" :loading="setLoading" @click="setTree">重新设置treeCode</el-button>
  7. </template>
  8. <hc-lazy-tree
  9. v-if="ishowTree"
  10. :auto-expand-keys="TreeAutoExpandKeys"
  11. tree-key="id"
  12. :h-props="treeProps"
  13. is-load-menu
  14. @load="treeLoadNode"
  15. @loadMenu="treeLoadMenu"
  16. @menuTap="treeMenuTap"
  17. @nodeTap="treeNodeTap"
  18. />
  19. </hc-new-card>
  20. </div>
  21. <div :id="`hc_table_card_${uuid}`" class="flex-1">
  22. <hc-new-card scrollbar title="合同计量单元">
  23. <template #extra>
  24. <el-button hc-btn type="primary" @click="editModalShow = true">修改</el-button>
  25. <el-button hc-btn type="danger">删除</el-button>
  26. <el-button hc-btn type="warning" @click="treeModalShow = true">增补单元</el-button>
  27. <el-button hc-btn type="success">导入</el-button>
  28. </template>
  29. <div class="relative">
  30. <infoTable :info-data="curTreeData" />
  31. <HcTitle title="清单分解汇总列表">
  32. <template #extra>
  33. <div class="text-sm text-orange">温馨提示:累计分解量 > 合同变更后量,整行文字红色</div>
  34. </template>
  35. </HcTitle>
  36. <div style="height: calc(100vh - 420px);">
  37. <hc-table :column="tableColumn" :datas="tableData" :loading="tableLoading" is-new :index-style="{ width: 60 }" :row-style="tableRowStyle">
  38. <template #key1="{ row }">
  39. <i class="i-iconoir-open-select-hand-gesture inline-block" />
  40. </template>
  41. </hc-table>
  42. </div>
  43. </div>
  44. </hc-new-card>
  45. </div>
  46. <!-- 节点新增和编辑 -->
  47. <treeForm v-model="treeModalShow" :ids="curTreeData.id" :menu-type="menuType" :template-id="curTreeData.templateId" @finish="finishForm" />
  48. <!-- 修改合同计量单元 -->
  49. <rowData v-model="editModalShow" :is-table="isInfoView" :ids="curTreeData.id" :cur-tree-data="curTreeData" @finish="finishEdit" />
  50. <!-- 调整排序 -->
  51. <hc-new-dialog v-model="sortModalShow" is-table widths="1100px" title="调整排序" :loading="sortNodeLoading" @save="sortModalSave">
  52. <hc-table
  53. ui="hc-table-row-drop"
  54. :column="sortTableColumn" :datas="sortTableData" :loading="sortTableLoading"
  55. is-row-drop quick-sort is-new :index-style="{ width: 80 }"
  56. @row-drop="sortTableRowDrop" @row-sort="sortTableRowDrop"
  57. >
  58. <template #key2="{ row }">
  59. <span class="text-link">{{ row?.key2 }}</span>
  60. </template>
  61. <template #action="{ index }">
  62. <span class="text-link text-xl" @click="upSortClick(index)">
  63. <HcIcon name="arrow-up" fill />
  64. </span>
  65. <span class="text-link ml-2 text-xl" @click="downSortClick(index)">
  66. <HcIcon name="arrow-down" fill />
  67. </span>
  68. </template>
  69. </hc-table>
  70. </hc-new-dialog>
  71. </div>
  72. </template>
  73. <script setup>
  74. import { nextTick, onMounted, ref } from 'vue'
  75. import { arrToId, getArrValue, getObjValue, getRandom } from 'js-fast-way'
  76. import infoTable from './components/unit/info-table.vue'
  77. import treeForm from './components/unit/tree-form.vue'
  78. import rowData from './components/unit/row-data.vue'
  79. import unitApi from '~api/project/debit/contract/unit.js'
  80. import { useAppStore } from '~src/store'
  81. import { getStoreValue, setStoreValue } from '~src/utils/storage'
  82. import { delMessageV2 } from '~com/message/index.js'
  83. import { getDictionary } from '~api/other'
  84. const useAppState = useAppStore()
  85. const projectId = ref(useAppState.getProjectId || '')
  86. const contractId = ref(useAppState.getContractId || '')
  87. defineOptions({
  88. name: 'ProjectDebitContractUnit',
  89. })
  90. const uuid = getRandom(4)
  91. //渲染完成
  92. onMounted(() => {
  93. setSplitRef()
  94. // getNodeType()
  95. })
  96. //初始化设置拖动分割线
  97. const setSplitRef = () => {
  98. //配置参考: https://split.js.org/#/?direction=vertical&snapOffset=0
  99. nextTick(() => {
  100. window.$split(['#hc_tree_card_' + uuid, '#hc_table_card_' + uuid], {
  101. sizes: [20, 80],
  102. snapOffset: 0,
  103. minSize: [50, 500],
  104. })
  105. })
  106. }
  107. //获节点类型
  108. const nodeOptions = ref([])
  109. const getNodeType = async (id) => {
  110. const { data } = await unitApi.getNodeTypeList({
  111. id,
  112. })
  113. nodeOptions.value = getArrValue(data)
  114. nodeOptions.value.forEach((ele)=>{
  115. ele.dictKey = Number(ele.dictKey)
  116. })
  117. }
  118. //搜索表单
  119. const searchForm = ref({})
  120. //数据格式
  121. const treeProps = {
  122. label: 'nodeName',
  123. children: 'children',
  124. isLeaf: 'notExsitChild',
  125. }
  126. const ishowTree = ref(true)
  127. //重新设置树
  128. const setLoading = ref(false)
  129. const setTree = async ()=>{
  130. const { error, code, msg } = await unitApi.refresh({
  131. projectId: projectId.value,
  132. contractId:contractId.value,
  133. })
  134. setLoading.value = false
  135. if (!error && code === 200) {
  136. window.$meaasge.sucess(msg)
  137. ishowTree.value = false
  138. setTimeout(() => {
  139. ishowTree.value = true
  140. }, 100)
  141. } else {
  142. // newlistdata.value = []
  143. }
  144. }
  145. //懒加载的数据
  146. const TreeAutoExpandKeys = ref(getStoreValue('wbsTreeExpandKeys') || [])
  147. const treeLoadNode = async ({ node, item, level }, resolve) => {
  148. let id = 0
  149. if (level !== 0) {
  150. const nodeData = getObjValue(item)
  151. id = nodeData?.id || ''
  152. }
  153. //获取数据
  154. const { error, code, data } = await unitApi.lazyTree({
  155. contractId: contractId.value,
  156. id:id,
  157. })
  158. resolve(getArrValue(data))
  159. }
  160. //节点点击
  161. const isInfoView = ref(false)
  162. const treeNodeTap = ({ node, data, keys }) => {
  163. getNodeType(data.id)
  164. isInfoView.value = node.isLeaf
  165. TreeAutoExpandKeys.value = keys || []
  166. setStoreValue('wbsTreeExpandKeys', keys)
  167. getTreeNodeDetail(data)
  168. }
  169. //获取节点详情
  170. const curTreeData = ref({})
  171. const getTreeNodeDetail = async (node)=>{
  172. const { id } = node
  173. const { error, code, data } = await unitApi.getNodeDetail({
  174. id,
  175. })
  176. if (!error && code === 200) {
  177. curTreeData.value = getObjValue(data)
  178. tableData.value = curTreeData.value['decompositionList']
  179. nodeOptions.value.forEach((ele)=>{
  180. if (curTreeData.value.nodeType === ele.dictKey) {
  181. curTreeData.value.nodeTypeName = ele.dictValue
  182. }
  183. })
  184. } else {
  185. curTreeData.value = {}
  186. tableData.value = []
  187. }
  188. }
  189. //菜单
  190. const treeLoadMenu = ({ item, level }, resolve) => {
  191. const { isLock } = item
  192. if (level === 1) {
  193. if (isLock === 1) {
  194. return resolve([
  195. { icon: 'lock', label: '解锁', key: 'lock' },
  196. { icon: 'upload-cloud', label: '导入', key: 'lead' },
  197. { icon: 'add', label: '新增', key: 'add' },
  198. { icon: 'arrow-up-down-line', label: '排序', key: 'sort' },
  199. ])
  200. } else {
  201. return resolve([
  202. { icon: 'lock', label: '锁定', key: 'lock' },
  203. { icon: 'upload-cloud', label: '导入', key: 'lead' },
  204. { icon: 'add', label: '新增', key: 'add' },
  205. { icon: 'arrow-up-down-line', label: '排序', key: 'sort' },
  206. ])
  207. }
  208. } else {
  209. if (isLock === 1) {
  210. return resolve([
  211. { icon: 'lock', label: '解锁', key: 'lock' },
  212. { icon: 'upload-cloud', label: '导入', key: 'lead' },
  213. { icon: 'add', label: '新增', key: 'add' },
  214. { icon: 'pencil', label: '修改', key: 'edit' },
  215. { icon: 'arrow-up-down-line', label: '排序', key: 'sort' },
  216. { icon: 'close', label: '删除', key: 'del' },
  217. ])
  218. } else {
  219. return resolve([
  220. { icon: 'lock', label: '锁定', key: 'lock' },
  221. { icon: 'upload-cloud', label: '导入', key: 'lead' },
  222. { icon: 'add', label: '新增', key: 'add' },
  223. { icon: 'pencil', label: '修改', key: 'edit' },
  224. { icon: 'arrow-up-down-line', label: '排序', key: 'sort' },
  225. { icon: 'close', label: '删除', key: 'del' },
  226. ])
  227. }
  228. }
  229. }
  230. const menuType = ref('')
  231. const treeMenuTap = ({ key, node, data, keys }) => {
  232. isInfoView.value = node.isLeaf
  233. menuType.value = key
  234. getTreeNodeDetail(data)
  235. setStoreValue('wbsTreeExpandKeys', keys)
  236. TreeAutoExpandKeys.value = keys || []
  237. if (data?.isLock !== 1) {
  238. if (key === 'add') {
  239. treeModalShow.value = true
  240. }
  241. if (key === 'edit') {
  242. editModalShow.value = true
  243. }
  244. if (key === 'sort') {
  245. let nodes = [], childNodes = []
  246. childNodes = node?.parent?.childNodes || node?.parent?.children || []
  247. for (let i = 0; i < childNodes.length; i++) {
  248. const res = childNodes[i]?.data
  249. nodes.push({
  250. nodeName:res?.nodeName,
  251. id:res?.id,
  252. })
  253. }
  254. sortTableData.value = nodes
  255. sortModalShow.value = true
  256. }
  257. if (key === 'del') {
  258. delModalClick()
  259. }
  260. }
  261. if (data?.isLock === 1 && key !== 'lock') {
  262. window.$message.warning('当前节点为锁定状态,不允许操作')
  263. }
  264. if (key === 'lock') {
  265. handleLockNode()
  266. }
  267. }
  268. //锁定节点
  269. const handleLockNode = async ()=>{
  270. const { error, code, msg } = await unitApi.getLock({
  271. id: curTreeData.value.id || '',
  272. lockStatus:curTreeData.value?.isLock === 1 ? 0 : 1,
  273. })
  274. if (!error && code === 200) {
  275. window?.$message?.success(msg)
  276. window?.location?.reload() //刷新页面
  277. }
  278. }
  279. //删除节点
  280. const delModalClick = () => {
  281. delMessageV2(async (action, instance, done) => {
  282. if (action === 'confirm') {
  283. instance.confirmButtonLoading = true
  284. removeContractTreeNode()
  285. instance.confirmButtonLoading = false
  286. done()
  287. } else {
  288. done()
  289. }
  290. })
  291. }
  292. const removeContractTreeNode = async () => {
  293. const loadingInstance = window.$loading.service({
  294. fullscreen: true,
  295. text: '删除节点中,请耐心等待...',
  296. background: 'rgba(0, 0, 0, 0.7)',
  297. })
  298. const { error, code } = await unitApi.deleteNode({
  299. id: curTreeData.value.id || '',
  300. })
  301. loadingInstance.close()
  302. if (!error && code === 200) {
  303. window?.$message?.success('删除成功')
  304. window?.location?.reload() //刷新页面
  305. }
  306. }
  307. //表格数据
  308. const tableLoading = ref(false)
  309. const tableColumn = ref([
  310. { key: 'key1', name: '分解明细', width: 80, align: 'center' },
  311. { key: 'formNumber', name: '清单编号', width: 120, align: 'center' },
  312. { key: 'formName', name: '清单名称', align: 'center' },
  313. { key: 'currentPrice', name: '单价', width: 90, align: 'center' },
  314. { key: 'contractTotal', name: '合同数量', width: 100, align: 'center' },
  315. { key: 'changeTotal', name: '变更后数量', width: 110, align: 'center' },
  316. { key: 'poseNum', name: '施工图数量', align: 'center' },
  317. { key: 'changeTotal', name: '施工图变更后数量', align: 'center' },
  318. ])
  319. const tableData = ref([
  320. ])
  321. //设置某一行的样式
  322. const tableRowStyle = ({ row, rowIndex }) => {
  323. if (row.poseNum > row.contractTotal) {
  324. return '--el-table-tr-bg-color: #fe0000; color:white'
  325. }
  326. }
  327. //弹窗
  328. const treeModalShow = ref(false)
  329. const editModalShow = ref(false)
  330. const finishForm = ()=>{
  331. treeModalShow.value = false
  332. ishowTree.value = false
  333. setTimeout(() => {
  334. ishowTree.value = true
  335. }, 100)
  336. }
  337. const finishEdit = ()=>{
  338. editModalShow.value = false
  339. ishowTree.value = false
  340. setTimeout(() => {
  341. ishowTree.value = true
  342. }, 100)
  343. }
  344. //调整排序
  345. const sortModalShow = ref(false)
  346. //表格数据
  347. const sortTableColumn = ref([
  348. { key:'nodeName', name: '节点名称' },
  349. { key:'action', name: '排序', width: 90 },
  350. ])
  351. const sortTableLoading = ref(false)
  352. const sortTableData = ref([])
  353. //拖动完成
  354. const sortTableRowDrop = (rows) => {
  355. sortTableData.value = [] // 先清空,否则排序会异常
  356. nextTick(() => {
  357. sortTableData.value = rows
  358. })
  359. }
  360. //向下
  361. const downSortClick = (index) => {
  362. const indexs = index + 1
  363. const data = sortTableData.value
  364. if (indexs !== data.length) {
  365. const tmp = data.splice(indexs, 1)
  366. sortTableData.value.splice(index, 0, tmp[0])
  367. } else {
  368. window?.$message?.warning('已经处于置底,无法下移')
  369. }
  370. }
  371. //向上
  372. const upSortClick = (index) => {
  373. const data = sortTableData.value || []
  374. if (index !== 0) {
  375. const tmp = data.splice(index - 1, 1)
  376. sortTableData.value.splice(index, 0, tmp[0])
  377. } else {
  378. window?.$message?.warning('已经处于置顶,无法上移')
  379. }
  380. }
  381. const sortNodeLoading = ref(false)
  382. const sortModalSave = async () => {
  383. const ids = arrToId(sortTableData.value)
  384. //发起请求
  385. sortNodeLoading.value = true
  386. const { error, code } = await unitApi.sortForm({ ids })
  387. sortNodeLoading.value = false
  388. //判断状态
  389. if (!error && code === 200) {
  390. window?.$message?.success('保存成功')
  391. sortModalShow.value = false
  392. window?.location?.reload() //刷新页面
  393. }
  394. }
  395. </script>