Blame view

uni_modules/uview-plus/components/u-switch/u-switch.vue 6.91 KB
a2702f6d   刘淇   巡查计划
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  <template>
  	<view
  	    class="u-switch cursor-pointer"
  	    :class="[disabled && 'u-switch--disabled']"
  	    :style="[switchStyle, addStyle(customStyle)]"
  	    @tap="clickHandler"
  	>
  		<view
  		    class="u-switch__bg"
  		    :style="[bgStyle]"
  		>
  		</view>
  		<view
  		    class="u-switch__node"
  		    <!-- #ifdef VUE3 -->
  			:class="[modelValue && 'u-switch__node--on']"
  			<!-- #endif -->
  			<!-- #ifdef VUE2 -->
  			:class="[value && 'u-switch__node--on']"
  			<!-- #endif -->
  		    :style="[nodeStyle]"
  		    ref="u-switch__node"
  		>
  			<u-loading-icon
  			    :show="loading"
  			    mode="circle"
  			    timingFunction='linear'
  			    <!-- #ifdef VUE3 -->
  				:color="modelValue ? activeColor : '#AAABAD'"
  				<!-- #endif -->
  				<!-- #ifdef VUE2 -->
  				:color="value ? activeColor : '#AAABAD'"
  				<!-- #endif -->
  			    :size="size * 0.6"
  			/>
  		</view>
  	</view>
  </template>
  
  <script>
  	import { props } from './props';
  	import { mpMixin } from '../../libs/mixin/mpMixin';
  	import { mixin } from '../../libs/mixin/mixin';
  	import { addStyle, addUnit, error } from '../../libs/function/index';
  	/**
  	 * switch 开关选择器
  	 * @description 选择开关一般用于只有两个选择,且只能选其一的场景。
  	 * @tutorial https://uview-plus.jiangruyi.com/components/switch.html
  	 * @property {Boolean}						loading			是否处于加载中(默认 false 
  	 * @property {Boolean}						disabled		是否禁用(默认 false 
  	 * @property {String | Number}				size			开关尺寸,单位px (默认 25 
  	 * @property {String}						activeColor		打开时的背景色 (默认 '#2979ff' 
  	 * @property {String} 						inactiveColor	关闭时的背景色 (默认 '#ffffff' 
  	 * @property {Boolean | String | Number}	value			通过v-model双向绑定的值 (默认 false 
  	 * @property {Boolean | String | Number}	activeValue		打开选择器时通过change事件发出的值 (默认 true 
  	 * @property {Boolean | String | Number}	inactiveValue	关闭选择器时通过change事件发出的值 (默认 false 
  	 * @property {Boolean}						asyncChange		是否开启异步变更,开启后需要手动控制输入值 (默认 false 
  	 * @property {String | Number}				space			圆点与外边框的距离 (默认 0 
  	 * @property {Object}						customStyle		定义需要用到的外部样式
  	 *
  	 * @event {Function} change 在switch打开或关闭时触发
  	 * @example <u-switch v-model="checked" active-color="red" inactive-color="#eee"></u-switch>
  	 */
  	export default {
  		name: "u-switch",
  		mixins: [mpMixin, mixin,props],
  		watch: {
  			// #ifdef VUE3
  			modelValue: {
  				immediate: true,
  				handler(n) {
  					if(n !== this.inactiveValue && n !== this.activeValue) {
  						error('v-model绑定的值必须为inactiveValue、activeValue二者之一')
  					}
  				}
  			},
  			// #endif
          	// #ifdef VUE2
  			value: {
  				immediate: true,
  				handler(n) {
  					if(n !== this.inactiveValue && n !== this.activeValue) {
  						error('v-model绑定的值必须为inactiveValue、activeValue二者之一')
  					}
  				}
  			}
  			// #endif
  		},
  		data() {
  			return {
  				bgColor: '#ffffff'
  			}
  		},
  		computed: {
  			isActive(){
  				// #ifdef VUE3
  				return this.modelValue === this.activeValue;
  				// #endif
          		// #ifdef VUE2
  				return this.value === this.activeValue;
  				// #endif
  			},
  			switchStyle() {
  				let style = {}
  				// 这里需要加2,是为了腾出边框的距离,否则圆点node会和外边框紧贴在一起
  				style.width = addUnit(this.size * 2 + 2)
  				style.height = addUnit(Number(this.size) + 2)
  				// style.borderColor = this.value ? 'rgba(0, 0, 0, 0)' : 'rgba(0, 0, 0, 0.12)'
  				// 如果自定义了“非激活”演示,name边框颜色设置为透明(跟非激活颜色一致)
  				// 这里不能简单的设置为非激活的颜色,否则打开状态时,会有边框,所以需要透明
  				if(this.customInactiveColor) {
  					style.borderColor = 'rgba(0, 0, 0, 0)'
  				}
  				style.backgroundColor = this.isActive ? this.activeColor : this.inactiveColor
  				return style;
  			},
  			nodeStyle() {
  				let style = {}
  				// 如果自定义非激活颜色,将node圆点的尺寸减少两个像素,让其与外边框距离更大一点
  				style.width = addUnit(this.size - this.space)
  				style.height = addUnit(this.size - this.space)
  				const translateX = this.isActive ? addUnit(this.space) : addUnit(this.size);
  				style.transform = `translateX(-${translateX})`
  				return style
  			},
  			bgStyle() {
  				let style = {}
  				// 这里配置一个多余的元素在HTML中,是为了让switch切换时,有更良好的背景色扩充体验(见实际效果)
  				style.width = addUnit(Number(this.size) * 2 - this.size / 2)
  				style.height = addUnit(this.size)
  				style.backgroundColor = this.inactiveColor
  				// 打开时,让此元素收缩,否则反之
  				style.transform = `scale(${this.isActive ? 0 : 1})`
  				return style
  			},
  			customInactiveColor() {
  				// 之所以需要判断是否自定义了“非激活”颜色,是为了让node圆点离外边框更宽一点的距离
  				return this.inactiveColor !== '#fff' && this.inactiveColor !== '#ffffff'
  			}
  		},
  		// #ifdef VUE3
  		emits: ['update:modelValue', 'change'],
      	// #endif
  		methods: {
  			addStyle,
  			clickHandler() {
  				if (!this.disabled && !this.loading) {
  					const oldValue = this.isActive ? this.inactiveValue : this.activeValue
  					if(!this.asyncChange) {
  						// #ifdef VUE3
  						this.$emit("update:modelValue", oldValue);
  						// #endif
  						// #ifdef VUE2
  						this.$emit("input", oldValue);
  						// #endif
  					}
  					// 放到下一个生命周期,因为双向绑定的value修改父组件状态需要时间,且是异步的
  					this.$nextTick(() => {
  						this.$emit('change', oldValue)
  					})
  				}
  			}
  		}
  	};
  </script>
  
  <style lang="scss" scoped>
  
  	.u-switch {
  		@include flex(row);
  		/* #ifndef APP-NVUE */
  		box-sizing: border-box;
  		/* #endif */
  		position: relative;
  		background-color: #fff;
  		border-width: 1px;
  		border-radius: 100px;
  		transition: background-color 0.4s;
  		border-color: rgba(0, 0, 0, 0.12);
  		border-style: solid;
  		justify-content: flex-end;
  		align-items: center;
  		// 由于weex为阿里逗着玩的KPI项目,导致bug奇多,这必须要写这一行,
  		// 否则在iOS上,点击页面任意地方,都会触发switch的点击事件
  		overflow: hidden;
  
  		&__node {
  			@include flex(row);
  			align-items: center;
  			justify-content: center;
  			border-radius: 100px;
  			background-color: #fff;
  			border-radius: 100px;
  			box-shadow: 1px 1px 1px 0 rgba(0, 0, 0, 0.25);
  			transition-property: transform;
  			transition-duration: 0.4s;
  			transition-timing-function: cubic-bezier(0.3, 1.05, 0.4, 1.05);
  		}
  
  		&__bg {
  			position: absolute;
  			border-radius: 100px;
  			background-color: #FFFFFF;
  			transition-property: transform;
  			transition-duration: 0.4s;
  			border-top-left-radius: 0;
  			border-bottom-left-radius: 0;
  			transition-timing-function: ease;
  		}
  
  		&--disabled {
  			opacity: 0.6;
  		}
  	}
  </style>