u-sidebar-item.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <template>
  2. <view
  3. class="u-sidebar-item"
  4. :class="[`u-sidebar-item-${index}`]"
  5. :style="[itemStyle]"
  6. @click="handleClick"
  7. hover-class="none"
  8. >
  9. <view class="u-sidebar-item__content" :class="[`u-sidebar-item__icon-${iconPosition}`]">
  10. <slot>
  11. <u-icon
  12. v-if="icon"
  13. :name="icon"
  14. :size="iconSize"
  15. :color="iconColor"
  16. class="u-sidebar-item__icon"
  17. ></u-icon>
  18. <text class="u-sidebar-item__text" >
  19. {{ label }}
  20. </text>
  21. <u-badge
  22. :isDot="propsBadge.isDot"
  23. :value="propsBadge.value"
  24. :type="propsBadge.type"
  25. :max="propsBadge.max"
  26. :showZero="propsBadge.showZero"
  27. :bgColor="propsBadge.bgColor"
  28. :color="propsBadge.color"
  29. :shape="propsBadge.shape"
  30. :numberType="propsBadge.numberType"
  31. :inverted="propsBadge.inverted"
  32. :customStyle="{
  33. position: 'absolute',
  34. right: '0',
  35. top: '0',
  36. transform: 'translate(50%, -50%)'
  37. }"
  38. ></u-badge>
  39. </slot>
  40. </view>
  41. <view
  42. v-if="active && parentData.showLine"
  43. class="u-sidebar-item__line"
  44. :style="[lineStyle]"
  45. ></view>
  46. </view>
  47. </template>
  48. <script>
  49. import props from './props.js';
  50. import mixin from '../../libs/mixin/mixin'
  51. import mpMixin from '../../libs/mixin/mpMixin';
  52. /**
  53. * SidebarItem 侧边栏项
  54. * @description 侧边栏的子组件,用于定义侧边栏的每一项
  55. * @tutorial https://uview.d3u.cn/components/sidebar.html
  56. *
  57. * @property {String} label 内容
  58. * @property {String | Number} value 值,用于标识该项
  59. * @property {String} icon 图标名称
  60. * @property {String | Number} iconSize 图标大小
  61. * @property {String} iconColor 图标颜色
  62. * @property {String} iconPosition 图标位置,支持 left 和 top
  63. * @property {String | Number | Object} badge 右上角的角标提示信息,支持字符串、数字、对象类型
  64. * @property {Boolean} dot 是否显示圆点,将会覆盖badge参数
  65. * @property {Boolean} disabled 是否禁用该项
  66. *
  67. * @example
  68. * <u-sidebar-item
  69. * label="标签名称"
  70. * icon="home"
  71. * iconPosition="top"
  72. * :badge="{ isDot: true, value: '5', type: 'error' }"
  73. * />
  74. */
  75. export default {
  76. name: 'u-sidebar-item',
  77. mixins: [mpMixin, mixin, props],
  78. data() {
  79. return {
  80. index: 0,
  81. parent: false, // 父组件实例
  82. propsBadge: {
  83. isDot: false,
  84. value: '',
  85. type: 'error',
  86. max: 99,
  87. showZero: false,
  88. bgColor: '',
  89. color: '',
  90. shape: 'circle',
  91. numberType: 'number',
  92. inverted: false,
  93. },
  94. parentData: {
  95. innerCurrent: 0,
  96. width: '100px',
  97. fontSize: '14px',
  98. lineHeight: '22px',
  99. textColor: '',
  100. disabledColor: '',
  101. disabledBgColor: '',
  102. bgColor: '',
  103. activeColor: '',
  104. activeBgColor: 'white',
  105. activeBold: true,
  106. activeStyle: {},
  107. inactiveStyle: {},
  108. lineWidth: '4px',
  109. lineHeight: '16px',
  110. lineColor: '#1677ff',
  111. lineBgSize: 'cover',
  112. showLine: true,
  113. }
  114. }
  115. },
  116. watch: {
  117. 'parentData'(newValue, oldValue) {
  118. // 父组件数据变化时的处理
  119. },
  120. badge: {
  121. immediate: true,
  122. handler(newVal) {
  123. if (!uni.$u.test.object(this.badge)) {
  124. this.propsBadge.value = this.badge;
  125. }
  126. }
  127. }
  128. },
  129. created() {
  130. // 父组件的实例
  131. this.parent = false;
  132. },
  133. computed: {
  134. active() {
  135. return this.index === this.parentData.innerCurrent
  136. },
  137. itemStyle() {
  138. const style = {}
  139. if (this.disabled) {
  140. // 禁用状态样式
  141. if (this.parentData.disabledColor) style.color = this.parentData.disabledColor
  142. if (this.parentData.disabledBgColor) style.backgroundColor = this.parentData.disabledBgColor
  143. } else if (this.active) {
  144. // 激活状态样式
  145. if (this.parentData.activeBold) style.fontWeight = 'bold'
  146. if (this.parentData.activeColor) style.color = this.parentData.activeColor
  147. if (this.parentData.activeBgColor) style.backgroundColor = this.parentData.activeBgColor
  148. return uni.$u.deepMerge(style, uni.$u.addStyle(this.parentData.activeStyle));
  149. } else {
  150. // 非激活状态样式
  151. if (this.parentData.textColor) style.color = this.parentData.textColor
  152. return uni.$u.deepMerge(style, uni.$u.addStyle(this.parentData.inactiveStyle));
  153. }
  154. return style
  155. },
  156. lineStyle() {
  157. const style = {}
  158. style.width = this.$u.addUnit(this.parentData.lineWidth)
  159. style.height = this.$u.addUnit(this.parentData.lineHeight )
  160. style.backgroundColor = this.parentData.lineColor
  161. if (this.parentData.lineBgSize) {
  162. style.backgroundSize = this.parentData.lineBgSize
  163. }
  164. return style
  165. }
  166. },
  167. mounted() {
  168. this.init();
  169. },
  170. methods: {
  171. init() {
  172. // 获取父组件u-sidebar
  173. let parent = this.$u.$parent.call(this, 'u-sidebar');
  174. if (parent) {
  175. this.parent = parent;
  176. // 将本组件的this,放入到父组件的children数组中,让父组件可以操作本(子)组件的方法和属性
  177. // push进去前,显判断是否已经存在了本实例,因为在子组件内部数据变化时,会通过父组件重新初始化子组件
  178. let exist = parent.children.find(val => {
  179. return this === val;
  180. })
  181. if (!exist) parent.children.push(this);
  182. // 更新父组件数据
  183. this.updateParentData();
  184. // 设置当前项的索引
  185. this.index = parent.children.indexOf(this);
  186. } else {
  187. uni.$u.error('u-sidebar-item必须要搭配u-sidebar组件使用');
  188. }
  189. },
  190. updateParentData() {
  191. // 此方法在mixin中
  192. this.getParentData('u-sidebar');
  193. },
  194. // 父组件数据发生变化
  195. updateFromParent() {
  196. this.init();
  197. },
  198. handleClick() {
  199. if (this.disabled) return
  200. // 优先使用value,如果没有value则使用index
  201. const clickValue = this.value !== '' ? this.value : this.index;
  202. // 通知父组件,参考u-dropdown-item的实现方式
  203. if (this.parent && this.parent.handleItemClick) {
  204. this.parent.handleItemClick(this.index);
  205. }
  206. }
  207. }
  208. }
  209. </script>
  210. <style lang="scss" scoped>
  211. .u-sidebar-item {
  212. position: relative;
  213. padding: 20px 10px;
  214. cursor: pointer;
  215. -webkit-tap-highlight-color: transparent;
  216. user-select: none;
  217. &__content {
  218. position: relative;
  219. display: flex;
  220. align-items: center;
  221. justify-content: center;
  222. }
  223. &__icon {
  224. flex-shrink: 0;
  225. }
  226. &__icon-top {
  227. flex-direction: column;
  228. }
  229. &__icon-left {
  230. flex-direction: row;
  231. }
  232. &__text {
  233. text-align: center;
  234. }
  235. &__line {
  236. position: absolute;
  237. left: 0;
  238. top: 50%;
  239. transform: translateY(-50%);
  240. width: 4px;
  241. height: 16px;
  242. border-radius: 2px;
  243. }
  244. }
  245. </style>