unit.vue 13 KB

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