xTab.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <template>
  2. <view :style="{padding:'0 '+conf.padding+'rpx',height:conf.height+'rpx'}" class="view-scroll">
  3. <scroll-view class="scroll-view" scroll-with-animation :scroll-x="!adaptation" :scroll-into-view="'sv_'+currAct"
  4. :style="{height:(conf.height)+'rpx'}">
  5. <view :class="adaptation?'adaptation':''">
  6. <view :id="'sv_'+item[setField.id]" @click="select(i)" v-for="(item,i) of tabList" :key="i" class="view-content"
  7. :style="{'font-size':conf.size+'rpx',color:conf.color,'margin-right':adaptation?0:conf.spacing+'rpx',height:conf.height+'rpx','line-height':conf.height+'rpx'}">
  8. <label :id="'txt_'+item[setField.id]" class="txt" :style="{color:currAct===item.id?(conf.actColor):(conf.color),'font-weight':(currAct===item.id?conf.actWeight:'initial')}">{{item[setField.name]}}</label>
  9. </view>
  10. <text v-if="actType =='triangle'" :style="{color:conf.actColor,left:moves+'px',top:((conf.height)-20)+'rpx'}" class="xTab-icon icon_act">&#xe6b5;</text>
  11. <text v-if="actType =='underline'" :style="{width:lineWidth+'px',background:conf.actColor,left:moves+'px',top:(conf.height-4)+'rpx'}"
  12. class="underline"></text>
  13. </view>
  14. </scroll-view>
  15. </view>
  16. </template>
  17. <script>
  18. export default {
  19. name: 'xTab',
  20. props: {
  21. config: {
  22. type: Object,
  23. default: () => {
  24. return {}
  25. }
  26. },
  27. value: {
  28. type: Array,
  29. default: () => {
  30. return []
  31. }
  32. },
  33. actValue: {
  34. type: Number,
  35. default: 0
  36. },
  37. setField: { //设置字段
  38. type: Object,
  39. default: () => {
  40. return {
  41. id: 'id',
  42. name: 'name'
  43. }
  44. }
  45. },
  46. adaptation: { //是否自适应显示
  47. type: Boolean,
  48. default: false
  49. },
  50. actType: {
  51. type: String,
  52. default: 'triangle' //选中类型: triangle (三角形)| underline (下划线)
  53. }
  54. },
  55. data() {
  56. return {
  57. conf: {
  58. height: 80,
  59. padding: 30,
  60. size: 34,
  61. color: '#FEC1AB',
  62. actColor: '#FFFFFF',
  63. spacing: 44,
  64. position: 0,
  65. actWeight: '100',
  66. },
  67. currAct: null,
  68. moves: 0,
  69. lineWidth: 0,
  70. tabList: [],
  71. screenWidth: null,
  72. isClick: null,
  73. }
  74. },
  75. created() {
  76. const that = this;
  77. uni.getSystemInfo({
  78. success: (res) => {
  79. that.screenWidth = res.screenWidth;
  80. }
  81. });
  82. this.init();
  83. },
  84. watch: {
  85. actValue(n, o) {
  86. if (this.value.length) {
  87. this.currAct = this.value[n][this.setField.id];
  88. if (!this.isClick) {
  89. this.emit(n);
  90. this.operate(n);
  91. }
  92. this.isClick = false;
  93. }
  94. },
  95. value() {
  96. this.init();
  97. }
  98. },
  99. methods: {
  100. init() {
  101. const that = this;
  102. //数据初始化
  103. if (this.value.length) {
  104. this.tabList = JSON.parse(JSON.stringify(this.value));
  105. this.conf = Object.assign(this.conf, this.config);
  106. this.currAct = this.value[this.actValue][this.setField.id];
  107. this.$nextTick(function() {
  108. that.setLineWidth();
  109. })
  110. }
  111. },
  112. select(i) {
  113. this.emit(i);
  114. this.operate(i);
  115. this.isClick = true;
  116. },
  117. operate(curr) {
  118. curr = Number(curr);
  119. let item = this.tabList[curr];
  120. if (this.actType === "underline") {
  121. this.lineWidth = item.width || 0;
  122. this.lineWidth -= this.conf.position * 2;
  123. }
  124. if (curr > 0) {
  125. this.deal(curr);
  126. } else {
  127. //第一个位置
  128. this.moves = this.conf.position
  129. }
  130. },
  131. deal(index) {
  132. let leng = 0;
  133. for (let i = 0; i < index; i++) {
  134. leng += this.tabList[i].width + (this.conf.spacing / 2);
  135. }
  136. this.moves = leng + this.conf.position;
  137. },
  138. emit(i) {
  139. let e = this.value[i];
  140. this.currAct = e[this.setField.id];
  141. e.index = i;
  142. this.$emit("changeTab", e);
  143. },
  144. setLineWidth() {
  145. const that = this;
  146. let w = null;
  147. this.tabList.map((item, index) => {
  148. let obj = uni.createSelectorQuery().in(this).select("#txt_" + item.id);
  149. obj.boundingClientRect(function(data) { //data - 各种参数
  150. if (data) {
  151. item.width = data.width;
  152. w += data.width;
  153. if (index == that.tabList.length - 1) {
  154. if (that.adaptation) {
  155. if (w >= that.screenWidth) {
  156. that.conf.spacing = 0;
  157. } else {
  158. let space = that.screenWidth - that.conf.padding - w;
  159. that.conf.spacing = Number(space / index) * 2;
  160. }
  161. }
  162. that.operate(that.actValue);
  163. }
  164. }
  165. }).exec();
  166. });
  167. }
  168. }
  169. }
  170. </script>
  171. <style scoped>
  172. @font-face {
  173. font-family: 'xTab-icon';
  174. /* project id 2137302 */
  175. src: url('//at.alicdn.com/t/font_2137302_tcl4wkbx5.eot');
  176. src: url('//at.alicdn.com/t/font_2137302_tcl4wkbx5.eot?#iefix') format('embedded-opentype'),
  177. url('//at.alicdn.com/t/font_2137302_tcl4wkbx5.woff2') format('woff2'),
  178. url('//at.alicdn.com/t/font_2137302_tcl4wkbx5.woff') format('woff'),
  179. url('//at.alicdn.com/t/font_2137302_tcl4wkbx5.ttf') format('truetype'),
  180. url('//at.alicdn.com/t/font_2137302_tcl4wkbx5.svg#icon') format('svg');
  181. }
  182. .xTab-icon {
  183. font-family: "xTab-icon" !important;
  184. font-size: 30rpx;
  185. font-style: normal;
  186. -webkit-font-smoothing: antialiased;
  187. -webkit-text-stroke-width: 0.2px;
  188. -moz-osx-font-smoothing: grayscale;
  189. }
  190. .scroll-view {
  191. width: 100%;
  192. white-space: nowrap;
  193. position: relative;
  194. transition: all .5s;
  195. }
  196. .scroll-view>view {
  197. width: 100%;
  198. height: 100%;
  199. }
  200. .view-content {
  201. display: inline-block;
  202. }
  203. .txt {
  204. transition: color .3s;
  205. }
  206. .view-content>text {
  207. position: absolute;
  208. bottom: -10rpx;
  209. left: 15%;
  210. font-size: 28rpx;
  211. color: white;
  212. }
  213. .icon_act {
  214. position: absolute;
  215. font-size: 28rpx;
  216. color: white;
  217. transition: all .4s;
  218. }
  219. .underline {
  220. position: absolute;
  221. bottom: 0;
  222. height: 4rpx;
  223. transition: all .4s;
  224. }
  225. .adaptation {
  226. display: flex;
  227. justify-content: space-between;
  228. }
  229. </style>