u-switch.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <template>
  2. <view class="u-switch" :class="[getValue() == true ? 'u-switch--on' : '', disabled ? 'u-switch--disabled' : '']" @tap="onClick"
  3. :style="[switchStyle]">
  4. <view class="u-switch__node node-class" :style="{
  5. width: $u.addUnit(size),
  6. height: $u.addUnit(size)
  7. }">
  8. <u-loading :show="loading" class="u-switch__loading" :size="size * 0.6" :color="loadingColor" />
  9. </view>
  10. </view>
  11. </template>
  12. <script>
  13. /**
  14. * switch 开关选择器
  15. * @description 选择开关一般用于只有两个选择,且只能选其一的场景。
  16. * @tutorial https://www.uviewui.com/components/switch.html
  17. * @property {Boolean} loading 是否处于加载中(默认false)
  18. * @property {Boolean} disabled 是否禁用(默认false)
  19. * @property {String Number} size 开关尺寸,单位rpx(默认50)
  20. * @property {String} active-color 打开时的背景色(默认#2979ff)
  21. * @property {Boolean} inactive-color 关闭时的背景色(默认#ffffff)
  22. * @property {Boolean | Number | String} active-value 打开选择器时通过change事件发出的值(默认true)
  23. * @property {Boolean | Number | String} inactive-value 关闭选择器时通过change事件发出的值(默认false)
  24. * @event {Function} change 在switch打开或关闭时触发
  25. * @example <u-switch v-model="checked" active-color="red" inactive-color="#eee"></u-switch>
  26. */
  27. export default {
  28. name: "u-switch",
  29. emits: ["update:modelValue", "input", "change"],
  30. props: {
  31. // 通过v-model双向绑定的值
  32. value: {
  33. type: Boolean,
  34. default: false
  35. },
  36. modelValue: {
  37. type: Boolean,
  38. default: false
  39. },
  40. // 是否为加载中状态
  41. loading: {
  42. type: Boolean,
  43. default: false
  44. },
  45. // 是否为禁用装填
  46. disabled: {
  47. type: Boolean,
  48. default: false
  49. },
  50. // 开关尺寸,单位rpx
  51. size: {
  52. type: [Number, String],
  53. default: 50
  54. },
  55. // 打开时的背景颜色
  56. activeColor: {
  57. type: String,
  58. default: '#2979ff'
  59. },
  60. // 关闭时的背景颜色
  61. inactiveColor: {
  62. type: String,
  63. default: '#ffffff'
  64. },
  65. // 是否使手机发生短促震动,目前只在iOS的微信小程序有效(2020-05-06)
  66. vibrateShort: {
  67. type: Boolean,
  68. default: false
  69. },
  70. // 打开选择器时的值
  71. activeValue: {
  72. type: [Number, String, Boolean],
  73. default: true
  74. },
  75. // 关闭选择器时的值
  76. inactiveValue: {
  77. type: [Number, String, Boolean],
  78. default: false
  79. },
  80. },
  81. data() {
  82. return {
  83. }
  84. },
  85. computed: {
  86. switchStyle() {
  87. let style = {};
  88. style.fontSize = this.size + 'rpx';
  89. style.backgroundColor = this.getValue() ? this.activeColor : this.inactiveColor;
  90. return style;
  91. },
  92. loadingColor() {
  93. return this.getValue() ? this.activeColor : null;
  94. }
  95. },
  96. methods: {
  97. getValue(){
  98. // #ifndef VUE3
  99. return this.value;
  100. // #endif
  101. // #ifdef VUE3
  102. return this.modelValue;
  103. // #endif
  104. },
  105. onClick() {
  106. if (!this.disabled && !this.loading) {
  107. // 使手机产生短促震动,微信小程序有效,APP(HX 2.6.8)和H5无效
  108. if(this.vibrateShort) uni.vibrateShort();
  109. this.$emit('input', !this.getValue());
  110. this.$emit("update:modelValue", !this.getValue());
  111. // 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
  112. this.$nextTick(() => {
  113. this.$emit('change', this.getValue() ? this.activeValue : this.inactiveValue);
  114. })
  115. }
  116. }
  117. }
  118. };
  119. </script>
  120. <style lang="scss" scoped>
  121. @import "../../libs/css/style.components.scss";
  122. .u-switch {
  123. position: relative;
  124. /* #ifndef APP-NVUE */
  125. display: inline-block;
  126. /* #endif */
  127. box-sizing: initial;
  128. width: 2em;
  129. height: 1em;
  130. background-color: #fff;
  131. border: 1px solid rgba(0, 0, 0, 0.1);
  132. border-radius: 1em;
  133. transition: background-color 0.3s;
  134. font-size: 50rpx;
  135. }
  136. .u-switch__node {
  137. @include vue-flex;
  138. align-items: center;
  139. justify-content: center;
  140. position: absolute;
  141. top: 0;
  142. left: 0;
  143. border-radius: 100%;
  144. z-index: 1;
  145. background-color: #fff;
  146. background-color: #fff;
  147. box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05);
  148. box-shadow: 0 3px 1px 0 rgba(0, 0, 0, 0.05), 0 2px 2px 0 rgba(0, 0, 0, 0.1), 0 3px 3px 0 rgba(0, 0, 0, 0.05);
  149. transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
  150. transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05), -webkit-transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
  151. transition: transform cubic-bezier(0.3, 1.05, 0.4, 1.05);
  152. transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05)
  153. }
  154. .u-switch__loading {
  155. @include vue-flex;
  156. align-items: center;
  157. justify-content: center;
  158. }
  159. .u-switch--on {
  160. background-color: #1989fa;
  161. }
  162. .u-switch--on .u-switch__node {
  163. transform: translateX(100%);
  164. }
  165. .u-switch--disabled {
  166. opacity: 0.4;
  167. }
  168. </style>