yui-tabs.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. <template>
  2. <view class="yui-tabs" :class="[tabsClass]">
  3. <!-- 依赖元素,用于处理滚动吸顶所需 -->
  4. <view class="depend-wrap"></view>
  5. <!-- 标签区域 -->
  6. <view class="yui-tabs__wrap" :style="[innerWrapStyle,wrapStyle]">
  7. <!-- scrollX为true,表示允许横向滚动 -->
  8. <scroll-view class="yui-tabs__scroll" :class="[scrollX?'enable-sroll':'']" :scroll-x="scrollX" :scroll-anchoring="true" enable-flex :scroll-left="scrollLeft"
  9. :scroll-into-view="!scrollToCenter?scrollId:''" scroll-with-animation :style="[scrollStyle]">
  10. <view class="yui-tabs__nav" :class="[navClass]" :style="[navStyle]">
  11. <view class="yui-tab" v-for="(tab,index) in tabList" :key="index" @tap.stop="onClick(index,true)" :id="`tab_${index}`" :class="[tabClass(index, tab)]" :style="[tabStyle(tab)]">
  12. <view class="yui-tab__text">
  13. <!-- #ifndef VUE3 -->
  14. <slot :name="tab.titleSlot">{{tab.label}}</slot>
  15. <!-- #endif -->
  16. <!-- #ifdef VUE3 -->
  17. {{tab.label}}
  18. <!-- #endif -->
  19. <text :class="[infoClass(tab)]" v-if="tab.badge || tab.dot">{{tab.badge}}</text>
  20. </view>
  21. </view>
  22. <view v-if="isLine" class="yui-tabs__line" :style="[lineStyle,lineAnimatedStyle]"></view>
  23. </view>
  24. </scroll-view>
  25. <!-- 标签栏额外内容 -->
  26. <view class="yui-tabs__extra">
  27. <slot name="extra"></slot>
  28. </view>
  29. </view>
  30. <view v-if="isFixed" class="yui-tabs__placeholder" :style="[{height:placeholderHeight+'px'}]"></view>
  31. <!-- 标签内容:普通实现 -->
  32. <view v-if="!noRenderConent && !swiper" class="yui-tabs__content" :class="{'yui-tabs__content--animated':animated,'yui-tabs__content--scrollspy':scrollspy}">
  33. <view class="yui-tabs__track" :style="[trackStyle]">
  34. <view class="yui-tab__pane" :class="[paneClass(index,tab)]" v-for="(tab,index) in tabList" :key="index" :style="[tab.paneStyle]" @touchstart="touchStart"
  35. @touchmove="touchMove($event,index)" @touchend="touchEnd($event,index)">
  36. <view v-if="tab.rendered ? true :value == index">
  37. <slot :name="tab.slot"></slot>
  38. </view>
  39. </view>
  40. </view>
  41. </view>
  42. <!-- 标签内容:使用swiper组件实现左右滑动 -->
  43. <swiper v-if="!noRenderConent && swiper" class="yui-tabs__swiper" :current="current" :duration="swiperDuration" @change="onSwiperChange">
  44. <swiper-item class="yui-tabs__swiper--item" v-for="(tab,index) in tabList" :key="index" @touchmove="stopTouchMove">
  45. <view class="yui-tabs__swiper--wrap" v-if="tab.rendered ? true :value == index">
  46. <slot :name="tab.slot"></slot>
  47. </view>
  48. </swiper-item>
  49. </swiper>
  50. </view>
  51. </template>
  52. <script>
  53. import {
  54. isNull,
  55. addUnit,
  56. isDef,
  57. isObject,
  58. getDirection,
  59. callInterceptor,
  60. } from "../yui-tabs/utils/uitls"
  61. import {
  62. emits,
  63. props,
  64. valueField
  65. } from "../yui-tabs/utils/const"
  66. import { touchMixin } from "../yui-tabs/utils/touchMixin"
  67. export default {
  68. name: "yui-tabs",
  69. mixins: [touchMixin],
  70. emits,
  71. // shared:表示页面 wxss 样式将影响到自定义组件
  72. options: {
  73. styleIsolation: 'shared'
  74. },
  75. props,
  76. data() {
  77. return {
  78. currentIndex: null,
  79. tabList: [], //标签页数据
  80. scrollId: 'tab_0', //值应为某子元素id(id不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素
  81. scrollLeft: 0, //设置横向滚动条位置
  82. extraWidth: 0, //标签栏右侧额外区域宽度
  83. contentWidth: 0, //标签内容宽度
  84. trackStyle: null, //标签内容滑动轨道样式
  85. touchInfo: {
  86. inited: false, //标记左右滑动时的初始化状态
  87. startX: null, //记录touch位置的横坐标
  88. startY: null, //记录touch位置的纵坐标
  89. moved: false, //用来判断是否是一次移动
  90. deltaX: 0, //记录拖动的横坐标距离
  91. isLeftSide: false, //标记是否为左滑
  92. },
  93. // 标签栏底部线条动画相关
  94. lineAnimated: false, //是否开启标签栏底部线条动画(首次不开启)
  95. lineAnimatedStyle: {
  96. transform: `translateX(-100%) translateX(-50%)`,
  97. transitionDuration: `0s`
  98. }, //标签栏底部线条动画样式
  99. isFixed: false, //是否吸顶
  100. current: this.currentIndex, //当前显示的滚动卡片
  101. isTabClick: false, //是否为标签标题点击
  102. placeholderHeight: 0, //标题栏占位高度
  103. windowHeight: 0, //屏幕高度
  104. lockedScrollspy: false, //锁定滚动导航模式下点击标题栏触发的滚动逻辑
  105. }
  106. },
  107. computed: {
  108. // 样式风格是否为line
  109. isLine() {
  110. return this.type === "line"
  111. },
  112. // 标签页容器class
  113. tabsClass() {
  114. return `yui-tabs--${this.type} ${this.visible?'yui-tabs--visible':''} ${this.fixed || this.isFixed?'yui-tabs--fixed':''} ${this.swiper?'yui-tabs--swiper':''} `
  115. },
  116. // 标签栏class
  117. navClass() {
  118. return `yui-tabs__nav--${this.type}`
  119. },
  120. // 标签栏style
  121. navStyle() {
  122. const style = {}
  123. if (this.type === "card") style.borderColor = this.color
  124. return style
  125. },
  126. // 标签栏包裹层样式
  127. innerWrapStyle() {
  128. const style = {
  129. background: this.background,
  130. }
  131. // 滚动吸顶下
  132. if (this.fixed || this.isFixed) {
  133. style.top = this.offsetTop + "px"
  134. style.zIndex = this.zIndex
  135. }
  136. return style
  137. },
  138. // 滚动区域样式
  139. scrollStyle() {
  140. return {
  141. width: `calc(100% - ${this.extraWidth}px)`
  142. }
  143. },
  144. // 标签栏底部线条样式
  145. lineStyle() {
  146. const {
  147. lineWidth,
  148. lineHeight,
  149. duration
  150. } = this;
  151. const lineStyle = {
  152. width: addUnit(lineWidth),
  153. backgroundColor: this.color,
  154. }
  155. if (isDef(lineHeight)) {
  156. const height = addUnit(lineHeight);
  157. lineStyle.height = height;
  158. lineStyle.borderRadius = height;
  159. }
  160. return lineStyle
  161. },
  162. // 是否允许横向滚动
  163. scrollX() {
  164. return !this.ellipsis && this.type !== "card" && this.tabs.length > this.scrollThreshold
  165. },
  166. // 标签数量
  167. dataLen() {
  168. return this.tabList.length
  169. },
  170. // swiper组件滑动动画时长
  171. swiperDuration() {
  172. return this.animated ? this.duration * 1000 : 0
  173. },
  174. // 粘性布局下的滚动偏移量
  175. scrollOffset() {
  176. return this.sticky ? this.offsetTop + this.placeholderHeight : 0;
  177. },
  178. },
  179. watch: {
  180. // 监听tabs变化,重新初始化tabList
  181. tabs: {
  182. handler() {
  183. this.updateTabList(); //更新tabList
  184. },
  185. deep: true
  186. },
  187. currentIndex: {
  188. handler(newIdx, oldIdx) {
  189. this.current = newIdx
  190. this.tabChange(newIdx, oldIdx) //标签切换
  191. this.changeStyle() // 样式切换
  192. },
  193. },
  194. },
  195. created() {
  196. // 监听选中标识符变化
  197. this.$watch(valueField, (index) => {
  198. if (index !== this.currentIndex) {
  199. this.setCurrentIndex(index) //设置当前下标
  200. setTimeout(() => {
  201. this.scrollToTop() //滚动到顶
  202. this.scrollToCurrContent() //滚动到当前标签内容
  203. }, 0)
  204. }
  205. })
  206. this.initTabList() // 初始化tabList
  207. },
  208. mounted() {
  209. this.$nextTick(() => {
  210. this.init() //初始化操作
  211. this.listenEvent(); //监听事件
  212. })
  213. },
  214. methods: {
  215. // @exposed-api
  216. resize() {
  217. // 外层元素大小或组件显示状态变化时,可以调用此方法来触发重绘
  218. this.init()
  219. },
  220. // 获取查询节点信息的对象
  221. getSelectorQuery() {
  222. let query = null
  223. // #ifdef MP-ALIPAY
  224. query = uni.createSelectorQuery()
  225. // #endif
  226. // #ifndef MP-ALIPAY
  227. query = uni.createSelectorQuery().in(this)
  228. // #endif
  229. return query
  230. },
  231. // 获取元素位置信息
  232. getRect(...selectors) {
  233. return new Promise((resolve, reject) => {
  234. if (!selectors) reject('Parameter is empty');
  235. const query = this.getSelectorQuery()
  236. selectors.forEach(seletor => {
  237. query.select(seletor).boundingClientRect()
  238. })
  239. query.exec(data => {
  240. data = data || []
  241. resolve(data.length === 1 ? data[0] : data)
  242. });
  243. })
  244. },
  245. // 标签项class
  246. tabClass(index, tab) {
  247. return `yui-tab_${index} ${tab.active?'yui-tab--active':''} ${this.ellipsis && !this.scrollX?'yui-tab__ellipsis':''}`
  248. },
  249. // 标签内容class
  250. paneClass(index, tab) {
  251. return `yui-tab__pane${index} ${tab.active?'yui-pane--active':''}`
  252. },
  253. // 标签项style
  254. tabStyle(tab) {
  255. let activeColor = this.titleActiveColor
  256. let inactiveColor = this.titleInactiveColor
  257. let background = ""
  258. let borderColor = ""
  259. // type="line" 时
  260. if (this.type === "line") {
  261. if (isNull(activeColor)) activeColor = "#646566"
  262. if (isNull(inactiveColor)) inactiveColor = "#323233"
  263. } else if (this.type === "text") { // type="text" 时,选中时使用主题色
  264. if (isNull(activeColor)) activeColor = this.color
  265. if (isNull(inactiveColor)) inactiveColor = "#323233"
  266. } else if (this.type === "card") { // type="card" 时,未选中则使用主题色
  267. background = this.color
  268. if (isNull(activeColor)) activeColor = "#fff"
  269. if (isNull(inactiveColor)) inactiveColor = this.color
  270. } else if (this.type === "button") { // type="button" 时
  271. background = this.color
  272. if (isNull(activeColor)) activeColor = "#fff"
  273. if (isNull(inactiveColor)) inactiveColor = "#323233"
  274. } else if (this.type === "line-button") { // type="line-button" 时
  275. borderColor = this.color
  276. if (isNull(activeColor)) activeColor = this.color
  277. if (isNull(inactiveColor)) inactiveColor = "#323233"
  278. }
  279. return {
  280. color: tab.active ? activeColor : inactiveColor,
  281. background: tab.active ? background : "",
  282. borderColor: tab.active ? borderColor : "",
  283. }
  284. },
  285. // 标题右上角信息class
  286. infoClass(tab) {
  287. return ` yui-tab__info ${tab.dot?'yui-tab__info--dot':''}`
  288. },
  289. // 监听事件
  290. listenEvent() {
  291. const that = this
  292. if (that.sticky || that.scrollspy) {
  293. uni.$on('onPageScroll', function(e) {
  294. const {
  295. stickyThreshold,
  296. offsetTop,
  297. scrollspy,
  298. lockedScrollspy,
  299. } = that
  300. // 粘性定位布局的吸顶处理
  301. that.getRect('.depend-wrap').then((rect) => {
  302. that.isFixed = rect.bottom - stickyThreshold <= offsetTop
  303. // 滚动时触发,仅在 sticky 模式下生效,{ scrollTop: 距离顶部位置, isFixed: 是否吸顶 }
  304. that.$emit("scroll", {
  305. scrollTop: e.scrollTop,
  306. isFixed: that.isFixed
  307. })
  308. })
  309. // 滚动导航模式下对选中标签的处理
  310. if (scrollspy && !lockedScrollspy) {
  311. that.getCurrIndexOnScroll().then(index => {
  312. that.setCurrentIndex(index) //设置当前下标
  313. })
  314. }
  315. })
  316. }
  317. },
  318. // 滚动时获取要选中的下标
  319. getCurrIndexOnScroll(res = []) {
  320. return new Promise((resolve, rejct) => {
  321. const selectors = this.tabList.map((o, i) => '.yui-tab__pane' + i)
  322. this.getRect(...selectors).then(res => {
  323. // 标签内容的top小于标题栏的top,则说明已经与标题栏部分重合
  324. let index = res.reduce((idx, o, i) => o.top < this.scrollOffset ? i : idx, 0)
  325. // 判断最后一个标签内容是否完整显示在底部,是则默认选中
  326. // const lastRect = res[res.length - 1] //最后一个标签内容
  327. // if (lastRect.bottom <= this.windowHeight) index = res.length - 1
  328. resolve(index)
  329. })
  330. })
  331. },
  332. // 初始化tabList
  333. initTabList() {
  334. const tabs = this.tabs.filter(o => !isNull(o))
  335. this.tabList = tabs.map((item, index) => {
  336. const isCurr = this.currentIndex == index
  337. const tab = {
  338. label: '', //标签名称
  339. slot: 'pane' + index, //标签内容的插槽名称,默认以"pane"+标签下标命名
  340. titleSlot: 'title' + index, //标签标题的插槽名称,默认以"title"+标签下标命名
  341. disabled: false, //是否禁用标签
  342. active: false, //是否选中
  343. rendered: !this.isLazyRender || this.scrollspy, //标记是否渲染过,非懒加载与滚动导航模式下默认渲染
  344. show: this.scrollspy, // 是否显示内容(滚动导航模式默认显示)
  345. dot: false, //是否在标题右上角显示小红点
  346. translateX: 0, //底部线条translateX值,
  347. scrollLeft: 0, //当前标签对应的横向滚动条位置
  348. paneTop: 0, //滚动导航模式下标签内容距离屏幕顶部的距离
  349. }
  350. tab.paneStyle = this.animated ? {
  351. visibility: tab.show ? 'visible' : 'visible',
  352. height: tab.show ? 'auto' : '0px'
  353. } : {
  354. display: tab.show ? 'block' : 'none'
  355. };
  356. // 读取标签对象值
  357. if (isObject(item)) {
  358. tab.label = item.label
  359. tab.slot = isNull(item.slot) ? tab.slot : item.slot
  360. tab.titleSlot = isNull(item.titleSlot) ? tab.titleSlot : item.titleSlot
  361. tab.dot = isNull(item.dot) ? tab.dot : item.dot
  362. tab.badge = !isNull(item.badge) && !tab.dot ? item.badge : ""
  363. } else {
  364. tab.label = item
  365. }
  366. return tab
  367. })
  368. },
  369. // 更新tabList
  370. updateTabList() {
  371. // 如果长度有变化,表示tabs有删除或新增,重新init一次
  372. if (this.tabs.length != this.dataLen) {
  373. this.initTabList() //初始化tabList
  374. } else {
  375. // 否则仅仅更改label,badge,dot值
  376. this.tabs.forEach((item, i) => {
  377. const tab = this.tabList[i]
  378. // 读取标签对象值
  379. if (isObject(item)) {
  380. this.$set(tab, "label", item.label)
  381. this.$set(tab, "dot", isNull(item.dot) ? tab.dot : item.dot)
  382. this.$set(tab, "badge", !isNull(item.badge) && !tab.dot ? item.badge : "")
  383. } else {
  384. this.$set(tab, "label", item)
  385. }
  386. })
  387. }
  388. this.$nextTick(() => {
  389. this.init() //初始化操作
  390. })
  391. },
  392. // 初始化操作
  393. async init() {
  394. //屏幕高度
  395. this.windowHeight = uni.getSystemInfoSync().windowHeight;
  396. //获取额外区域的宽度
  397. let rect = await this.getRect('.yui-tabs__extra')
  398. this.extraWidth = rect ? rect.width : 0
  399. //获取标签内容区域的宽度
  400. rect = await this.getRect('.yui-tabs__content')
  401. this.contentWidth = rect ? rect.width : 0
  402. // 获取标题栏包裹层的rect
  403. rect = await this.getRect('.yui-tabs__wrap')
  404. const halfWrapWidth = rect ? rect.width / 2 : 0
  405. this.placeholderHeight = rect ? rect.height : 0
  406. //获取标签容器距离视口左侧的left值
  407. rect = await this.getRect('.yui-tabs')
  408. const parentLeft = rect ? rect.left : 0
  409. // 计算每个tab的相关参数
  410. const isSpy = this.scrollspy //是否滚动导航模式
  411. const selectors = this.tabList.reduce((arr, tab, index) => {
  412. arr.push('.yui-tab_' + index)
  413. if(isSpy) arr.push('.yui-tab__pane' + index)
  414. return arr
  415. }, [])
  416. const rects = await this.getRect(...selectors);
  417. this.tabList.forEach((tab) => {
  418. const [r1, r2] = rects.splice(0, isSpy ? 2 : 1)
  419. tab.translateX = r1 ? r1.left + r1.width / 2 - parentLeft : 0 //标签线条偏移量
  420. tab.scrollLeft = tab.translateX - halfWrapWidth //标签相对于屏幕左侧的距离值
  421. if (isSpy) tab.paneTop = r2 ? r2.top : 0; //标签内容相对于屏幕顶部的距离值
  422. })
  423. this.setCurrentIndex(this[valueField]) //设置当前下标
  424. },
  425. // 标签点击事件
  426. onClick(index, isTabClick = false) {
  427. this.isTabClick = isTabClick // 是否为标签标题点击
  428. if (isTabClick) this.$emit('click', index, this.tabs[index], this.isTabClick) // 标签点击事件
  429. callInterceptor({
  430. interceptor: this.beforeChange,
  431. args: [index],
  432. done: () => {
  433. this.setCurrentIndex(index) //设置当前下标
  434. setTimeout(() => {
  435. this.scrollToTop() //滚动到顶
  436. this.scrollToCurrContent() //滚动到当前标签内容
  437. }, 0)
  438. },
  439. });
  440. },
  441. // 设置当前下标
  442. setCurrentIndex(newIdx) {
  443. const shouldEmit = this.currentIndex !== newIdx
  444. const shouldEmitChange = this.currentIndex !== null
  445. const currTab = this.tabList[newIdx] //当前tab
  446. // 非滚动导航模式下,触发rendered事件
  447. if (this.isLazyRender && !this.scrollspy && !currTab.rendered) {
  448. this.$emit('rendered', newIdx, this.tabs[newIdx])
  449. }
  450. this.currentIndex = newIdx
  451. if (shouldEmit) { //禁止重复切换
  452. this.$emit(emits[0], newIdx) // 更新v-model绑定的值
  453. if (shouldEmitChange) {
  454. this.$emit('change', newIdx, this.tabs[newIdx], this.isTabClick)
  455. }
  456. }
  457. },
  458. // 滚动到顶
  459. scrollToTop() {
  460. //标签点击时页面回滚顶部
  461. if (!this.scrollspy && this.tabClickScrollTop) {
  462. setTimeout(function() {
  463. uni.pageScrollTo({
  464. scrollTop: 0,
  465. duration: 0
  466. });
  467. }, this.duration * 1000);
  468. }
  469. },
  470. // 滚动到当前标签内容
  471. scrollToCurrContent(immediate = false) {
  472. if (this.scrollspy) {
  473. const duration = immediate ? 0 : this.duration * 1000
  474. this.lockedScrollspy = true
  475. uni.pageScrollTo({
  476. scrollTop: this.tabList[this.currentIndex].paneTop,
  477. duration,
  478. });
  479. setTimeout(() => {
  480. this.lockedScrollspy = false
  481. }, duration * 2)
  482. }
  483. },
  484. // 标签切换
  485. tabChange(newIdx, oldIdx) {
  486. const oldTab = this.tabList[oldIdx] || {} //上一个tab
  487. const currTab = this.tabList[newIdx] || {} //当前tab
  488. // 设置选中态
  489. oldTab.active = false
  490. currTab.active = true
  491. //非滚动导航模式下
  492. if (!this.scrollspy) {
  493. currTab.rendered = true //标记当前tab的内容渲染过
  494. oldTab.show = false //隐藏旧tab的内容
  495. currTab.show = true //显示当前tab的内容
  496. }
  497. },
  498. // 样式切换
  499. changeStyle() {
  500. this.scrollIntoView() //将活动的tab滚动到视图中
  501. this.setLine() //改变标签栏底部线条位置
  502. // 标签内容滑动非swiper实现及非滚动导航模式时
  503. if (!this.swiper && !this.scrollspy) {
  504. this.changeTrackStyle(false, this.duration) //改变标签内容滑动轨道样式
  505. this.changePaneStyle() //改变标签内容样式
  506. }
  507. },
  508. // 将活动的tab滚动到视图中
  509. scrollIntoView() {
  510. // 标签栏允许滚动:设置横向滚动条位置,scrollToCenter为true,当前标签则滚动至中心位置
  511. if (this.scrollX) {
  512. if (this.scrollToCenter) this.scrollLeft = this.tabList[this.currentIndex].scrollLeft
  513. else this.scrollId = `tab_${this.currentIndex-1}`;
  514. }
  515. },
  516. // 设置标签栏底部线条位置
  517. setLine() {
  518. if (this.isLine) { // 仅在 type="line" 时有效
  519. const val = this.tabList[this.currentIndex].translateX
  520. const transform = `translateX(${isDef(val) ? val + "px" : '-100%'}) translateX(-50%)`
  521. const duration = `${this.lineAnimated?this.duration:'0'}s`
  522. this.$set(this.lineAnimatedStyle, 'transform', transform)
  523. this.$set(this.lineAnimatedStyle, 'transitionDuration', duration)
  524. this.lineAnimated = true //是否开启标签栏动画
  525. }
  526. },
  527. // 改变标签内容滑动轨道样式
  528. changeTrackStyle(isSlide = false, duration = 0, offsetWidth = 0) {
  529. if (this.animated) {
  530. // isSlide为true,表示左右滑动;false表示点击标签的转场动画
  531. this.trackStyle = {
  532. 'transform': isSlide ? `translate3d(${offsetWidth}px,0,0)` : `translateX(${-100 * this.currentIndex}%)`,
  533. 'transition': `transform ${duration}s ease-in-out`
  534. }
  535. }
  536. },
  537. // 改变标签内容样式
  538. changePaneStyle() {
  539. this.getRect('.yui-tab__pane' + this.currentIndex).then(rect => {
  540. // 有拖动动画时,隐藏的标签内容高度取显示的标签内容高度
  541. const height = rect && this.swipeAnimated ? rect.height : 0
  542. this.tabList.forEach(tab => {
  543. const paneStyle = this.animated ? {
  544. // 有拖动动画时或指定标签内容显示时,为visible
  545. visibility: this.swipeAnimated || tab.show ? 'visible' : 'hidden',
  546. height: tab.show ? 'auto' : height + 'px'
  547. } : {
  548. display: tab.show ? 'block' : 'none'
  549. };
  550. this.$set(tab, "paneStyle", paneStyle)
  551. })
  552. })
  553. },
  554. // 禁止swiper组件手动滑动
  555. stopTouchMove(event) {
  556. if (!this.swipeable) event.stopPropagation()
  557. },
  558. // swiper组件的current改变时会触发 change 事件
  559. onSwiperChange(e) {
  560. this.setCurrentIndex(e.target.current || e.detail.current) //设置当前下标
  561. },
  562. }
  563. }
  564. </script>
  565. <style lang="less" scoped>
  566. @import url("css/index.less");
  567. </style>