8
0

drawer-temp.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. <template>
  2. <hc-drawer
  3. v-model="isShow"
  4. to-id="hc-main-box"
  5. is-close
  6. @close="drawerClose"
  7. >
  8. <hc-body split :options="splitOptions">
  9. <template #left>
  10. <hc-tab-card :tabs="tabsData1" :tab-key="tabsKey1">
  11. <hc-card scrollbar>
  12. <template #header>
  13. <hc-search-input
  14. v-model="searchTreeName"
  15. placeholder="请输入关键字"
  16. @search="searchTreeNameClick"
  17. />
  18. </template>
  19. <template v-if="isTreeMode === 1">
  20. <hc-lazy-tree
  21. v-if="isShow"
  22. ref="treeRef1"
  23. is-load-menu
  24. :h-props="treeProps"
  25. tree-key="id"
  26. @load-menu="treeMenu"
  27. @load="treeLoadNode"
  28. @node-tap="treeNodeTap"
  29. @menu-tap="treeMenuClick"
  30. >
  31. <template #name="{ data }">
  32. <span class="text-16px font-400">{{
  33. data.title
  34. }}</span>
  35. </template>
  36. </hc-lazy-tree>
  37. </template>
  38. <template v-if="isTreeMode === 2">
  39. <hc-data-tree
  40. ref="treeRef2"
  41. is-load-menu
  42. :h-props="treeProps"
  43. tree-key="id"
  44. :datas="treeData"
  45. @load-menu="treeMenu"
  46. @node-tap="treeNodeTap"
  47. @menu-tap="treeMenuClick"
  48. >
  49. <template #name="{ data }">
  50. <span class="text-16px font-400">{{
  51. data.title
  52. }}</span>
  53. </template>
  54. </hc-data-tree>
  55. </template>
  56. </hc-card>
  57. </hc-tab-card>
  58. </template>
  59. <hc-tab-card
  60. :tabs="tabsData"
  61. :tab-key="tabsKey"
  62. @change="tabsChange"
  63. >
  64. <template v-if="tabsKey === 'second'">
  65. <div class="hc-flex mb-10px">
  66. <el-button type="primary" plain @click="setClick(1)">
  67. 设置为最高并卷层级
  68. </el-button>
  69. <el-button type="primary" plain @click="setClick(2)">
  70. 设置为分类卷并卷
  71. </el-button>
  72. <el-button type="primary" plain @click="setClick(3)">
  73. 设置为独立组卷
  74. </el-button>
  75. </div>
  76. <div class="hc-tab-scroll-class-second">
  77. <el-scrollbar v-loading="secondTreeLoad">
  78. <hc-data-tree
  79. ref="secondTree"
  80. is-load-menu
  81. :h-props="secondTreeProps"
  82. tree-key="id"
  83. :datas="secondTreeData"
  84. show-checkbox
  85. check-strictly
  86. @load-menu="secondTreeMenu"
  87. @menu-tap="secondTreeMenuClick"
  88. >
  89. <template #name="{ data }">
  90. <!-- <div class="config_type">最</div> -->
  91. <template
  92. v-if="
  93. Number(
  94. data.archiveAutoGroupSelect,
  95. ) === 1
  96. "
  97. >
  98. <div
  99. v-if="
  100. Number(data.archiveAutoType)
  101. === 1
  102. "
  103. class="config_type"
  104. >
  105. </div>
  106. <div
  107. v-if="
  108. Number(data.archiveAutoType)
  109. === 2
  110. "
  111. class="config_type"
  112. >
  113. </div>
  114. <div
  115. v-if="
  116. Number(data.archiveAutoType)
  117. === 3
  118. "
  119. class="config_type"
  120. >
  121. </div>
  122. </template>
  123. <span class="text-16px font-400">{{
  124. data.title
  125. }}</span>
  126. </template>
  127. </hc-data-tree>
  128. </el-scrollbar>
  129. </div>
  130. </template>
  131. <template v-if="tabsKey === 'first'">
  132. <entryConfig
  133. :right-data="secondTreeData"
  134. :project-id="dataInfo.id"
  135. :tab-key="tabsKey"
  136. />
  137. </template>
  138. </hc-tab-card>
  139. </hc-body>
  140. <!-- 树节点新增 -->
  141. <HcTreeNodeForm
  142. v-model="isTreeFormShow"
  143. :info="dataInfo"
  144. :data="treeFormData"
  145. :node="treeFormNode"
  146. :type="treeFormType"
  147. @finish="pseudoRefresh"
  148. />
  149. <!-- 树节点排序 -->
  150. <HcTreeNodeSort
  151. v-model="isTreeSortShow"
  152. :data="treeSortData"
  153. @finish="pseudoRefresh"
  154. />
  155. <!-- 查看配置 -->
  156. <hc-dialog v-model="configVisible" title="查看配置">
  157. <div v-if="configInfo && configInfo.type">
  158. <div class="mb-2" style="font-size: 18px">
  159. <span>配置类别:</span>
  160. <span v-if="configInfo.type == 1">最高并卷层级</span>
  161. <span v-if="configInfo.type == 2">分类并卷</span>
  162. <span v-if="configInfo.type == 3">独立组卷</span>
  163. </div>
  164. <template v-if="configInfo.type !== 2">
  165. <div class="config-allname">
  166. {{ configInfo.data.allName }}
  167. </div>
  168. </template>
  169. <template v-else>
  170. <hc-data-tree
  171. ref="configtree"
  172. :datas="configInfo.data"
  173. tree-key="id"
  174. :h-props="configTreeProps"
  175. show-checkbox
  176. :check-strictly="true"
  177. :default-checked-keys="checkedKeys"
  178. :auto-expand-keys="checkedKeys"
  179. @check="configCheckChange"
  180. />
  181. </template>
  182. </div>
  183. <div v-else style="text-align: center; font-size: 22px">
  184. 该节点尚未配置
  185. </div>
  186. <template #footer>
  187. <el-button @click="configVisible = false">取消查看</el-button>
  188. <el-button
  189. v-if="configInfo && configInfo.type == 2"
  190. type="primary"
  191. @click="changeConfig"
  192. >
  193. 确认修改配置
  194. </el-button>
  195. </template>
  196. </hc-dialog>
  197. <!-- 设置最高层级 -->
  198. <hc-dialog
  199. v-model="highVisible"
  200. :title="titleName"
  201. @save="setRecordsHandle"
  202. @close="highVisible = false"
  203. >
  204. <el-checkbox-group v-model="checkList" class="wraplabel">
  205. <div v-for="node in nodesList" :key="node.id">
  206. <el-checkbox
  207. :label="node.pathName"
  208. :value="node.id"
  209. style="margin-bottom: 10px"
  210. />
  211. </div>
  212. </el-checkbox-group>
  213. </hc-dialog>
  214. </hc-drawer>
  215. </template>
  216. <script setup>
  217. import { ref, watch } from 'vue'
  218. import { HcDelMsg, HcFirmMsg } from 'hc-vue3-ui'
  219. import { getArrValue, isNullES } from 'js-fast-way'
  220. import HcTreeNodeForm from './tree-form.vue'
  221. import HcTreeNodeSort from './tree-sort.vue'
  222. import mainApi from '~api/project/tree'
  223. import archiveApi from '~api/desk/archive-tree'
  224. import entryConfig from '../../desk/wbs/entry-config.vue'
  225. const props = defineProps({
  226. data: {
  227. type: Object,
  228. default: () => ({}),
  229. },
  230. })
  231. //事件
  232. const emit = defineEmits(['close'])
  233. //双向绑定
  234. const isShow = defineModel('modelValue', {
  235. default: false,
  236. })
  237. //监听数据
  238. const dataInfo = ref(props.data)
  239. watch(
  240. () => props.data,
  241. (data) => {
  242. dataInfo.value = data
  243. },
  244. { immediate: true, deep: true },
  245. )
  246. //监听显示
  247. watch(isShow, (val) => {
  248. if (val) getDataApi()
  249. })
  250. //处理相关数据
  251. const getDataApi = () => {
  252. getTreeAllData()
  253. getSecondTreeData()
  254. }
  255. //树搜索
  256. const isTreeMode = ref(1) //1懒加载,2全加载
  257. const searchTreeName = ref('')
  258. const searchTreeNameClick = async () => {
  259. if (isNullES(searchTreeName.value)) {
  260. isTreeMode.value = 1
  261. } else {
  262. isTreeMode.value = 2
  263. await getTreeAllData()
  264. treeRef2.value?.treeRef?.filter(searchTreeName.value)
  265. }
  266. }
  267. //树配置
  268. const treeRef1 = ref(null)
  269. const treeRef2 = ref(null)
  270. const treeProps = {
  271. label: 'title',
  272. children: 'children',
  273. isLeaf: 'hasChildren',
  274. }
  275. //懒加载树的菜单
  276. const treeMenu = ({ level }, resolve) => {
  277. let newMenu = []
  278. if (level !== 1) {
  279. newMenu.push({ icon: 'add-circle', label: '新增', key: 'add' })
  280. newMenu.push({ icon: 'draft', label: '编辑', key: 'edit' })
  281. newMenu.push({ icon: 'arrow-up-down', label: '排序', key: 'rank' })
  282. newMenu.push({ icon: 'delete-bin', label: '删除', key: 'del' })
  283. }
  284. newMenu.push({ icon: 'refresh', label: '同步', key: 'sync' })
  285. resolve(newMenu)
  286. }
  287. //菜单被点击
  288. const treeMenuClick = ({ key, data, node }) => {
  289. if (key === 'add') {
  290. if (Number(data.isStorageNode) !== 1) {
  291. treeFormType.value = '新增'
  292. treeFormData.value = data
  293. treeFormNode.value = node
  294. isTreeFormShow.value = true
  295. } else {
  296. window.$message.warning('该节点下不允许新增节点')
  297. }
  298. } else if (key === 'edit') {
  299. treeFormType.value = '编辑'
  300. treeFormData.value = data
  301. treeFormNode.value = node
  302. isTreeFormShow.value = true
  303. } else if (key === 'rank') {
  304. treeSortData.value = data
  305. isTreeSortShow.value = true
  306. } else if (key === 'del') {
  307. delTreeNode(data.id)
  308. } else if (key === 'sync') {
  309. syncTreeNode(data.id)
  310. }
  311. }
  312. //树节点新增/编辑
  313. const isTreeFormShow = ref(false)
  314. const treeFormData = ref({})
  315. const treeFormNode = ref({})
  316. const treeFormType = ref('新增')
  317. //树节点排序
  318. const isTreeSortShow = ref(false)
  319. const treeSortData = ref({})
  320. //删除树节点
  321. const delTreeNode = (id) => {
  322. HcDelMsg(async (resolve) => {
  323. const { isRes } = await mainApi.removeTree(id)
  324. if (!isRes) return
  325. window.$message.success('删除成功')
  326. resolve() //关闭弹窗的回调
  327. pseudoRefresh()
  328. })
  329. }
  330. //同步树节点
  331. const syncTreeNode = (id) => {
  332. HcFirmMsg({ text: '是否同步该节点?' }, async (resolve) => {
  333. const { isRes } = await mainApi.syncProjectTree({ id })
  334. resolve() //关闭弹窗的回调
  335. if (!isRes) return
  336. window.$message.success('操作成功')
  337. pseudoRefresh()
  338. })
  339. }
  340. //伪刷新
  341. const pseudoRefresh = () => {
  342. const val = isTreeMode.value
  343. isTreeMode.value = 4
  344. treeFormData.value = {}
  345. treeFormNode.value = {}
  346. treeFormType.value = ''
  347. treeSortData.value = {}
  348. setTimeout(() => {
  349. isTreeMode.value = val
  350. }, 300)
  351. }
  352. //全加载树
  353. const treeData = ref([])
  354. const getTreeAllData = async () => {
  355. const { data } = await mainApi.allTree({
  356. projectId: dataInfo.value.id,
  357. })
  358. treeData.value = getArrValue(data)
  359. }
  360. //立卷规则树
  361. const getSecondTreeData = async () => {
  362. secondTreeLoad.value = true
  363. const { data } = await mainApi.allTree({
  364. projectId: dataInfo.value.id,
  365. })
  366. secondTreeLoad.value = false
  367. secondTreeData.value = getArrValue(data)
  368. }
  369. //懒加载树
  370. const treeLoadNode = async ({ item, level }, resolve) => {
  371. const parentId = level === 0 ? 0 : item.id
  372. if (level === 0) {
  373. const arr = await lazyNodeTree(parentId)
  374. if (arr.length) {
  375. resolve(arr)
  376. } else {
  377. resolve(await archiveTreeInit())
  378. }
  379. } else {
  380. resolve(await lazyNodeTree(parentId))
  381. }
  382. }
  383. //懒加载树接口
  384. const lazyNodeTree = async (id) => {
  385. const { data } = await mainApi.lazyTree({
  386. parentId: id,
  387. projectId: dataInfo.value.id,
  388. })
  389. const res = getArrValue(data)
  390. for (let i = 0; i < res.length; i++) {
  391. res[i].hasChildren = !res[i].hasChildren
  392. }
  393. return res
  394. }
  395. //初始化根节点
  396. const archiveTreeInit = async () => {
  397. const { data } = await mainApi.treeInit({
  398. projectId: dataInfo.value.id,
  399. })
  400. const res = getArrValue(data)
  401. for (let i = 0; i < res.length; i++) {
  402. res[i].hasChildren = !res[i].hasChildren
  403. }
  404. return res
  405. }
  406. //树节点被点击
  407. const nodeInfo = ref({})
  408. const treeNodeTap = ({ data }) => {
  409. nodeInfo.value = data
  410. }
  411. //页面分割
  412. const splitOptions = { sizes: [50, 50], snapOffset: 0, minSize: [300, 300] }
  413. //左边选项卡
  414. const tabsKey1 = ref('1')
  415. const tabsData1 = ref([
  416. { key: '1', name: '竣工档案目录' },
  417. { key: '2', name: '文书档案目录' },
  418. ])
  419. //右边选项卡
  420. const tabsKey = ref('second')
  421. const tabsData = ref([
  422. { key: 'second', name: '立卷规则' },
  423. { key: 'first', name: '工程文件入口配置' },
  424. ])
  425. const tabsChange = ({ key }) => {
  426. tabsKey.value = key
  427. getSecondTreeData()
  428. }
  429. //立卷规则树
  430. const secondTreeLoad = ref(false)
  431. const secondTree = ref(null)
  432. const secondTreeData = ref([])
  433. const secondTreeProps = {
  434. label: 'title',
  435. }
  436. //立卷规则树右键菜单
  437. const secondTreeMenu = (_, resolve) => {
  438. resolve([
  439. { icon: 'eye', label: '查看配置', key: 'rank' },
  440. { icon: 'delete-bin', label: '删除', key: 'del' },
  441. ])
  442. }
  443. const secondTreeMenuClick = async ({ key, data, node }) => {
  444. if (key === 'rank') {
  445. configInfo.value = null
  446. const {
  447. data: res,
  448. code,
  449. msg,
  450. } = await archiveApi.getArchiveAutoRule({
  451. nodeId: data.id, //归档树节点的id 或者 挂载wbs节点的ID(具体哪个ID待定)
  452. iswbsNode: data.iswbsNode, //是否是wbs节点
  453. projectId: dataInfo.value.id, // 系统级为0 项目级为项目id
  454. wbsNode2ArchiveTreeNodeId: data.wbsNode2ArchiveTreeNodeId, // 这个树节点里面有(iswbsNode为true需传)
  455. })
  456. console.log(res, 'res')
  457. if (code == 200 && msg == '操作成功') {
  458. if (res.type) {
  459. configInfo.value = res
  460. } else if (res.archiveAutoType) {
  461. configInfo.value = {
  462. type: 2,
  463. data: res.tree,
  464. }
  465. checkedKeys.value = res?.selectNodeIds.split(',')
  466. } else {
  467. configInfo.value = {}
  468. }
  469. console.log(configInfo.value, 'configInfo.value ')
  470. configVisible.value = true
  471. }
  472. } else if (key === 'del') {
  473. HcDelMsg(async (resolve) => {
  474. const { code } = await archiveApi.removeArchiveAutoRule({
  475. nodeId: data.id,
  476. iswbsNode: data.iswbsNode, //是否是wbs节点 flase 不是 true 是 (先false,具体怎么区分后面再看)
  477. projectId: dataInfo.value.id, // 系统级为0 项目级为项目id
  478. })
  479. resolve() //关闭弹窗的回调
  480. if (code !== 200) return
  481. window.$message.success('删除成功')
  482. getSecondTreeData()
  483. }).then()
  484. }
  485. }
  486. //设置为最高并卷层级
  487. //获取树节点名字路径
  488. const getTreeNodePath = (node, refName) => {
  489. let nodeInfo = secondTree.value.treeRef.getNode(node.id)
  490. let pathArr = []
  491. while (nodeInfo.parent) {
  492. pathArr.push(nodeInfo.data.title)
  493. nodeInfo = nodeInfo.parent
  494. }
  495. return pathArr.reverse().join('/')
  496. }
  497. const setRecordsHandle = async () => {
  498. const { code, msg } = await archiveApi.saveArchiveAutoRule({
  499. archiveAutoType: archiveAutoType.value, //最高1 分类2 独立3
  500. selectNodeIds: checkList.value.join(','), //鼠标选择的节点ID(只要鼠标选择的节点,选择节点的下级子节点那种不要),逗号拼接
  501. iswbsNode: nodesList.value[0]?.iswbsNode, //是否是wbs节点
  502. projectId: dataInfo.value.id, // 系统级为0 项目级为项目id
  503. })
  504. if (code == 200 && msg == '操作成功') {
  505. window.$message({
  506. type: 'success',
  507. message: '设置成功',
  508. })
  509. highVisible.value = false
  510. getSecondTreeData()
  511. }
  512. }
  513. const setClick = (type) => {
  514. const treeRef = secondTree.value?.getRef() //树的原始ref
  515. const nodes = treeRef.getCheckedNodes() //获取选中的节点
  516. console.log(nodes, 'nodes')
  517. if (nodes.length < 1) {
  518. window.$message({
  519. message: '请先勾选节点,再进行设置',
  520. type: 'warning',
  521. })
  522. return false
  523. }
  524. let ids = []
  525. nodes.forEach((element) => {
  526. element.pathName = getTreeNodePath(element, 'secondTree')
  527. ids.push(element.id)
  528. })
  529. console.log(nodes, 'nodes')
  530. checkList.value = ids
  531. nodesList.value = nodes
  532. setName(type)
  533. highVisible.value = true
  534. }
  535. const titleName = ref('')
  536. const archiveAutoType = ref(1)
  537. const highVisible = ref(false)
  538. const checkList = ref([])
  539. const nodesList = ref([])
  540. const setName = (type) => {
  541. switch (type) {
  542. case 1:
  543. archiveAutoType.value = 1
  544. titleName.value = '设置最高并卷层级'
  545. break
  546. case 2:
  547. archiveAutoType.value = 2
  548. titleName.value = '设置分类并卷层级'
  549. break
  550. case 3:
  551. archiveAutoType.value = 3
  552. titleName.value = '设置单独并卷层级'
  553. break
  554. }
  555. }
  556. //查看配置
  557. const configtree = ref(null)
  558. const configVisible = ref(false)
  559. const configInfo = ref({})
  560. const checkedKeys = ref([])
  561. const configTreeProps = {
  562. label: 'nodeName',
  563. children: 'children',
  564. isLeaf: 'hasChildren',
  565. }
  566. const configCheckChange = (_, { checkedNodes }) => {
  567. let array = checkedNodes
  568. for (let index = 1; index < array.length; index++) {
  569. const element = array[index]
  570. if (element.iswbsNode !== array[0].iswbsNode) {
  571. //取消勾选
  572. configtree.value.treeRef.setChecked(data.id, false, false)
  573. window.$message({
  574. message: '非wbs节点不能和wbs节点一起设置规则',
  575. type: 'warning',
  576. })
  577. return
  578. }
  579. }
  580. }
  581. const changeConfig = async () => {
  582. let keys = configtree.value.treeRef.getCheckedKeys()
  583. let nodes = configtree.value.treeRef.getCheckedNodes()
  584. console.log(nodes)
  585. const { code, msg } = await archiveApi.updateArchiveAutoRules({
  586. archiveAutoType: 2, //最高1 分类2 独立3
  587. archiveAutoGroupId: configInfo.value.data.archiveAutoGroupId,
  588. selectNodeIds: keys.join(','), //鼠标选择的节点ID(只要鼠标选择的节点,选择节点的下级子节点那种不要),逗号拼接
  589. iswbsNode: nodes[0]?.iswbsNode, //是否是wbs节点
  590. projectId: dataInfo.value.id, // 系统级为0 项目级为项目id
  591. })
  592. if (code == 200) {
  593. window.$message({
  594. type: 'success',
  595. message: msg,
  596. })
  597. configVisible.value = false
  598. }
  599. }
  600. //关闭抽屉
  601. const drawerClose = () => {
  602. isShow.value = false
  603. emit('close')
  604. }
  605. </script>
  606. <style scoped lang="scss">
  607. .hc-tab-scroll-class-second {
  608. position: relative;
  609. height: calc(100% - 42px);
  610. }
  611. .hc-tab-scroll-class-second :deep(.hc-tree-node .data-custom-tree-node .label) {
  612. display: flex;
  613. }
  614. .hc-tab-scroll-class-second :deep(.config_type) {
  615. position: relative;
  616. height: 18px;
  617. width: 18px;
  618. font-size: 14px;
  619. display: flex;
  620. align-items: center;
  621. justify-content: center;
  622. background: #fb5c5c;
  623. color: white;
  624. border-radius: 3px;
  625. margin-right: 5px;
  626. }
  627. </style>