index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. <template>
  2. <hc-drawer v-model="isShow" to-id="hc-main-box" is-close @close="drawerClose">
  3. <hc-body split :options="{ sizes: [14, 96] }">
  4. <template #left>
  5. <hc-card scrollbar>
  6. <h3 class="mb-2">引用元素表</h3>
  7. <ElTree v-if="isShowTree" ref="treeRef" node-key="id" :load="treeLoadNode" :props="treeProps" accordion highlight-current lazy :default-expanded-keys="defaultExpandKey" @node-click="treeNodeTap" />
  8. </hc-card>
  9. </template>
  10. <hc-card>
  11. <template #header>
  12. <div class="w-400px">
  13. <hc-search-input v-model="searchForm.titleName" @search="searchClick" />
  14. </div>
  15. </template>
  16. <template #extra>
  17. <el-button hc-btn type="primary" @click="quteEleTableClick">引用元素表单库</el-button>
  18. <el-button hc-btn type="danger" :loading="batchDelLoad" @click="batchDel">删除元素表</el-button>
  19. </template>
  20. <hc-table
  21. :column="tableColumn" :datas="tableData" :loading="tableLoading" :index-style="{ width: 60 }"
  22. is-check :check-style="{ width: 29 }" @selection-change="tableCheckChange"
  23. >
  24. <template #action="{ row }">
  25. <el-link type="primary" @click="linkExcelClick(row)">关联清表</el-link>
  26. <el-link v-loading="editElementLoading" type="primary" :disabled="row.excelIds == -1" @click="editElement(row)">编辑元素</el-link>
  27. <el-link v-loading="adjustExcelLoading" type="primary" :disabled="row.excelIds == -1" @click="adjustExcelClick(row)">调整表单</el-link>
  28. <el-link type="primary" @click="rowFormulaClick(row)">编辑公式</el-link>
  29. <el-link type="primary" @click="assignWbs(row)">分配WBS</el-link>
  30. </template>
  31. </hc-table>
  32. <template #action>
  33. <hc-pages :pages="searchForm" @change="pageChange" />
  34. </template>
  35. </hc-card>
  36. </hc-body>
  37. <!-- 引用元素表 -->
  38. <quteElePage v-model="quteEleShow" :project-id="projectId" @close="quteEleClose" />
  39. <!-- 关联清表 -->
  40. <HcAssociationList v-model="isAssociationShow" :info="associationInfo" :type="2" @close="linkClose" />
  41. <!-- 编辑元素 -->
  42. <HcEditElement v-model="isEditElementShow" :info="editElementInfo" :data="editElementData" :type="2" @to-page="editElementToPage" />
  43. <!-- 调整表单 -->
  44. <HcAdjustExcel v-model="isAdjustExcelShow" :info="adjustExcelInfo" :type="2" />
  45. <!-- 分配WBS -->
  46. <allocateWbs v-model="isAllocateShow" :wbs-type="wbsType" :project-id="projectId" :data="allocateWbsInfo" />
  47. <!-- 编辑元素公式 -->
  48. <hc-dialog v-model="elementFormulasShow" :footer="false" title="元素公式" is-table widths="660px" @close="elementFormulasClose">
  49. <template #search>
  50. <hc-search-input v-model="formulaInput" @search="searchFormulaClick" />
  51. </template>
  52. <hc-table :column="formulaTableColumn" :datas="formulaTableData" :is-current-row="false" :is-index="false">
  53. <template #action="{ row }">
  54. <el-link :type="row.globalFormula === 1 ? 'warning' : 'primary'" @click="toFormulaGlobal(row)">全局公式</el-link>
  55. </template>
  56. </hc-table>
  57. </hc-dialog>
  58. <!-- 编辑公式 -->
  59. <HcEditFormula v-model="isEditFormulaShow" :data="editFormulaData" @finish="editFormulaFinish" />
  60. </hc-drawer>
  61. </template>
  62. <script setup>
  63. import { nextTick, ref, watch } from 'vue'
  64. import { HcDelMsg } from 'hc-vue3-ui'
  65. import { arrToId, deepClone, getArrValue, isNullES } from 'js-fast-way'
  66. import HcEditFormula from '~src/views/project/list/edit-formula.vue'
  67. import quteElePage from './quteElePage.vue'
  68. import HcAssociationList from '../association-list.vue'
  69. import HcEditElement from '../edit-element.vue'
  70. import HcAdjustExcel from '../adjust-excel.vue'
  71. import allocateWbs from './allocateWbs.vue'
  72. import privateApi from '~api/wbs/private'
  73. import excelApi from '~api/exctab/exceltab'
  74. import treeApi from '~api/wbs/tree'
  75. const props = defineProps({
  76. projectId: {
  77. type: String,
  78. default: '',
  79. },
  80. wbsType: {
  81. type: [String, Number],
  82. default: '',
  83. },
  84. wbsId: {
  85. type: [String, Number],
  86. default: '',
  87. },
  88. })
  89. //事件
  90. const emit = defineEmits(['close'])
  91. //双向绑定
  92. const isShow = defineModel('modelValue', {
  93. default: false,
  94. })
  95. //监听显示
  96. watch(isShow, (val) => {
  97. if (val) getDataApi()
  98. })
  99. const projectId = ref(props.projectId)
  100. const wbsId = ref(props.wbsId)
  101. const wbsType = ref(props.wbsType)
  102. //监听数据
  103. watch(
  104. () => [props.projectId, props.wbsId, props.wbsType],
  105. ([pid, wid, wtype]) => {
  106. projectId.value = pid
  107. wbsId.value = wid
  108. wbsType.value = wtype
  109. },
  110. { deep: true },
  111. )
  112. //处理相关数据
  113. const getDataApi = () => {}
  114. //获取数据
  115. const tabTypeLazyTree = async (parentId = '12345678910') => {
  116. //发起请求
  117. const { data } = await privateApi.tabTypeLazyTree({
  118. parentId,
  119. current: 1,
  120. size: 1000,
  121. projectId: projectId.value,
  122. })
  123. const records = getArrValue(data?.records)
  124. records.forEach((item) => {
  125. item.isLeaf = !item.hasChildren
  126. })
  127. return { data: records, total: data?.total }
  128. }
  129. const isShowTree = ref(true)
  130. const refreshTree = () => {
  131. isShowTree.value = false
  132. setTimeout(() => {
  133. isShowTree.value = true
  134. nextTick(()=>{
  135. defaultExpandKey.value.push(nodeInfo.value.id)
  136. treeRef.value.setCurrentKey(nodeInfo.value.id)
  137. })
  138. }, 1000)
  139. }
  140. const treeLoadNode = async (node, resolve) => {
  141. if (node.level === 0) {
  142. const resData = await tabTypeLazyTree()
  143. resolve(resData?.data)
  144. } else {
  145. const resData = await tabTypeLazyTree(
  146. node?.data?.primaryKeyId,
  147. '',
  148. false,
  149. {
  150. current: 1,
  151. size: 2000,
  152. projectId: projectId.value,
  153. },
  154. )
  155. resolve(resData?.data)
  156. }
  157. }
  158. //树节点被点击
  159. const nodeInfo = ref({})
  160. const defaultExpandKey = ref([])
  161. const treeRef = ref(null)
  162. const treeProps = {
  163. label: 'title',
  164. isLeaf: (item) => {
  165. return !item.hasChildren
  166. },
  167. }
  168. const curNode = ref({})
  169. const treeNodeTap = (data, node) => {
  170. console.log(node, 'node')
  171. nodeInfo.value = data
  172. curNode.value = node
  173. searchForm.value.parentId = data.id
  174. if (node?.level === 1) {
  175. searchClick()
  176. } else if (node?.level === 2) {
  177. searchForm.value.total = 1
  178. tableData.value = [data]
  179. }
  180. }
  181. //搜索表单
  182. const searchForm = ref({ current: 1, size: 30, total: 0 })
  183. //搜索
  184. const searchClick = () => {
  185. const { parentId } = searchForm.value
  186. if (isNullES(parentId)) {
  187. window?.$message?.warning('请先在左侧点击一个节点')
  188. return
  189. }
  190. searchForm.value.current = 1
  191. getTableData()
  192. }
  193. //分页
  194. const pageChange = ({ current, size }) => {
  195. const { parentId } = searchForm.value
  196. if (isNullES(parentId)) {
  197. window?.$message?.warning('请先在左侧点击一个节点')
  198. return
  199. }
  200. searchForm.value.current = current
  201. searchForm.value.size = size
  202. getTableData()
  203. }
  204. //表格数据
  205. const tableData = ref([])
  206. const tableColumn = ref([
  207. { key: 'title', name: '名称' },
  208. { key: 'elementTotal', name: '总量', width: 80, align: 'center' },
  209. { key: 'tabOwner', name: '所属方', width: 140, align: 'center' },
  210. { key: 'fillRate', name: '填报率', width: 80, align: 'center' },
  211. { key: 'action', name: '操作', width: 220, align: 'center' },
  212. ])
  213. //获取表格数据
  214. const tableLoading = ref(false)
  215. const getTableData = async () => {
  216. tableData.value = []
  217. tableLoading.value = true
  218. const { data } = await privateApi.tabTypeLazyTree({
  219. ...searchForm.value,
  220. total: null,
  221. projectId: projectId.value,
  222. })
  223. tableLoading.value = false
  224. tableData.value = getArrValue(data?.records)
  225. searchForm.value.total = data?.total || 0
  226. }
  227. //表格被选择
  228. const tableCheckKeys = ref([])
  229. const tableCheckChange = (rows) => {
  230. tableCheckKeys.value = rows
  231. }
  232. //关联清表
  233. const isAssociationShow = ref(false)
  234. const associationInfo = ref({})
  235. const linkExcelClick = async (item) => {
  236. associationInfo.value = item
  237. await nextTick()
  238. isAssociationShow.value = true
  239. }
  240. const linkClose = ()=>{
  241. isAssociationShow.value = false
  242. getTableData().then()
  243. refreshTreeData()
  244. }
  245. const refreshTreeData = ()=>{
  246. //刷新左边树形数据
  247. if (curNode.value.level === 1) {
  248. refreshTree()
  249. nextTick(()=>{
  250. defaultExpandKey.value.push(curNode.value.data.id)
  251. treeRef.value.setCurrentKey(curNode.value.data.id)
  252. })
  253. } else {
  254. updateTreeNewNode()
  255. }
  256. }
  257. const updateTreeNewNode = ()=>{
  258. privateApi. tabTypeLazyTree(
  259. { parentId: nodeInfo.value.parentId, projectId: projectId.value, current: 1, size: 1000 },
  260. ).then((res) => {
  261. treeRef.value.updateKeyChildren(
  262. nodeInfo.value.parentId,
  263. res.data.records,
  264. )
  265. nextTick(()=>{
  266. treeRef.value.setCurrentKey(curNode.value.data.id)
  267. let arr = getArrValue(res.data.records)
  268. arr.forEach((ele)=>{
  269. if (ele.id === curNode.value.data.id) {
  270. tableData.value = [ele]
  271. }
  272. })
  273. })
  274. })
  275. }
  276. //公式配置
  277. const elementFormulasObj = ref({})
  278. const elementFormulasShow = ref(false)
  279. const rowFormulaClick = async (row) => {
  280. elementFormulasObj.value = row
  281. elementFormulasShow.value = true
  282. const { data } = await treeApi.getTableElments({
  283. id: row.initTableId,
  284. })
  285. const arr = getArrValue(data)
  286. formulaTableData.value = arr
  287. formulaTableList.value = deepClone(arr)
  288. }
  289. //元素公式列表
  290. const formulaTableColumn = [
  291. { key: 'eName', name: '字段信息' },
  292. { key: 'action', name: '操作', width: 80, align: 'center' },
  293. ]
  294. const formulaTableData = ref([])
  295. const formulaTableList = ref([])
  296. // 搜索元素公式
  297. const formulaInput = ref('')
  298. const searchFormulaClick = () => {
  299. const arr = formulaTableList.value
  300. formulaTableData.value = arr.filter(({ eName }) => {
  301. return eName.indexOf(formulaInput.value) > -1
  302. })
  303. }
  304. //编辑元素公式关闭
  305. const elementFormulasClose = () => {
  306. elementFormulasShow.value = false
  307. formulaTableData.value = []
  308. formulaTableList.value = []
  309. }
  310. //全局公式
  311. const editFormulaData = ref({})
  312. const isEditFormulaShow = ref(false)
  313. const toFormulaGlobal = async (row) => {
  314. elementFormulasClose()
  315. const obj = elementFormulasObj.value
  316. const nodeId = nodeInfo.value.id
  317. editFormulaData.value = {
  318. node: obj,
  319. pid: projectId.value,
  320. //wbsId: wbsId.value,
  321. nodeId: nodeId,
  322. eleId: row.id,
  323. eleType: true,
  324. globalType: 1,
  325. }
  326. await nextTick()
  327. isEditFormulaShow.value = true
  328. }
  329. //公式操作完成
  330. const editFormulaFinish = () => {
  331. }
  332. //分配WBS
  333. const isAllocateShow = ref(false)
  334. const allocateWbsInfo = ref({})
  335. const assignWbs = (row) => {
  336. allocateWbsInfo.value = row
  337. isAllocateShow.value = true
  338. }
  339. //编辑元素
  340. const isEditElementShow = ref(false)
  341. const editElementInfo = ref({})
  342. const editElementData = ref({})
  343. const editElementLoading = ref(false)
  344. const editElement = async (row) => {
  345. editElementLoading.value = true
  346. const { code, data } = await excelApi.getExcelHtml({ pkeyId: row.id })
  347. if (code !== 200 || isNullES(data)) {
  348. editElementLoading.value = false
  349. window?.$message.warning('表单异常,请联系管理员')
  350. return
  351. }
  352. editElementLoading.value = false
  353. editElementInfo.value = row
  354. editElementData.value = {
  355. pid: projectId.value,
  356. wbsid: wbsId.value,
  357. }
  358. await nextTick()
  359. isEditElementShow.value = true
  360. }
  361. //编辑元素里的跳转页面
  362. const editElementToPage = async (name) => {
  363. const row = deepClone(editElementInfo.value)
  364. //表单调整
  365. if (name === 'adjustment') {
  366. adjustExcelInfo.value = deepClone(row)
  367. await nextTick()
  368. isAdjustExcelShow.value = true
  369. }
  370. //公式配置
  371. if (name === 'formula') {
  372. console.log('还没做')
  373. }
  374. }
  375. //调整表单
  376. const isAdjustExcelShow = ref(false)
  377. const adjustExcelLoading = ref(false)
  378. const adjustExcelInfo = ref({})
  379. const adjustExcelClick = async (row) => {
  380. const { primaryKeyId, excelIds } = row
  381. if (isNullES(primaryKeyId) || isNullES(excelIds)) {
  382. window?.$message.warning('表单值异常,请联系管理员')
  383. return
  384. }
  385. adjustExcelLoading.value = true
  386. const { code, data } = await excelApi.getExcelHtml({
  387. pkeyId: primaryKeyId,
  388. })
  389. if (code !== 200 || isNullES(data)) {
  390. adjustExcelLoading.value = false
  391. window?.$message.warning('表单异常,请联系管理员')
  392. return
  393. }
  394. adjustExcelInfo.value = deepClone(row)
  395. adjustExcelLoading.value = false
  396. await nextTick()
  397. isAdjustExcelShow.value = true
  398. }
  399. //关闭抽屉
  400. const drawerClose = () => {
  401. isShow.value = false
  402. emit('close')
  403. }
  404. //删除元素表
  405. const batchDelLoad = ref(false)
  406. const batchDel = async () => {
  407. if (tableCheckKeys.value.length == 0) {
  408. window?.$message?.warning('请先选择一个元素')
  409. return
  410. }
  411. HcDelMsg(async (resolve) => {
  412. //发起请求
  413. const ids = arrToId(tableCheckKeys.value)
  414. const { isRes } = await privateApi.delAprojectTab({
  415. projectId: projectId.value,
  416. primaryKeyIds: ids,
  417. })
  418. resolve() //关闭弹窗
  419. if (!isRes) return
  420. window.$message.success('删除成功')
  421. getTableData().then()
  422. refreshTree()
  423. })
  424. }
  425. //引用元素表单库
  426. const quteEleShow = ref(false)
  427. const quteEleTableClick = () => {
  428. quteEleShow.value = true
  429. }
  430. const quteEleClose = () => {
  431. quteEleShow.value = false
  432. getTableData().then()
  433. refreshTree()
  434. }
  435. </script>