合同小程序前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

216 lines
8.2 KiB

<template>
<view class="l-radio" :class="classes" :style="[styles]" @click="onClick">
<slot name="radio" :checked="radioChecked" :disabled="isDisabled">
<slot name="icon" :checked="radioChecked" :disabled="isDisabled">
<view class="l-radio__icon" ref="iconRef" :class="iconClasses" :style="[iconStyle]"></view>
</slot>
<text class="l-radio__label" :class="labelClass" v-if="label != null || $slots['default'] != null">
<slot>{{label}}</slot>
</text>
</slot>
</view>
</template>
<script lang="uts" setup>
import { RadioProps } from './type';
// #ifndef APP
import type { ComputedRef } from 'vue';
type ComputedRefImpl<T> = ComputedRef<T>
// #endif
const emit = defineEmits(['change', 'update:checked'])
const props = withDefaults(defineProps<RadioProps>(), {
allowUncheck: false,
checked: false,
disabled: false,
icon: 'circle',
size: 'medium'
})
defineSlots<{
radio(props : { checked : boolean, disabled: boolean}) : any,
icon(props : { checked : boolean, disabled: boolean}) : any,
default(props : { checked : boolean, disabled: boolean}) : any,
}>()
const name = 'l-radio'
const radioGroupProps = inject<LRadioGroupComponentPublicInstance|null>('limeRadioGroupProps', null);
const radioGroupValue = inject<ComputedRefImpl<any>|null>('limeRadioGroupValue', null);
const radioGroupChange = inject<((value: any|null) => void)|null>('limeRadioGroupChange', null);
const modelValue = defineModel({type: Boolean, default: false});
const innerChecked = computed({
set(value: boolean){
modelValue.value = value;
emit('change', value)
},
get(): boolean{
return props.checked || modelValue.value
},
} as WritableComputedOptions<boolean>)
const isDisabled = computed(():boolean => (props.disabled || (radioGroupProps?.disabled ?? false)))
const groupDisabled = computed(():boolean|null => radioGroupProps?.disabled);
const radioChecked = computed(():boolean => innerChecked.value || (props.name != null && props.name == radioGroupValue?.value) || (props.value != null && props.value == radioGroupValue?.value));
const finalAllowUncheck = computed(():boolean => props.allowUncheck || (radioGroupProps?.allowUncheck ?? false));
const innerIcon = computed(():string => radioGroupProps?.icon ?? props.icon)
const innerSize = computed(():string => radioGroupProps?.size ?? props.size)
const innerIconSize = computed(():string|null => radioGroupProps?.iconSize ?? props.iconSize)
const innerFontSize = computed(():string|null => radioGroupProps?.fontSize ?? props.fontSize)
const innerCheckedColor = computed(():string|null => radioGroupProps?.checkedColor ?? props.checkedColor)
const innerIconBgColor = computed(():string => props.iconBgColor ?? radioGroupProps?.iconBgColor ?? 'white')
const innerIconBorderColor = computed(():string => props.iconBorderColor ?? radioGroupProps?.iconBorderColor ?? '#c5c5c5')
const innerIconDisabledColor = computed(():string => props.iconDisabledColor ?? radioGroupProps?.iconDisabledColor ?? '#c5c5c5')
const innerIconDisabledBgColor = computed(():string => props.iconDisabledBgColor ?? radioGroupProps?.iconDisabledBgColor ?? '#f3f3f3')
const classes = computed(():Map<string, boolean>=>{
const cls = new Map<string, boolean>();
cls.set(`${name}--disabled`, isDisabled.value)
return cls
})
const iconClasses = computed(():Map<string, boolean>=>{
const cls = new Map<string, boolean>();
// #ifdef APP
cls.set(`${name}__icon--checked`, radioChecked.value && innerCheckedColor.value == null)
cls.set(`${name}__icon--${innerIcon.value}`, true)
cls.set(`${name}__icon--disabled`, isDisabled.value)
// #endif
// #ifndef APP
cls.set(`${name}__icon--checked`, radioChecked.value)
cls.set(`${name}__icon--${innerIcon.value}`, true)
cls.set(`${name}__icon--disabled`, isDisabled.value)
// #endif
return cls
})
const labelClass = computed(():Map<string, boolean>=>{
const cls = new Map<string, boolean>();
cls.set(`${name}__label--disabled`, isDisabled.value)
return cls
})
const styles = computed(():Map<string, any>=>{
const style = new Map<string, any>();
if(radioGroupProps != null && radioGroupProps.gap != null) {
style.set(radioGroupProps.direction == 'horizontal' ? 'margin-right' : 'margin-bottom', radioGroupProps.gap!)
}
// #ifndef APP
if(innerCheckedColor.value != null) {
style.set('--l-radio-icon-checked-color', innerCheckedColor.value!)
}
if(innerIconBorderColor.value != null) {
style.set('--l-radio-icon-border-color', innerIconBorderColor.value!)
}
if(innerIconDisabledColor.value != null) {
style.set('--l-radio-icon-disabled-color', innerIconDisabledColor.value!)
}
if(innerIconDisabledBgColor.value != null) {
style.set('--l-radio-icon-disabled-bg-color', innerIconDisabledBgColor.value!)
}
if(innerFontSize.value != null) {
style.set('--l-radio-font-size', innerFontSize.value!)
}
// #endif
return style
})
const iconStyle = computed(():Map<string, any>=>{
const style = new Map<string, any>();
if(innerIconSize.value != null) {
style.set('width', innerIconSize.value!)
style.set('height', innerIconSize.value!)
// #ifndef APP
style.set('--l-radio-icon-size', innerIconSize.value!)
// #endif
}
if(innerCheckedColor.value != null) {
// #ifndef APP
style.set('--l-radio-icon-checked-color', innerCheckedColor.value!)
// #endif
// #ifdef APP
if(radioChecked.value && ['dot', 'circle'].includes(innerIcon.value)) {
style.set('background', innerCheckedColor.value!)
style.set('border-color', innerCheckedColor.value!)
}
if(!isDisabled.value && !radioChecked.value && ['dot', 'circle'].includes(innerIcon.value)) {
style.set('background', innerIconBgColor.value)
style.set('border-color', innerIconBorderColor.value)
}
if(isDisabled.value && ['dot', 'circle'].includes(innerIcon.value)) {
style.set('background', innerIconDisabledBgColor.value)
style.set('border-color', innerIconDisabledColor.value)
}
// #endif
}
return style
})
const labelStyle = computed(():Map<string, any>=>{
const style = new Map<string, any>();
const fontSize = props.fontSize ?? radioGroupProps?.fontSize
if(fontSize != null) {
style.set('font-size', fontSize)
}
return style
})
const onClick = (e: UniPointerEvent) => {
if(isDisabled.value) return;
const _name = props.value ?? props.name
if(radioGroupChange != null && _name != null) {
const value = finalAllowUncheck.value && radioChecked.value ? null : _name;
radioGroupChange(value);
} else {
const value = finalAllowUncheck.value ? !radioChecked.value : true;
innerChecked.value = value
}
}
// #ifdef APP
const iconRef = ref<UniElement|null>(null)
const update = () => {
if(iconRef.value == null) return
const ctx = iconRef.value!.getDrawableContext()!;
const rect = iconRef.value!.getBoundingClientRect();
const x = rect.width / 2;
const y = rect.height / 2;
ctx.reset();
if(innerIcon.value == 'circle') {
if(radioChecked.value) {
ctx.strokeStyle = isDisabled.value ? innerIconDisabledColor.value : 'white';
ctx.lineWidth = rect.width * 0.12;
ctx.lineCap = 'round';
ctx.moveTo(rect.width * 0.2967, rect.height * 0.53)
ctx.lineTo(rect.width * 0.4342, rect.height * 0.6675)
ctx.lineTo(rect.width * 0.7092, rect.height * 0.3925)
ctx.stroke()
}
} else if(innerIcon.value == 'line') {
if(radioChecked.value) {
ctx.strokeStyle = isDisabled.value ? innerIconDisabledColor.value : props.checkedColor ?? '#3283ff';
ctx.lineWidth = rect.width * 0.16;
ctx.lineCap = 'round';
ctx.moveTo(rect.width * 0.10, rect.height * 0.5466)
ctx.lineTo(rect.width * 0.3666, rect.height * 0.8134)
ctx.lineTo(rect.width * 0.90, rect.height * 0.28)
ctx.stroke()
}
} else if(innerIcon.value == 'dot'){
if(radioChecked.value) {
ctx.fillStyle = isDisabled.value ? innerIconDisabledColor.value : 'white';
ctx.arc(x, y, rect.width * 0.22, 0, Math.PI * 2)
ctx.fill()
}
}
ctx.update();
}
watchEffect(update)
onMounted(()=>{
nextTick(update)
})
// #endif
</script> <style lang="scss"> @import './index-u'; </style>