tn-switch.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <template>
  2. <view
  3. class="tn-switch-class tn-switch"
  4. :class="[
  5. value ? 'tn-switch--on' : '',
  6. disabled ? 'tn-switch--disabled' : '',
  7. `tn-switch--${shape}`
  8. ]"
  9. :style="[switchStyle]"
  10. @tap="click"
  11. >
  12. <view
  13. class="tn-switch__node"
  14. :class="[`tn-switch__node--${shape}`]"
  15. :style="[switchNodeStyle]"
  16. >
  17. <tn-loading class="tn-switch__node__loading" :show="loading" mode="flower" :size="size * 0.6" :color="loadingColor"></tn-loading>
  18. </view>
  19. <!-- 左图标 -->
  20. <view
  21. v-if="leftIcon !== ''"
  22. class="tn-switch__icon tn-switch__icon--left"
  23. :class="[
  24. `tn-icon-${leftIcon}`,
  25. value ? 'tn-switch__icon--show' : ''
  26. ]"
  27. :style="[iconStyle]"></view>
  28. <!-- 右图标 -->
  29. <view
  30. v-if="rightIcon !== ''"
  31. class="tn-switch__icon tn-switch__icon--right"
  32. :class="[
  33. `tn-icon-${rightIcon}`,
  34. !value ? 'tn-switch__icon--show' : ''
  35. ]"
  36. :style="[iconStyle]"></view>
  37. </view>
  38. </template>
  39. <script>
  40. export default {
  41. name: 'tn-switch',
  42. props: {
  43. value: {
  44. type: Boolean,
  45. default: false
  46. },
  47. // 按钮的样式
  48. // circle 圆角 square 方形
  49. shape: {
  50. type: String,
  51. default: 'circle'
  52. },
  53. // 是否禁用
  54. disabled: {
  55. type: Boolean,
  56. default: false
  57. },
  58. // 尺寸
  59. size: {
  60. type: Number,
  61. default: 50
  62. },
  63. // 打开时的背景颜色
  64. activeColor: {
  65. type: String,
  66. default: ''
  67. },
  68. // 关闭时的背景颜色
  69. inactiveColor: {
  70. type: String,
  71. default: ''
  72. },
  73. // 激活时的值
  74. activeValue: {
  75. type: [Number, String, Boolean],
  76. default: true
  77. },
  78. // 关闭时的值
  79. inactiveValue: {
  80. type: [Number, String, Boolean],
  81. default: false
  82. },
  83. // 左图标
  84. leftIcon: {
  85. type: String,
  86. default: ''
  87. },
  88. // 右图标
  89. rightIcon: {
  90. type: String,
  91. default: ''
  92. },
  93. // 是否为加载状态
  94. loading: {
  95. type: Boolean,
  96. default: false
  97. },
  98. // 点击手机是否震动
  99. vibrateShort: {
  100. type: Boolean,
  101. default: false
  102. }
  103. },
  104. computed: {
  105. switchStyle() {
  106. let style = {}
  107. style.fontSize = this.$t.string.getLengthUnitValue(this.size)
  108. style.backgroundColor = this.value ?
  109. this.activeColor ? this.activeColor : '#01BEFF' :
  110. this.inactiveColor ? this.inactiveColor : '#AAAAAA'
  111. return style
  112. },
  113. switchNodeStyle() {
  114. let style = {}
  115. style.width = this.$t.string.getLengthUnitValue(this.size)
  116. style.height = style.width
  117. return style
  118. },
  119. iconStyle() {
  120. let style = {}
  121. style.fontSize = this.$t.string.getLengthUnitValue(this.size - 20)
  122. style.lineHeight = this.$t.string.getLengthUnitValue(this.size)
  123. return style
  124. },
  125. loadingColor() {
  126. return this.value ? this.activeColor : ''
  127. }
  128. },
  129. data() {
  130. return {
  131. }
  132. },
  133. methods: {
  134. click() {
  135. if (!this.disabled && !this.loading) {
  136. if (this.vibrateShort) uni.vibrateShort()
  137. this.$emit('input', !this.value)
  138. // 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
  139. this.$nextTick(() => {
  140. this.$emit('change', this.value ? this.activeValue : this.inactiveValue);
  141. })
  142. }
  143. }
  144. }
  145. }
  146. </script>
  147. <style lang="scss" scoped>
  148. .tn-switch {
  149. /* #ifndef APP-NVUE */
  150. display: inline-block;
  151. /* #endif */
  152. position: relative;
  153. box-sizing: initial;
  154. width: 2em;
  155. height: 1em;
  156. font-size: 50rpx;
  157. background-color: #AAAAAA;
  158. transition: background-color 0.3s;
  159. &__node {
  160. display: flex;
  161. flex-direction: row;
  162. align-items: center;
  163. justify-content: center;
  164. position: absolute;
  165. top: 0;
  166. left: 0;
  167. z-index: 1;
  168. background-color: #FFFFFF;
  169. transform: scale(0.9);
  170. box-shadow: 0 6rpx 2rpx 0 rgba(0, 0, 0, 0.05), 0 4rpx 4rpx 0 rgba(0, 0, 0, 0.1), 0 6rpx 6rpx 0 rgba(0, 0, 0, 0.05);
  171. transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
  172. -webkit-transition: transform 0.3s cubic-bezier(0.3, 1.05, 0.4, 1.05);
  173. &__loading {
  174. display: flex;
  175. flex-direction: row;
  176. align-items: center;
  177. justify-content: center;
  178. }
  179. &--circle {
  180. border-radius: 100%;
  181. }
  182. &--square {
  183. border-radius: 15%;
  184. }
  185. }
  186. &__icon {
  187. color: #FFFFFF;
  188. font-size: 30rpx;
  189. line-height: 50rpx;
  190. height: 100%;
  191. vertical-align: middle;
  192. position: absolute;
  193. transform: scale(0);
  194. transform-origin: 50% 50%;
  195. transition: transform 0.3s ease-in-out;
  196. &--left {
  197. top: 0;
  198. left: 10rpx;
  199. }
  200. &--right {
  201. top: 0;
  202. right: 10rpx;
  203. }
  204. &--show {
  205. transform: scale(1);
  206. }
  207. }
  208. &--circle {
  209. border-radius: 1em;
  210. }
  211. &--square {
  212. border-radius: 0.1em;
  213. }
  214. &--on {
  215. background-color: $tn-main-color;
  216. .tn-switch__node {
  217. transform: translateX(100%) scale(0.9);
  218. }
  219. }
  220. &--disabled {
  221. opacity: 0.4;
  222. }
  223. }
  224. </style>