审查视图

node_modules/uview-plus/components/u-radio/u-radio.vue 11.3 KB
韩昌 authored
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 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
<template>
	<view
	    class="u-radio"
		@tap.stop="wrapperClickHandler"
	    :style="[radioStyle]"
	    :class="[`u-radio-label--${parentData.iconPlacement}`, parentData.borderBottom && parentData.placement === 'column' && 'u-border-bottom']"
	>
		<view
		    class="u-radio__icon-wrap"
		    @tap.stop="iconClickHandler"
		    :class="iconClasses"
		    :style="[iconWrapStyle]"
		>
			<slot name="icon">
				<u-icon
				    class="u-radio__icon-wrap__icon"
				    name="checkbox-mark"
				    :size="elIconSize"
				    :color="elIconColor"
				/>
			</slot>
		</view>
		<text
			class="u-radio__text"
		    @tap.stop="labelClickHandler"
		    :style="{
				color: elDisabled ? elInactiveColor : elLabelColor,
				fontSize: elLabelSize,
				lineHeight: elLabelSize
			}"
		>{{label}}</text>
	</view>
</template>

<script>
	import props from './props.js';
	import mpMixin from '../../libs/mixin/mpMixin.js';
	import mixin from '../../libs/mixin/mixin.js';
	/**
	 * radio 单选框
	 * @description 单选框用于有一个选择,用户只能选择其中一个的场景。搭配u-radio-group使用
	 * @tutorial https://ijry.github.io/uview-plus/components/radio.html
	 * @property {String | Number}	name			radio的名称
	 * @property {String}			shape			形状,square为方形,circle为圆型
	 * @property {Boolean}			disabled		是否禁用
	 * @property {String | Boolean}	labelDisabled	是否禁止点击提示语选中单选框
	 * @property {String}			activeColor		选中时的颜色,如设置parent的active-color将失效
	 * @property {String}			inactiveColor	未选中的颜色
	 * @property {String | Number}	iconSize		图标大小,单位px
	 * @property {String | Number}	labelSize		label字体大小,单位px
	 * @property {String | Number}	label			label提示文字,因为nvue下,直接slot进来的文字,由于特殊的结构,无法修改样式
	 * @property {String | Number}	size			整体的大小
	 * @property {String}			iconColor		图标颜色
	 * @property {String}			labelColor		label的颜色
	 * @property {Object}			customStyle		组件的样式,对象形式
	 * 
	 * @event {Function} change 某个radio状态发生变化时触发(选中状态)
	 * @example <u-radio :labelDisabled="false">门掩黄昏,无计留春住</u-radio>
	 */
	export default {
		name: "u-radio",
		
		mixins: [mpMixin, mixin,props],
		data() {
			return {
				checked: false,
				// 当你看到这段代码的时候,
				// 父组件的默认值,因为头条小程序不支持在computed中使用this.parent.shape的形式
				// 故只能使用如此方法
				parentData: {
					iconSize: 12,
					labelDisabled: null,
					disabled: null,
					shape: null,
					activeColor: null,
					inactiveColor: null,
					size: 18,
					value: null,
					modelValue: null,
					iconColor: null,
					placement: 'row',
					borderBottom: false,
					iconPlacement: 'left'
				}
			}
		},
		computed: {
			// 是否禁用,如果父组件u-raios-group禁用的话,将会忽略子组件的配置
			elDisabled() {
				return this.disabled !== '' ? this.disabled : this.parentData.disabled !== null ? this.parentData.disabled : false;
			},
			// 是否禁用label点击
			elLabelDisabled() {
				return this.labelDisabled !== '' ? this.labelDisabled : this.parentData.labelDisabled !== null ? this.parentData.labelDisabled :
					false;
			},
			// 组件尺寸,对应size的值,默认值为21px
			elSize() {
				return this.size ? this.size : (this.parentData.size ? this.parentData.size : 21);
			},
			// 组件的勾选图标的尺寸,默认12px
			elIconSize() {
				return this.iconSize ? this.iconSize : (this.parentData.iconSize ? this.parentData.iconSize : 12);
			},
			// 组件选中激活时的颜色
			elActiveColor() {
				return this.activeColor ? this.activeColor : (this.parentData.activeColor ? this.parentData.activeColor : '#2979ff');
			},
			// 组件选未中激活时的颜色
			elInactiveColor() {
				return this.inactiveColor ? this.inactiveColor : (this.parentData.inactiveColor ? this.parentData.inactiveColor :
					'#c8c9cc');
			},
			// label的颜色
			elLabelColor() {
				return this.labelColor ? this.labelColor : (this.parentData.labelColor ? this.parentData.labelColor : '#606266')
			},
			// 组件的形状
			elShape() {
				return this.shape ? this.shape : (this.parentData.shape ? this.parentData.shape : 'circle');
			},
			// label大小
			elLabelSize() {
				return uni.$u.addUnit(this.labelSize ? this.labelSize : (this.parentData.labelSize ? this.parentData.labelSize :
					'15'))
			},
			elIconColor() {
				const iconColor = this.iconColor ? this.iconColor : (this.parentData.iconColor ? this.parentData.iconColor :
					'#ffffff');
				// 图标的颜色
				if (this.elDisabled) {
					// disabled状态下,已勾选的radio图标改为elInactiveColor
					return this.checked ? this.elInactiveColor : 'transparent'
				} else {
					return this.checked ? iconColor : 'transparent'
				}
			},
			iconClasses() {
				let classes = []
				// 组件的形状
				classes.push('u-radio__icon-wrap--' + this.elShape)
				if (this.elDisabled) {
					classes.push('u-radio__icon-wrap--disabled')
				}
				if (this.checked && this.elDisabled) {
					classes.push('u-radio__icon-wrap--disabled--checked')
				}
				// 支付宝,头条小程序无法动态绑定一个数组类名,否则解析出来的结果会带有",",而导致失效
				// #ifdef MP-ALIPAY || MP-TOUTIAO
				classes = classes.join(' ')
				// #endif
				return classes
			},
			iconWrapStyle() {
				// radio的整体样式
				const style = {}
				style.backgroundColor = this.checked && !this.elDisabled ? this.elActiveColor : '#ffffff'
				style.borderColor = this.checked && !this.elDisabled ? this.elActiveColor : this.elInactiveColor
				style.width = uni.$u.addUnit(this.elSize)
				style.height = uni.$u.addUnit(this.elSize)
				// 如果是图标在右边的话,移除它的右边距
				if (this.parentData.iconPlacement === 'right') {
					style.marginRight = 0
				}
				return style
			},
			radioStyle() {
				const style = {}
				if(this.parentData.borderBottom && this.parentData.placement === 'row') {
					uni.$u.error('检测到您将borderBottom设置为true,需要同时将u-radio-groupplacement设置为column才有效')
				}
				// 当父组件设置了显示下边框并且排列形式为纵向时,给内容和边框之间加上一定间隔
				if(this.parentData.borderBottom && this.parentData.placement === 'column') {
					// ios像素密度高,需要多一点的距离
					style.paddingBottom = uni.$u.os() === 'ios' ? '12px' : '8px'
				}
				return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle))
			}
		},
		mounted() {
			this.init()
		},

		methods: {
			init() {
				// 支付宝小程序不支持provide/inject,所以使用这个方法获取整个父组件,在created定义,避免循环引用
				this.updateParentData()
				if (!this.parent) {
					uni.$u.error('u-radio必须搭配u-radio-group组件使用')
				}
				// 设置初始化时,是否默认选中的状态
				// #ifdef VUE3
				this.checked = this.name === this.parentData.modelValue
				// #endif
        		// #ifdef VUE2
				this.checked = this.name === this.parentData.value
				// #endif
			},
			updateParentData() {
				this.getParentData('u-radio-group')
			},
			// 点击图标
			iconClickHandler(e) {
				this.preventEvent(e)
				// 如果整体被禁用,不允许被点击
				if (!this.elDisabled) {
					this.setRadioCheckedStatus()
				}
			},
			// 横向两端排列时,点击组件即可触发选中事件
			wrapperClickHandler(e) {
				this.parentData.iconPlacement === 'right' && this.iconClickHandler(e)
			},
			// 点击label
			labelClickHandler(e) {
				this.preventEvent(e)
				// 如果按钮整体被禁用或者label被禁用,则不允许点击文字修改状态
				if (!this.elLabelDisabled && !this.elDisabled) {
					this.setRadioCheckedStatus()
				}
			},
			emitEvent() {
				// u-radio的checked不为true时(意味着未选中),才发出事件,避免多次点击触发事件
				if (!this.checked) {
					this.$emit('change', this.name)
					// 尝试调用u-form的验证方法,进行一定延迟,否则微信小程序更新可能会不及时
					this.$nextTick(() => {
						uni.$u.formValidate(this, 'change')
					})
				}
			},
			// 改变组件选中状态
			// 这里的改变的依据是,更改本组件的checked值为true,同时通过父组件遍历所有u-radio实例
			// 将本组件外的其他u-radio的checked都设置为false(都被取消选中状态),因而只剩下一个为选中状态
			setRadioCheckedStatus() {
				this.emitEvent()
				// 将本组件标记为选中状态
				this.checked = true
				typeof this.parent.unCheckedOther === 'function' && this.parent.unCheckedOther(this)
			}
		}
	}
</script>

<style lang="scss" scoped>
	@import "../../libs/css/components.scss";
	$u-radio-wrap-margin-right:6px !default;
	$u-radio-wrap-font-size:20px !default;
	$u-radio-wrap-border-width:1px !default;
	$u-radio-wrap-border-color: #c8c9cc !default;
	$u-radio-line-height:0 !default;
	$u-radio-circle-border-radius:100% !default;
	$u-radio-square-border-radius:3px !default;
	$u-radio-checked-color:#fff !default;
	$u-radio-checked-background-color:red !default;
	$u-radio-checked-border-color: #2979ff !default;
	$u-radio-disabled-background-color:#ebedf0 !default;
	$u-radio-disabled--checked-color:#c8c9cc !default;
	$u-radio-label-margin-left: 5px !default;
	$u-radio-label-margin-right:12px !default;
	$u-radio-label-color:$u-content-color !default;
	$u-radio-label-font-size:15px !default;
	$u-radio-label-disabled-color:#c8c9cc !default;
	
	.u-radio {
		/* #ifndef APP-NVUE */
		@include flex(row);
		/* #endif */
		overflow: hidden;
		flex-direction: row;
		align-items: center;
		margin-bottom: 5px;
		margin-top: 5px;
		
		&-label--left {
			flex-direction: row
		}

		&-label--right {
			flex-direction: row-reverse;
			justify-content: space-between
		}

		&__icon-wrap {
			/* #ifndef APP-NVUE */
			box-sizing: border-box;
			// nvue下,border-color过渡有问题
			transition-property: border-color, background-color, color;
			transition-duration: 0.2s;
			/* #endif */
			color: $u-content-color;
			@include flex;
			align-items: center;
			justify-content: center;
			color: transparent;
			text-align: center;
			margin-right: $u-radio-wrap-margin-right;
			font-size: $u-radio-wrap-font-size;
			border-width: $u-radio-wrap-border-width;
			border-color: $u-radio-wrap-border-color;
			border-style: solid;

			/* #ifdef MP-TOUTIAO */
			// 头条小程序兼容性问题,需要设置行高为0,否则图标偏下
			&__icon {
				line-height: $u-radio-line-height;
			}

			/* #endif */

			&--circle {
				border-radius: $u-radio-circle-border-radius;
			}

			&--square {
				border-radius: $u-radio-square-border-radius;
			}

			&--checked {
				color: $u-radio-checked-color;
				background-color: $u-radio-checked-background-color;
				border-color: $u-radio-checked-border-color;
			}

			&--disabled {
				background-color: $u-radio-disabled-background-color !important;
			}

			&--disabled--checked {
				color: $u-radio-disabled--checked-color !important;
			}
		}

		&__label {
			/* #ifndef APP-NVUE */
			word-wrap: break-word;
			/* #endif */
			margin-left: $u-radio-label-margin-left;
			margin-right: $u-radio-label-margin-right;
			color: $u-radio-label-color;
			font-size: $u-radio-label-font-size;

			&--disabled {
				color: $u-radio-label-disabled-color;
			}
		}
	}
</style>