当前位置:   article > 正文

el-menu el-collapse 左右滚动联动,左右展开项联动_el-menu collapse

el-menu collapse

需求:

  1. 左侧menu每次只保持一个子菜单的展开,右侧每次只能展开一个面板(手风琴效果)
  2. 滚动左侧的 menu 菜单,右侧的折叠面板(collapse)会跟随滚动到对应区域
  3. 滚动右侧的折叠面板,左侧的menu 菜单会跟随滚动到对应区域
  4. 展开左侧的menu 菜单,右侧的折叠面板对应的面板展开,滚动到顶部
  5. 点击左侧的 menu 子项item,右侧折叠面板中的二级菜单滚动到顶部
  6. 点击展开右侧的折叠面板,左侧对应的菜单项展开

遇到的问题:

  1. 左侧menu 的数据获取是一个接口,右侧菜单的子项是根据左侧的一级菜单id获取,每次展开左侧右侧需要去异步获取数据填充到面板中,展开的时候数据没有填充完滚动条还未撑起来滚动位置会出错
  2. 左右侧滚动联动的时候,左侧滚动去滚动右侧,右侧滚动去设置左侧滚动,当左右两侧都设置了对方跟随自己滚动的时候,滚动时会出现卡顿,他们会互相循环调用

解决问题:

  1. 动态计算前面有多少个兄弟节点,每个节点是固定高度,设置计算后的值
  2. 设置一个拦截标识,左侧滚动的时候禁止右侧去触发左侧的滚动;右侧滚动的时候禁止左侧去触发右侧的滚动

效果:

代码实现:

menu 组件:

  1. <template>
  2. <div class="menuList">
  3. <div class="search-box">
  4. <el-autocomplete
  5. class="search-input"
  6. v-model="keyword"
  7. size="small"
  8. :clearable="true"
  9. placeholder="请输入关键字"
  10. :trigger-on-focus="false"
  11. :fetch-suggestions="querySeachSync"
  12. @select="handleSelect" />
  13. <div @click="seachData" class="bth-search el-icon-search"></div>
  14. </div>
  15. <el-menu
  16. id="label-mid-el-menu"
  17. :default-active="curMenuId[1]"
  18. @select="handleSelectMenu"
  19. @open="handleOpenMenu"
  20. :unique-opened="true">
  21. <nav-item v-for="nav in navList"
  22. :key="nav.classificationId"
  23. :ref="'menu_' + nav.classificationId"
  24. :nav="nav"
  25. @clickNav="hanleClickNavItem" />
  26. </el-menu>
  27. </div>
  28. </template>
  29. <script>
  30. import NavItem from './NavItem.vue'
  31. import { keywordLabels } from '@/api/modules/labelMiddleware.js'
  32. export default {
  33. props: {
  34. navList: {
  35. type: Array,
  36. default: () => []
  37. },
  38. curMenuId: {
  39. type: Array
  40. }
  41. },
  42. watch: {
  43. curMenuId(arr) {
  44. // 折叠面板展开时展开对应的menu
  45. this.scrollToTarget(arr[0])
  46. }
  47. },
  48. components: {
  49. NavItem
  50. },
  51. data() {
  52. return {
  53. keyword: '',
  54. isScroll: false,
  55. // 定时器
  56. scrollTimer: null
  57. }
  58. },
  59. methods: {
  60. // 展开右侧折叠面板的时候当前menu滚动到顶部
  61. scrollToTarget(classificationId) {
  62. // 设置当前为左侧滚动标识,不触发右侧滚动
  63. window.rightScroll = true
  64. window.clearTimeout(this.scrollTimer)
  65. this.scrollTimer = setTimeout(_ => {
  66. // 放开标识
  67. window.rightScroll = false
  68. }, 800)
  69. // 找到目标元素
  70. const id = classificationId
  71. let el = this.$refs['menu_' + id][0]
  72. el = el.$el ? el.$el : el
  73. // 通过计算前面有多少元素来手动计算scrollTop高度
  74. const scrollBox = document.querySelector('#label-mid-el-menu')
  75. const children = Array.from(el.parentNode.children)
  76. const prevSiblings = []
  77. for (const child of children) {
  78. if (child === el) {
  79. break
  80. }
  81. prevSiblings.push(child)
  82. }
  83. const top = prevSiblings.length * 44
  84. this.$nextTick(_ => {
  85. scrollBox.scrollTo({
  86. top: top,
  87. behavior: 'smooth'
  88. })
  89. })
  90. },
  91. // 模糊搜索
  92. async seachData() {
  93. const { data } = await keywordLabels({
  94. keyword: this.keyword
  95. })
  96. return data
  97. },
  98. // 搜索出来选择
  99. handleSelect(label) {
  100. this.$emit('setCurIdArea', label.firstClassificationId)
  101. },
  102. // 键入关键字搜索
  103. async querySeachSync(keyworld, callback) {
  104. const data = await this.seachData()
  105. const res = data.map(item => {
  106. return {
  107. ...item,
  108. value: item.labelName
  109. }
  110. })
  111. callback(res)
  112. },
  113. // 设置菜单默认选中项为第一个
  114. setDefaultActive(list) {
  115. while (true) {
  116. if (Array.isArray(list)) {
  117. list = list[0]
  118. continue
  119. }
  120. if (list.childList && list.childList.length > 0) {
  121. list = list.childList
  122. continue
  123. }
  124. break
  125. }
  126. this.defaultActive = list.classificationId
  127. },
  128. // 点击item
  129. hanleClickNavItem(key) {
  130. },
  131. // 展开某个菜单列表
  132. handleOpenMenu(key, keyPath) {
  133. // console.log(key, keyPath, 'openMenu');
  134. this.$emit('setCurIdArea', keyPath, 'menu')
  135. },
  136. // 选择菜单列表菜单
  137. handleSelectMenu(key, keyPath) {
  138. // console.log(typeof key, keyPath, 'itemMenu')
  139. if (keyPath.length === 1) {
  140. // 菜单没有子项,触发menu
  141. this.$emit('setCurIdArea', keyPath, 'menu')
  142. } else {
  143. this.$emit('setCurIdArea', keyPath, 'item')
  144. }
  145. },
  146. // 右侧滚动的时候左侧滚动到目标位置
  147. menuScroll() {
  148. // 右侧如果正在滚动不触发左侧滚动
  149. if (window.rightScroll === true) {
  150. return
  151. }
  152. // 设置左侧正在滚动的标识,此时不触发右侧滚动
  153. window.leftScroll = true
  154. window.clearTimeout(this.scrollTimer)
  155. this.scrollTimer = setTimeout(_ => {
  156. // 左侧滚动结束,放开标识,此时才能触发右侧的滚动
  157. window.leftScroll = false
  158. }, 600)
  159. // 左侧滚动的menu
  160. const menuEl = document.querySelector('#label-mid-el-menu')
  161. // menu的滚动高度
  162. const scrollMenu = menuEl.scrollHeight - menuEl.offsetHeight
  163. // 右侧的折叠面板
  164. const labelContent = document.querySelector('.label-middleware-list-wrapper .content')
  165. // 折叠面板的滚动高度
  166. const labelScroll = labelContent.scrollHeight - labelContent.offsetHeight
  167. // 计算滚动比率
  168. const ratio = scrollMenu / labelScroll
  169. labelContent.scrollTo({
  170. top: menuEl.scrollTop / ratio
  171. })
  172. }
  173. },
  174. mounted() {
  175. this.setDefaultActive(this.navList)
  176. const menuEl = document.querySelector('#label-mid-el-menu')
  177. menuEl.addEventListener('scroll', this.menuScroll)
  178. },
  179. beforeDestroy() {
  180. const menuEl = document.querySelector('#label-mid-el-menu')
  181. menuEl.removeEventListener('scroll', this.menuScroll)
  182. }
  183. }
  184. </script>
  185. <style lang="scss" scoped>
  186. .menuList {
  187. height: 100%;
  188. display: flex;
  189. flex-direction: column;
  190. padding-top: 16px;
  191. border-right: solid 1px #e6e6e6;
  192. .search-box {
  193. display: flex;
  194. align-items: center;
  195. width: 208px;
  196. height: 32px;
  197. background: #FFFFFF;
  198. border-radius: 2px;
  199. margin-left: 16px;
  200. margin-bottom: 16px;
  201. .search-input {
  202. flex: 1;
  203. margin-right: -2px;
  204. /deep/ .el-input__inner {
  205. height: 32px;
  206. line-height: 32px;
  207. border-right: none;
  208. }
  209. }
  210. .bth-search {
  211. flex: 0 0 auto;
  212. width: 32px;
  213. height: 32px;
  214. background: #E02D39;
  215. color: #fff;
  216. display: flex;
  217. align-items: center;
  218. justify-content: center;
  219. position: relative;
  220. z-index: 2;
  221. cursor: pointer;
  222. }
  223. }
  224. .el-menu {
  225. flex: 1;
  226. overflow: auto;
  227. border-right: none;
  228. /deep/ {
  229. .is-active:not(.el-submenu) {
  230. color: #e02d39;
  231. position: relative;
  232. background: #F9F9FA;
  233. font-weight: bold;
  234. &::after {
  235. content: "";
  236. position: absolute;
  237. left: 0;
  238. top: 0;
  239. width: 2px;
  240. height: 100%;
  241. background: #e02d39;
  242. }
  243. }
  244. }
  245. }
  246. }
  247. </style>

menu 里面的 item 组件:

  1. <template>
  2. <el-submenu
  3. :key="nav.classificationId"
  4. v-if="nav.childList && nav.childList.length>0"
  5. :index="String(nav.classificationId)">
  6. <template slot="title">
  7. <span>{{nav.classificationName}}</span>
  8. </template>
  9. <nav-item
  10. v-for="item in nav.childList"
  11. :key="item.classificationId"
  12. :nav="item"
  13. v-on="$listeners" />
  14. </el-submenu>
  15. <el-menu-item
  16. v-else
  17. :key="nav.classificationId"
  18. :index="String(nav.classificationId)"
  19. @click="$emit('clickNav', nav.classificationId)">
  20. <span slot="title">{{nav.classificationName}}</span>
  21. </el-menu-item>
  22. </template>
  23. <script>
  24. export default {
  25. name: 'NavItem',
  26. props: {
  27. nav: {
  28. type: Object,
  29. default: () => {}
  30. }
  31. }
  32. }
  33. </script>
  34. <style lang="scss" scoped>
  35. .el-submenu {
  36. /deep/ {
  37. .el-menu-item,
  38. .el-submenu__title {
  39. height: 44px;
  40. line-height: 44px;
  41. }
  42. // .el-submenu__title {
  43. // padding-right: 32px !important;
  44. // }
  45. // .el-submenu .el-menu-item {
  46. // padding: 0 32px;
  47. // }
  48. .is-active:not(.el-submenu) {
  49. color: #e02d39;
  50. position: relative;
  51. background: #F9F9FA;
  52. font-weight: bold;
  53. &::after {
  54. content: "";
  55. position: absolute;
  56. left: 0;
  57. top: 0;
  58. width: 2px;
  59. height: 100%;
  60. background: #e02d39;
  61. }
  62. }
  63. }
  64. }
  65. /*菜单关闭*/
  66. .el-submenu /deep/ .el-submenu__title .el-submenu__icon-arrow{
  67. -webkit-transform: rotateZ(-90deg);
  68. -ms-transform: rotate(-90deg);
  69. transform: rotateZ(-90deg);
  70. }
  71. /*菜单展开*/
  72. .el-submenu.is-opened /deep/ .el-submenu__title .el-submenu__icon-arrow{
  73. -webkit-transform: rotateZ(0deg);
  74. -ms-transform: rotate(0deg);
  75. transform: rotateZ(0deg);
  76. }
  77. </style>

右侧的 collapse LabelList 组件:

  1. <template>
  2. <div class="label-middleware-list-wrapper"
  3. v-loading="loading"
  4. element-loading-text="加载中请稍后..."
  5. element-loading-background="rgba(255, 255, 255, .8)">
  6. <div class="content">
  7. <el-collapse
  8. accordion
  9. v-model="activeItem"
  10. @change="handleChangeCollapse">
  11. <el-collapse-item
  12. v-for="nav in labelList"
  13. :ref="'collapse_'+nav.classificationId"
  14. :key="nav.classificationId"
  15. :title="nav.classificationName"
  16. :name="nav.classificationId"
  17. >
  18. <div class="sub-box"
  19. v-for="subs in nav.children"
  20. :key="subs.classificationId"
  21. :ref="'collapse_sub_'+subs.classificationId">
  22. <div class="sub-title">{{subs.classificationName}}</div>
  23. <popover-label
  24. :labelList="subs.labList"
  25. :selectedLabels="selectedLabels"
  26. :popoverVisible="popoverVisible"
  27. @addLabels="addLabels"
  28. @closePopover="closePopover"
  29. @showPopover="showPopover" />
  30. </div>
  31. </el-collapse-item>
  32. </el-collapse>
  33. </div>
  34. </div>
  35. </template>
  36. <script>
  37. import { queryLabels } from '@/api/modules/labelMiddleware.js'
  38. import PopoverLabel from './PopoverLabel.vue'
  39. export default {
  40. components: {
  41. PopoverLabel
  42. },
  43. props: {
  44. navList: {
  45. type: Array,
  46. default: () => []
  47. },
  48. selectedLabels: {
  49. type: Array,
  50. default: () => []
  51. },
  52. curIdArea: {
  53. type: Array,
  54. defaut: () => []
  55. },
  56. curAreaType: {
  57. type: String
  58. }
  59. },
  60. data() {
  61. return {
  62. loading: false,
  63. activeItem: 'org',
  64. labelList: [],
  65. childLabels: [],
  66. popoverVisible: {},
  67. // 定时器
  68. scrollTimer: null
  69. }
  70. },
  71. watch: {
  72. // 展开当前区域
  73. async curIdArea(keyPath) {
  74. const classificationId = keyPath[0]
  75. this.activeItem = isNaN(classificationId) ? classificationId : Number(classificationId)
  76. // 如果左侧是展开的菜单
  77. if (this.curAreaType === 'menu') {
  78. await this.openCollapse(classificationId)
  79. this.scrollToTarget()
  80. }
  81. // 如果左侧是点击的item
  82. if (this.curAreaType === 'item') {
  83. const curItemId = keyPath[keyPath.length - 1]
  84. this.scrollToItem(curItemId)
  85. }
  86. }
  87. },
  88. methods: {
  89. // 滚动到目标位置, 点击navItem时
  90. scrollToItem(navId) {
  91. // 设置当前为左侧滚动标识,不触发右侧滚动
  92. window.leftScroll = true
  93. window.clearTimeout(this.scrollTimer)
  94. this.scrollTimer = setTimeout(_ => {
  95. // 放开标识
  96. window.leftScroll = false
  97. }, 800)
  98. let el = this.$refs['collapse_sub_' + navId][0]
  99. el = el.$el ? el.$el : el
  100. const scrollBox = document.querySelector('.label-middleware-list-wrapper .content')
  101. this.$nextTick(_ => {
  102. scrollBox.scrollTo({
  103. top: el.offsetTop - 8,
  104. behavior: 'smooth'
  105. })
  106. })
  107. },
  108. // 滚动到目标位置, 展开menu时
  109. scrollToTarget(classificationId) {
  110. // 设置当前为右侧滚动标识,不触发左侧滚动
  111. window.leftScroll = true
  112. window.clearTimeout(this.scrollTimer)
  113. this.scrollTimer = setTimeout(_ => {
  114. // 放开标识
  115. window.leftScroll = false
  116. }, 800)
  117. // 找到目标元素
  118. const id = classificationId ?? this.curIdArea
  119. let el = this.$refs['collapse_' + id][0]
  120. el = el.$el ? el.$el : el
  121. while (el) {
  122. if (!el.classList.contains('el-collapse-item')) {
  123. el = el.parentNode
  124. } else {
  125. break
  126. }
  127. }
  128. // 通过计算前面有多少元素来手动计算scrollTop高度
  129. const scrollBox = document.querySelector('.label-middleware-list-wrapper .content')
  130. const children = Array.from(el.parentNode.children)
  131. const prevSiblings = []
  132. for (const child of children) {
  133. if (child === el) {
  134. break
  135. }
  136. prevSiblings.push(child)
  137. }
  138. const top = prevSiblings.length * 48
  139. this.$nextTick(_ => {
  140. scrollBox.scrollTo({
  141. top: top,
  142. behavior: 'smooth'
  143. })
  144. })
  145. },
  146. // 添加标签addLabels
  147. addLabels(label) {
  148. this.$emit('addLabels', label)
  149. },
  150. // 关闭popover
  151. closePopover() {
  152. for (let item in this.popoverVisible) {
  153. this.popoverVisible[item] = false
  154. }
  155. },
  156. // 展示特定的popover
  157. showPopover(item) {
  158. this.popoverVisible[item] = true
  159. },
  160. // 获取label列表
  161. async getLabels() {
  162. try {
  163. this.loading = true
  164. const { data } = await queryLabels({
  165. classificationId: Array.isArray(this.activeItem) ? this.activeItem[this.activeItem.length - 1] : this.activeItem
  166. })
  167. this.childLabels = data
  168. this.loading = false
  169. const target = this.labelList.find(item => item.classificationId === this.activeItem)
  170. // 设置数据结构
  171. data.forEach(item => {
  172. if (item.labList) {
  173. item.labList.forEach(label => {
  174. // 最终值
  175. label.tagAttributes = []
  176. label.curRanges = []
  177. // 临时修改的值,点击确定后才赋值给最终的值
  178. label.tempTagAttributes = []
  179. label.tempCurRanges = []
  180. })
  181. }
  182. })
  183. if (target) {
  184. this.$set(target, 'children', data)
  185. }
  186. } catch (err) {
  187. console.log(err)
  188. }
  189. },
  190. // 设置机构标签
  191. async setOrgLabels(id) {
  192. const target = this.labelList.find(() => id === this.activeItem)
  193. const data = [{
  194. classificationId: 'org',
  195. classificationName: "组织机构",
  196. labList: [
  197. {
  198. labelId: "org_label",
  199. labelName: "中台机构",
  200. labelPropType: "org_label",
  201. labelPropList: [],
  202. labelRuleDescription: "",
  203. selected: false,
  204. tagAttributes: [],
  205. curRanges: [],
  206. value: "",
  207. orgLabel: ''
  208. }
  209. ],
  210. selected: false
  211. }]
  212. if (target) {
  213. this.$set(target, 'children', data)
  214. }
  215. },
  216. // 折叠面板展开设置数据
  217. async openCollapse(id) {
  218. if (id) {
  219. // 初始化手动添加的组织机构
  220. if (id === 'org') {
  221. await this.setOrgLabels('org')
  222. } else {
  223. // 获取当前详细labels
  224. await this.getLabels()
  225. }
  226. }
  227. },
  228. // 展开关闭collapse
  229. async handleChangeCollapse(id) {
  230. if (id) {
  231. await this.openCollapse(id)
  232. // 当前展开项的第0或者第一个子元素,由于左侧没有本身子元素所以设为第一个
  233. let classificationId = (this.childLabels[1]?.classificationId ?? this.childLabels[0]?.classificationId) || ''
  234. // 组织机构是手动添加的数据单独处理
  235. classificationId = id === 'org' ? 'org_label' : classificationId
  236. this.$emit('changeCollapse', [String(id), String(classificationId) || String(id)])
  237. // 先将右侧滚动到目标位置
  238. this.scrollToTarget(id)
  239. // 左侧滚动到目标位置
  240. this.labelListScroll()
  241. }
  242. },
  243. // 右侧滚动的时候左侧滚动到目标位置
  244. labelListScroll() {
  245. // 左侧如果正在滚动不触发右侧滚动
  246. if (window.leftScroll === true) {
  247. return
  248. }
  249. // 设置右侧正在滚动的标识,此时不触发左侧滚动
  250. window.rightScroll = true
  251. window.clearTimeout(this.scrollTimer)
  252. this.scrollTimer = setTimeout(_ => {
  253. // 右侧滚动结束,放开标识,此时才能触发左侧的滚动
  254. window.rightScroll = false
  255. }, 600)
  256. // 左侧滚动的menu
  257. const menuEl = document.querySelector('#label-mid-el-menu')
  258. // menu的滚动高度
  259. const scrollMenu = menuEl.scrollHeight - menuEl.offsetHeight
  260. // 右侧的折叠面板
  261. const labelContent = document.querySelector('.label-middleware-list-wrapper .content')
  262. // 折叠面板的滚动高度
  263. const labelScroll = labelContent.scrollHeight - labelContent.offsetHeight
  264. // 计算滚动比率
  265. const ratio = scrollMenu / labelScroll
  266. menuEl.scrollTo({
  267. top: labelContent.scrollTop * ratio
  268. })
  269. }
  270. },
  271. mounted() {
  272. const labelContent = document.querySelector('.label-middleware-list-wrapper .content')
  273. labelContent.addEventListener('scroll', this.labelListScroll)
  274. },
  275. beforeDestroy() {
  276. const labelContent = document.querySelector('.label-middleware-list-wrapper .content')
  277. labelContent.removeEventListener('scroll', this.labelListScroll)
  278. },
  279. created() {
  280. this.labelList = this.navList
  281. this.openCollapse(this.activeItem)
  282. }
  283. }
  284. </script>
  285. <style lang="scss" scoped>
  286. .label-middleware-list-wrapper {
  287. height: 100%;
  288. .content {
  289. height: 100%;
  290. overflow: auto;
  291. }
  292. .el-collapse {
  293. position: relative;
  294. }
  295. .el-collapse-item /deep/ .el-collapse-item__content {
  296. border-top: 1px solid #EBEEF5;
  297. padding-bottom: 0;
  298. }
  299. /deep/ .el-collapse-item__header{
  300. font-size: 14px;
  301. font-family: PingFangSC-Regular, PingFang SC;
  302. font-weight: 500;
  303. color: #262626;
  304. }
  305. .sub-box {
  306. padding: 8px 16px 0 12px;
  307. background: #FAFAFA;
  308. .sub-title {
  309. height: 18px;
  310. font-size: 13px;
  311. font-family: PingFangSC-Regular, PingFang SC;
  312. font-weight: 400;
  313. color: #292B33;
  314. line-height: 18px;
  315. margin-bottom: 9px;
  316. }
  317. .label-box {
  318. display: flex;
  319. /deep/ .label-item {
  320. white-space: nowrap;
  321. display: flex;
  322. align-items: center;
  323. height: 28px;
  324. padding: 0 8px;
  325. background: #F1F1F1;
  326. border-radius: 3px;
  327. font-size: 12px;
  328. font-family: PingFangSC-Regular, PingFang SC;
  329. font-weight: 400;
  330. color: #595959;
  331. margin-right: 8px;
  332. margin-bottom: 8px;
  333. cursor: pointer;
  334. }
  335. }
  336. }
  337. }
  338. </style>

 menu 和 右侧的 LbelList 组件的父组件:

  1. <template>
  2. <el-dialog
  3. class="dialog"
  4. title="标签组合筛选"
  5. width="80%"
  6. :visible="visible"
  7. :close-on-click-modal="false"
  8. @close="handleClose"
  9. >
  10. <div class="body">
  11. <div class="line"></div>
  12. <div class="content">
  13. <div class="left-nav">
  14. <nav-list
  15. :navList="navList"
  16. :curMenuId.sync="curMenuId"
  17. @setCurIdArea="setCurIdArea" />
  18. </div>
  19. <div class="label-collapse">
  20. <label-list
  21. :navList="navList"
  22. :selectedLabels="selectedLabels"
  23. :curIdArea="curIdArea"
  24. :curAreaType="curAreaType"
  25. @addLabels="addLabels"
  26. @changeCollapse="changeCollapse" />
  27. </div>
  28. </div>
  29. <div class="selected-label">
  30. <div class="title">已选标签:</div>
  31. <div class="selected-box labels-box">
  32. <div class="label-item" v-for="(label, index) in selectedLabels" :key="label.labelId">
  33. <div class="text">
  34. <div>{{label.labelName}}:</div>
  35. <div class="value-item"
  36. v-for="item in renderLabelInfo(label)"
  37. :key="item.showValue">{{item.showValue}}</div>
  38. </div>
  39. <div @click="deleteLabel(label, index)" class="delete-label el-icon-close"></div>
  40. </div>
  41. </div>
  42. </div>
  43. </div>
  44. <div class="footer" slot="footer">
  45. <div class="tip">共筛选出<span class="number">{{total}}</span></div>
  46. <div class="buttons">
  47. <button class="btn-reset" @click="handleClickClearAll">清空重选</button>
  48. <button class="btn-confirm" @click="handleClickSubmit">确定</button>
  49. </div>
  50. </div>
  51. </el-dialog>
  52. </template>
  53. <script>
  54. import NavList from './NavList.vue'
  55. import LabelList from './LabelList.vue'
  56. import { getClassificationTree } from '@/api/modules/labelMiddleware.js'
  57. export default {
  58. components: {
  59. NavList,
  60. LabelList
  61. },
  62. props: {
  63. visible: {
  64. type: Boolean,
  65. default: false
  66. },
  67. selectedLabels: {
  68. type: Array,
  69. default: () => []
  70. },
  71. total: {
  72. type: Number
  73. },
  74. renderLabelInfo: {
  75. type: Function,
  76. default: () => () => {}
  77. }
  78. },
  79. data() {
  80. return {
  81. navList: [],
  82. search: '',
  83. curIdArea: [],
  84. // 左侧选中的type,展开还是点击item
  85. curAreaType: '',
  86. curMenuId: ['org', 'org_label']
  87. }
  88. },
  89. methods: {
  90. // 添加label
  91. addLabels(label) {
  92. this.$emit('addLabels', label)
  93. },
  94. // 删除label
  95. deleteLabel(label, index) {
  96. this.$emit('deleteLabel', label, index)
  97. },
  98. // 左侧菜单当前展开项
  99. setCurIdArea(keyPath, type) {
  100. this.curIdArea = keyPath
  101. this.curAreaType = type
  102. },
  103. // 获取navList,左侧的menu菜单数据,右侧的面板一级数据
  104. async getNavList() {
  105. const { data } = await getClassificationTree()
  106. this.navList = data || []
  107. this.navList.unshift({
  108. classificationId: 'org',
  109. classificationName: "组织机构",
  110. childList: [
  111. {
  112. childList: null,
  113. classificationId: 'org_label',
  114. classificationName: "组织机构"
  115. }
  116. ]
  117. })
  118. },
  119. // 点击清空重选
  120. handleClickClearAll() {
  121. this.$emit('resetSelectLabels')
  122. },
  123. // 点击确定
  124. handleClickSubmit() {
  125. this.$emit('getTableData')
  126. this.handleClose()
  127. },
  128. // 关闭dialog
  129. handleClose() {
  130. this.$emit('closeDialog')
  131. },
  132. // 修改了折叠面板,设置当前展开想的父级和当前id
  133. changeCollapse(idArr) {
  134. this.curMenuId = idArr
  135. }
  136. },
  137. mounted() {
  138. this.getNavList()
  139. }
  140. }
  141. </script>
  142. <style lang="scss" scoped>
  143. .dialog {
  144. /deep/ .el-dialog{
  145. margin-top: 8vh !important;
  146. }
  147. /deep/ .el-dialog__body {
  148. padding: 0;
  149. }
  150. .left-nav {
  151. height: 100%;
  152. width: 240px;
  153. }
  154. .content {
  155. display: flex;
  156. border: 1px solid rgba(0, 0, 0, 0.1);
  157. border-right: none;
  158. border-left: none;
  159. margin-bottom: 8px;
  160. height: 556px;
  161. padding: 0;
  162. padding-right: 16px;
  163. .label-collapse {
  164. flex: 1;
  165. overflow: auto;
  166. /deep/ {
  167. .el-collapse-item__header {
  168. padding-left: 20px;
  169. }
  170. }
  171. }
  172. }
  173. .selected-label {
  174. margin-bottom: 18px;
  175. padding: 0 16px;
  176. .title {
  177. height: 20px;
  178. font-size: 14px;
  179. font-family: PingFangSC-Medium, PingFang SC;
  180. font-weight: 500;
  181. color: #262626;
  182. line-height: 20px;
  183. margin-bottom: 12px;
  184. }
  185. .labels-box {
  186. display: flex;
  187. min-height: 50px;
  188. max-height: 76px;
  189. overflow: auto;
  190. flex: 1;
  191. flex-wrap: wrap;
  192. .label-item {
  193. display: flex;
  194. align-items: center;
  195. height: 28px;
  196. background: rgba(224, 45, 57, 0.06);
  197. border-radius: 3px;
  198. padding: 4px 11px 4px 8px;
  199. margin-right: 8px;
  200. margin-bottom: 8px;
  201. white-space: nowrap;
  202. .text {
  203. display: flex;
  204. align-items: center;
  205. white-space: nowrap;
  206. font-size: 14px;
  207. font-family: PingFangSC-Regular, PingFang SC;
  208. font-weight: 400;
  209. color: #595959;
  210. margin-right: 24px;
  211. .value-item:not(:last-child){
  212. margin-right: 8px;
  213. }
  214. }
  215. .delete-label {
  216. color: #8C8C8C;
  217. cursor: pointer;
  218. font-size: 16px;
  219. }
  220. }
  221. }
  222. }
  223. .footer {
  224. display: flex;
  225. justify-content: space-between;
  226. align-items: center;
  227. .tip {
  228. font-size: 12px;
  229. font-family: PingFangSC-Regular, PingFang SC;
  230. font-weight: 400;
  231. color: #595959;
  232. line-height: 17px;
  233. .number {
  234. font-size: 12px;
  235. font-family: PingFangSC-Regular, PingFang SC;
  236. font-weight: 400;
  237. color: #E02D39;
  238. line-height: 17px;
  239. padding: 0 4px;
  240. }
  241. }
  242. .buttons {
  243. display: flex;
  244. align-items: center;
  245. .btn-reset {
  246. width: 72px;
  247. height: 28px;
  248. display: flex;
  249. align-items: center;
  250. justify-content: center;
  251. border-radius: 3px;
  252. border: 1px solid #E02D39;
  253. font-size: 12px;
  254. font-family: PingFangSC-Medium, PingFang SC;
  255. font-weight: 500;
  256. color: #E02D39;
  257. margin-right: 8px;
  258. cursor: pointer;
  259. }
  260. .btn-confirm {
  261. height: 28px;
  262. display: flex;
  263. align-items: center;
  264. justify-content: center;
  265. width: 60px;
  266. background: #E02D39;
  267. border-radius: 3px;
  268. font-size: 12px;
  269. font-family: PingFangSC-Medium, PingFang SC;
  270. font-weight: 500;
  271. color: #FFFFFF;
  272. cursor: pointer;
  273. }
  274. }
  275. }
  276. }
  277. </style>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/971978
推荐阅读
相关标签
  

闽ICP备14008679号